fix(eap): catch decoding errors
This commit is contained in:
parent
6fc7301c60
commit
97ea3fad1d
@ -3,7 +3,7 @@
|
|||||||
import * as NodeCache from 'node-cache';
|
import * as NodeCache from 'node-cache';
|
||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { makeid } from '../../helpers';
|
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 { IEAPMethod } from '../../types/EAPMethod';
|
||||||
import { buildEAPResponse, decodeEAPHeader } from './eap/EAPHelper';
|
import { buildEAPResponse, decodeEAPHeader } from './eap/EAPHelper';
|
||||||
|
|
||||||
@ -34,99 +34,104 @@ export class EAPPacketHandler implements IPacketHandler {
|
|||||||
// EAP MESSAGE
|
// EAP MESSAGE
|
||||||
const msg = packet.attributes['EAP-Message'] as Buffer;
|
const msg = packet.attributes['EAP-Message'] as Buffer;
|
||||||
|
|
||||||
const { code, type, identifier, data } = decodeEAPHeader(msg);
|
try {
|
||||||
|
const { code, type, identifier, data } = decodeEAPHeader(msg);
|
||||||
|
|
||||||
const currentState = this.eapConnectionStates.get(stateID) as { validMethods: IEAPMethod[] };
|
const currentState = this.eapConnectionStates.get(stateID) as { validMethods: IEAPMethod[] };
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 1: // for request
|
case 1: // for request
|
||||||
case 2: // for response
|
case 2: // for response
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 1: // identifiy
|
case 1: // identifiy
|
||||||
log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', stateID, data.toString());
|
log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', stateID, data.toString());
|
||||||
if (data) {
|
if (data) {
|
||||||
this.identities.set(stateID, data); // use token til binary 0.);
|
this.identities.set(stateID, data); // use token til binary 0.);
|
||||||
} else {
|
} else {
|
||||||
log('no msg');
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// start identify
|
||||||
if (currentState.validMethods.length > 0) {
|
if (currentState.validMethods.length > 0) {
|
||||||
return currentState.validMethods[0].identify(identifier, stateID, data);
|
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 buildEAPResponse(identifier, 3); // NAK
|
||||||
return eapMethod.handleMessage(
|
case 2: // notification
|
||||||
identifier,
|
log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {});
|
||||||
stateID,
|
console.info('notification');
|
||||||
msg,
|
break;
|
||||||
packet,
|
case 4: // md5-challenge
|
||||||
this.identities.get(stateID)
|
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}`);
|
||||||
|
|
||||||
|
return buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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}`);
|
|
||||||
|
|
||||||
return buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
|
|
||||||
}
|
}
|
||||||
}
|
break;
|
||||||
break;
|
case 3:
|
||||||
case 3:
|
log('Client Auth Success');
|
||||||
log('Client Auth Success');
|
break;
|
||||||
break;
|
case 4:
|
||||||
case 4:
|
log('Client Auth FAILURE');
|
||||||
log('Client Auth FAILURE');
|
break;
|
||||||
break;
|
default:
|
||||||
default:
|
}
|
||||||
|
// silently ignore;
|
||||||
|
return {};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('decoding of (generic) EAP package failed', msg, err);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
// silently ignore;
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,22 +37,29 @@ export class EAPGTC implements IEAPMethod {
|
|||||||
): Promise<IPacketHandlerResult> {
|
): Promise<IPacketHandlerResult> {
|
||||||
const username = identity; // this.loginData.get(stateID) as Buffer | undefined;
|
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) {
|
if (!username) {
|
||||||
throw new Error('no username');
|
throw new Error('no username');
|
||||||
|
}
|
||||||
|
|
||||||
|
log('username', username, username.toString());
|
||||||
|
log('token', token, 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,
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
console.error('decoding of EAP-GTC package failed', msg, err);
|
||||||
|
return {
|
||||||
|
code: PacketResponseCode.AccessReject,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
log('username', username, username.toString());
|
|
||||||
log('token', token, 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,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -300,113 +300,120 @@ export class EAPTTLS implements IEAPMethod {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
this.lastProcessedIdentifier.set(stateID, identifier);
|
this.lastProcessedIdentifier.set(stateID, identifier);
|
||||||
const { data } = this.decodeTTLSMessage(msg);
|
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
|
// 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) {
|
if (!data || data.length === 0) {
|
||||||
const queuedData = this.queueData.get(stateID);
|
const queuedData = this.queueData.get(stateID);
|
||||||
if (queuedData instanceof Buffer && queuedData.length > 0) {
|
if (queuedData instanceof Buffer && queuedData.length > 0) {
|
||||||
log(`returning queued data for ${stateID}`);
|
log(`returning queued data for ${stateID}`);
|
||||||
return this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, queuedData, false);
|
return this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, queuedData, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
log(`empty data queue for ${stateID}`);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
log(`empty data queue for ${stateID}`);
|
let connection = this.openTLSSockets.get(stateID) as ITLSServer;
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
let connection = this.openTLSSockets.get(stateID) as ITLSServer;
|
if (!connection) {
|
||||||
|
connection = startTLSServer();
|
||||||
|
this.openTLSSockets.set(stateID, connection);
|
||||||
|
|
||||||
if (!connection) {
|
connection.events.on('end', () => {
|
||||||
connection = startTLSServer();
|
// cleanup socket
|
||||||
this.openTLSSockets.set(stateID, connection);
|
log('ENDING SOCKET');
|
||||||
|
this.openTLSSockets.del(stateID);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
connection.events.on('end', () => {
|
const sendResponsePromise = newDeferredPromise();
|
||||||
// cleanup socket
|
|
||||||
log('ENDING SOCKET');
|
|
||||||
this.openTLSSockets.del(stateID);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const sendResponsePromise = newDeferredPromise();
|
const incomingMessageHandler = async (incomingData: Buffer) => {
|
||||||
|
const ret: any = {};
|
||||||
|
ret.attributes = {};
|
||||||
|
ret.raw_attributes = [];
|
||||||
|
|
||||||
const incomingMessageHandler = async (incomingData: Buffer) => {
|
const AVPs = this.decodeAVPs(incomingData);
|
||||||
const ret: any = {};
|
|
||||||
ret.attributes = {};
|
|
||||||
ret.raw_attributes = [];
|
|
||||||
|
|
||||||
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
|
attributes.State = `${stateID}-inner`;
|
||||||
const attributes: IPacketAttributes = {};
|
|
||||||
AVPs.forEach((avp) => {
|
|
||||||
attributes[attr_id_to_name(avp.type)] = avp.data;
|
|
||||||
});
|
|
||||||
|
|
||||||
attributes.State = `${stateID}-inner`;
|
// handle incoming package via inner tunnel
|
||||||
|
const result = await this.innerTunnel.handlePacket(
|
||||||
// handle incoming package via inner tunnel
|
{
|
||||||
const result = await this.innerTunnel.handlePacket(
|
attributes,
|
||||||
{
|
},
|
||||||
attributes,
|
this.getEAPType()
|
||||||
},
|
|
||||||
this.getEAPType()
|
|
||||||
);
|
|
||||||
|
|
||||||
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');
|
log('inner tunnel result', result);
|
||||||
if (!eapMessage) {
|
|
||||||
throw new Error('no eap message found');
|
|
||||||
}
|
|
||||||
|
|
||||||
connection.events.emit(
|
if (
|
||||||
'encrypt',
|
result.code === PacketResponseCode.AccessReject ||
|
||||||
this.buildAVP(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer)
|
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 responseHandler = (encryptedResponseData: Buffer) => {
|
const eapMessage = result.attributes?.find((attr) => attr[0] === 'EAP-Message');
|
||||||
// send back...
|
if (!eapMessage) {
|
||||||
sendResponsePromise.resolve(
|
throw new Error('no eap message found');
|
||||||
this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData)
|
}
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
// register event listeners
|
connection.events.emit(
|
||||||
connection.events.on('incoming', incomingMessageHandler);
|
'encrypt',
|
||||||
connection.events.on('response', responseHandler);
|
this.buildAVP(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// emit data to tls server
|
const responseHandler = (encryptedResponseData: Buffer) => {
|
||||||
connection.events.emit('decrypt', data);
|
// send back...
|
||||||
const responseData = await sendResponsePromise.promise;
|
sendResponsePromise.resolve(
|
||||||
|
this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
// cleanup
|
// register event listeners
|
||||||
connection.events.off('incoming', incomingMessageHandler);
|
connection.events.on('incoming', incomingMessageHandler);
|
||||||
connection.events.off('response', responseHandler);
|
connection.events.on('response', responseHandler);
|
||||||
|
|
||||||
// send response
|
// emit data to tls server
|
||||||
return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData);
|
connection.events.emit('decrypt', data);
|
||||||
|
const responseData = await sendResponsePromise.promise;
|
||||||
|
|
||||||
|
// cleanup
|
||||||
|
connection.events.off('incoming', incomingMessageHandler);
|
||||||
|
connection.events.off('response', responseHandler);
|
||||||
|
|
||||||
|
// 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) {
|
private transformAttributesArrayToMap(attributes: [string, Buffer | string][] | undefined) {
|
||||||
|
Loading…
Reference in New Issue
Block a user