2020-02-22 23:29:33 +00:00
// https://tools.ietf.org/html/rfc3748#section-4.1
import * as NodeCache from 'node-cache' ;
import { makeid } from '../../helpers' ;
2020-06-25 09:18:55 +00:00
import { IPacket , IPacketHandler , IPacketHandlerResult } from '../../types/PacketHandler' ;
2020-02-22 23:29:33 +00:00
import { IEAPMethod } from '../../types/EAPMethod' ;
2020-02-24 17:52:21 +00:00
import { buildEAPResponse , decodeEAPHeader } from './eap/EAPHelper' ;
2021-10-24 20:37:16 +00:00
import PackageInterface from '../../interface' ;
2020-02-22 23:29:33 +00:00
2021-10-24 20:37:16 +00:00
const packageInterface = PackageInterface . get ( ) ;
const log = ( . . . args ) = > packageInterface . log ( . . . args ) ;
2020-02-22 23:49:17 +00:00
2020-02-22 23:29:33 +00:00
export class EAPPacketHandler implements IPacketHandler {
2020-02-25 10:54:57 +00:00
private identities = new NodeCache ( { useClones : false , stdTTL : 60 } ) ; // queue data maximum for 60 seconds
2020-02-22 23:29:33 +00:00
// private eapConnectionStates: { [key: string]: { validMethods: IEAPMethod[] } } = {};
private eapConnectionStates = new NodeCache ( { useClones : false , stdTTL : 3600 } ) ; // max for one hour
2020-02-24 17:52:21 +00:00
constructor ( private eapMethods : IEAPMethod [ ] ) { }
2020-02-22 23:29:33 +00:00
2020-02-25 10:54:57 +00:00
async handlePacket ( packet : IPacket , handlingType? : number ) : Promise < IPacketHandlerResult > {
if ( ! packet . attributes [ 'EAP-Message' ] ) {
2020-02-22 23:29:33 +00:00
// not an EAP message
return { } ;
}
2020-02-25 10:54:57 +00:00
const stateID = ( packet . attributes . State && packet . attributes . State . toString ( ) ) || makeid ( 16 ) ;
2020-02-22 23:29:33 +00:00
if ( ! this . eapConnectionStates . get ( stateID ) ) {
this . eapConnectionStates . set ( stateID , {
2020-05-14 13:02:15 +00:00
validMethods : this.eapMethods.filter ( ( eap ) = > eap . getEAPType ( ) !== handlingType ) , // on init all registered eap methods are valid, we kick them out in case we get a NAK response
2020-02-22 23:29:33 +00:00
} ) ;
}
// EAP MESSAGE
2020-06-25 10:06:25 +00:00
let msg = packet . attributes [ 'EAP-Message' ] as Buffer ;
2020-08-05 10:44:39 +00:00
if ( Array . isArray ( msg ) && ! ( packet . attributes [ 'EAP-Message' ] instanceof Buffer ) ) {
// log('multiple EAP Messages received, concat', msg.length);
const allMsgs = msg as Buffer [ ] ;
msg = Buffer . concat ( allMsgs ) ;
// log('final EAP Message', msg);
2020-06-25 10:06:25 +00:00
}
2020-02-22 23:29:33 +00:00
2020-06-25 09:17:19 +00:00
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' ) ;
2020-02-22 23:29:33 +00:00
}
2020-02-25 10:54:57 +00:00
// start identify
if ( currentState . validMethods . length > 0 ) {
return currentState . validMethods [ 0 ] . identify ( identifier , stateID , data ) ;
}
2020-02-22 23:29:33 +00:00
2020-06-25 09:17:19 +00:00
return buildEAPResponse ( identifier , 3 ) ; // NAK
case 2 : // notification
log ( '>>>>>>>>>>>> REQUEST FROM CLIENT: notification' , { } ) ;
2021-10-24 20:37:16 +00:00
log ( 'notification' ) ;
2020-06-25 09:17:19 +00:00
break ;
case 4 : // md5-challenge
log ( '>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge' , { } ) ;
2021-10-24 20:37:16 +00:00
log ( 'md5-challenge' ) ;
2020-06-25 09:17:19 +00:00
break ;
case 254 : // expanded type
2021-10-24 20:37:16 +00:00
log ( 'not implemented type' , type ) ;
2020-06-25 09:17:19 +00:00
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 ) ;
}
2021-01-23 21:14:56 +00:00
currentState . validMethods = currentState . validMethods . filter (
( method ) = > supportedEAPMethods . includes ( method . getEAPType ( ) ) // kick it out?
) ;
2020-06-25 09:17:19 +00:00
// 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 : {
2021-01-23 21:14:56 +00:00
const eapMethod = this . eapMethods . find ( ( method ) = > type === method . getEAPType ( ) ) ;
2020-06-25 09:17:19 +00:00
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 ( )
) ;
2020-02-22 23:29:33 +00:00
2021-10-24 20:37:16 +00:00
log ( 'unsupported type' , type , ` requesting: ${ serverSupportedMethods } ` ) ;
2020-02-22 23:29:33 +00:00
2020-06-25 09:17:19 +00:00
return buildEAPResponse ( identifier , 3 , Buffer . from ( serverSupportedMethods ) ) ;
}
2020-02-22 23:29:33 +00:00
}
2020-06-25 09:17:19 +00:00
break ;
case 3 :
log ( 'Client Auth Success' ) ;
break ;
case 4 :
log ( 'Client Auth FAILURE' ) ;
break ;
default :
}
// silently ignore;
return { } ;
} catch ( err ) {
2021-10-24 20:37:16 +00:00
log (
2020-06-25 09:18:55 +00:00
'decoding of (generic) EAP package failed' ,
msg ,
err ,
this . eapConnectionStates . get ( stateID )
) ;
2020-06-25 09:17:19 +00:00
return { } ;
2020-02-22 23:29:33 +00:00
}
}
}