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 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);
|
||||
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) {
|
||||
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);
|
||||
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)
|
||||
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}`);
|
||||
|
||||
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;
|
||||
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 {};
|
||||
}
|
||||
}
|
||||
|
@ -37,22 +37,29 @@ export class EAPGTC implements IEAPMethod {
|
||||
): Promise<IPacketHandlerResult> {
|
||||
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());
|
||||
|
||||
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 {};
|
||||
}
|
||||
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
|
||||
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);
|
||||
// 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()
|
||||
);
|
||||
|
||||
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),
|
||||
},
|
||||
}
|
||||
)
|
||||
// handle incoming package via inner tunnel
|
||||
const result = await this.innerTunnel.handlePacket(
|
||||
{
|
||||
attributes,
|
||||
},
|
||||
this.getEAPType()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const eapMessage = result.attributes?.find((attr) => attr[0] === 'EAP-Message');
|
||||
if (!eapMessage) {
|
||||
throw new Error('no eap message found');
|
||||
}
|
||||
log('inner tunnel result', result);
|
||||
|
||||
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
|
||||
) {
|
||||
sendResponsePromise.resolve(
|
||||
this.authResponse(
|
||||
identifier,
|
||||
result.code === PacketResponseCode.AccessAccept,
|
||||
connection.tls,
|
||||
{
|
||||
...packet,
|
||||
attributes: {
|
||||
...packet.attributes,
|
||||
...this.transformAttributesArrayToMap(result.attributes),
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const responseHandler = (encryptedResponseData: Buffer) => {
|
||||
// send back...
|
||||
sendResponsePromise.resolve(
|
||||
this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData)
|
||||
);
|
||||
};
|
||||
const eapMessage = result.attributes?.find((attr) => attr[0] === 'EAP-Message');
|
||||
if (!eapMessage) {
|
||||
throw new Error('no eap message found');
|
||||
}
|
||||
|
||||
// register event listeners
|
||||
connection.events.on('incoming', incomingMessageHandler);
|
||||
connection.events.on('response', responseHandler);
|
||||
connection.events.emit(
|
||||
'encrypt',
|
||||
this.buildAVP(attr_name_to_id('EAP-Message'), eapMessage[1] as Buffer)
|
||||
);
|
||||
};
|
||||
|
||||
// emit data to tls server
|
||||
connection.events.emit('decrypt', data);
|
||||
const responseData = await sendResponsePromise.promise;
|
||||
const responseHandler = (encryptedResponseData: Buffer) => {
|
||||
// send back...
|
||||
sendResponsePromise.resolve(
|
||||
this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData)
|
||||
);
|
||||
};
|
||||
|
||||
// cleanup
|
||||
connection.events.off('incoming', incomingMessageHandler);
|
||||
connection.events.off('response', responseHandler);
|
||||
// register event listeners
|
||||
connection.events.on('incoming', incomingMessageHandler);
|
||||
connection.events.on('response', responseHandler);
|
||||
|
||||
// send response
|
||||
return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData);
|
||||
// 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);
|
||||
|
||||
// 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) {
|
||||
|
Loading…
Reference in New Issue
Block a user