feat: inner tunnel for TTSL support added
This commit is contained in:
parent
191bb54264
commit
6aa4b9f92e
@ -18,6 +18,7 @@ export class Authentication implements IAuthentication {
|
||||
}
|
||||
|
||||
const authResult = await this.authenticator.authenticate(username, password);
|
||||
console.log(`Auth Result for user ${username}`, authResult ? 'SUCCESS' : 'Failure');
|
||||
this.cache.set(cacheKey, authResult, 86400); // cache for one day
|
||||
|
||||
return authResult;
|
||||
|
@ -113,7 +113,7 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
if (!dnsFetched && !forceFetching) {
|
||||
return this.authenticate(username, password, count, true);
|
||||
}
|
||||
console.error(`invalid username, not found in DN: ${username}`, this.allValidDNsCache);
|
||||
console.error(`invalid username, not found in DN: ${username}`); // , this.allValidDNsCache);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,21 @@ import { EAPPacketHandler } from './handler/EAPPacketHandler';
|
||||
import { DefaultPacketHandler } from './handler/DefaultPacketHandler';
|
||||
import { IPacketHandler, IPacketHandlerResult, PacketResponseCode } from '../types/PacketHandler';
|
||||
|
||||
import { EAPTTLS } from './handler/eap/eapMethods/EAP-TTLS';
|
||||
import { EAPMD5 } from './handler/eap/eapMethods/EAP-MD5';
|
||||
import { EAPGTC } from './handler/eap/eapMethods/EAP-GTC';
|
||||
|
||||
export class RadiusService {
|
||||
radiusPacketHandlers: IPacketHandler[] = [];
|
||||
|
||||
constructor(private secret: string, private authentication: IAuthentication) {
|
||||
this.radiusPacketHandlers.push(new EAPPacketHandler(authentication));
|
||||
this.radiusPacketHandlers.push(
|
||||
new EAPPacketHandler([
|
||||
new EAPTTLS(authentication),
|
||||
new EAPGTC(authentication),
|
||||
new EAPMD5(authentication)
|
||||
])
|
||||
);
|
||||
this.radiusPacketHandlers.push(new DefaultPacketHandler(authentication));
|
||||
}
|
||||
|
||||
|
@ -3,103 +3,21 @@
|
||||
import * as NodeCache from 'node-cache';
|
||||
import { RadiusPacket } from 'radius';
|
||||
import debug from 'debug';
|
||||
import { EAPTTLS } from './eapMethods/EAPTTLS';
|
||||
import { makeid } from '../../helpers';
|
||||
import {
|
||||
IPacketHandler,
|
||||
IPacketHandlerResult,
|
||||
PacketResponseCode
|
||||
} from '../../types/PacketHandler';
|
||||
import { IAuthentication } from '../../types/Authentication';
|
||||
import { IPacketHandler, IPacketHandlerResult } from '../../types/PacketHandler';
|
||||
import { IEAPMethod } from '../../types/EAPMethod';
|
||||
import { buildEAPResponse, decodeEAPHeader } from './eap/EAPHelper';
|
||||
|
||||
const log = debug('radius:eap');
|
||||
|
||||
export class EAPPacketHandler implements IPacketHandler {
|
||||
private eapMethods: IEAPMethod[] = [];
|
||||
|
||||
// private eapConnectionStates: { [key: string]: { validMethods: IEAPMethod[] } } = {};
|
||||
private eapConnectionStates = new NodeCache({ useClones: false, stdTTL: 3600 }); // max for one hour
|
||||
|
||||
constructor(authentication: IAuthentication) {
|
||||
this.eapMethods.push(new EAPTTLS(authentication));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param msgType 1 = identity, 21 = EAP-TTLS, 2 = notification, 4 = md5-challenge, 3 = NAK
|
||||
*/
|
||||
private async buildEAPResponse(
|
||||
identifier: number,
|
||||
msgType: number,
|
||||
data?: Buffer
|
||||
): Promise<IPacketHandlerResult> {
|
||||
/** build a package according to this:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Type-Data ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
*/
|
||||
const buffer = Buffer.from([
|
||||
1, // request
|
||||
identifier,
|
||||
0, // length (1/2)
|
||||
0, // length (2/2)
|
||||
msgType // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK
|
||||
]);
|
||||
|
||||
const resBuffer = data ? Buffer.concat([buffer, data]) : buffer;
|
||||
// set EAP length header
|
||||
resBuffer.writeUInt16BE(resBuffer.byteLength, 2);
|
||||
|
||||
return {
|
||||
code: PacketResponseCode.AccessChallenge,
|
||||
attributes: [['EAP-Message', buffer]]
|
||||
};
|
||||
}
|
||||
|
||||
private decodeEAPHeader(msg: Buffer) {
|
||||
/**
|
||||
* parse msg according to this:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Type-Data ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
*/
|
||||
|
||||
/*
|
||||
code:
|
||||
1 Request
|
||||
2 Response
|
||||
3 Success
|
||||
4 Failure
|
||||
*/
|
||||
const code = msg.slice(0, 1).readUInt8(0);
|
||||
/* identifier is a number */
|
||||
const identifier = msg.slice(1, 2).readUInt8(0);
|
||||
const length = msg.slice(2, 4).readInt16BE(0);
|
||||
/* EAP type */
|
||||
const type = msg.slice(4, 5).readUInt8(0);
|
||||
const data = msg.slice(5);
|
||||
|
||||
return {
|
||||
code,
|
||||
identifier,
|
||||
length,
|
||||
type,
|
||||
data
|
||||
};
|
||||
}
|
||||
constructor(private eapMethods: IEAPMethod[]) {}
|
||||
|
||||
async handlePacket(
|
||||
attributes: { [key: string]: Buffer },
|
||||
attributes: { [key: string]: Buffer | string },
|
||||
orgRadiusPacket: RadiusPacket
|
||||
): Promise<IPacketHandlerResult> {
|
||||
if (!attributes['EAP-Message']) {
|
||||
@ -116,9 +34,9 @@ export class EAPPacketHandler implements IPacketHandler {
|
||||
}
|
||||
|
||||
// EAP MESSAGE
|
||||
const msg = attributes['EAP-Message'];
|
||||
const msg = attributes['EAP-Message'] as Buffer;
|
||||
|
||||
const { code, type, identifier, data } = this.decodeEAPHeader(msg);
|
||||
const { code, type, identifier, data } = decodeEAPHeader(msg);
|
||||
|
||||
const currentState = this.eapConnectionStates.get(stateID) as { validMethods: IEAPMethod[] };
|
||||
|
||||
@ -130,10 +48,10 @@ export class EAPPacketHandler implements IPacketHandler {
|
||||
log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', {});
|
||||
// start identify
|
||||
if (currentState.validMethods.length > 0) {
|
||||
return currentState.validMethods[0].identify(identifier, stateID);
|
||||
return currentState.validMethods[0].identify(identifier, stateID, data);
|
||||
}
|
||||
|
||||
return this.buildEAPResponse(identifier, 3);
|
||||
return buildEAPResponse(identifier, 3); // NAK
|
||||
case 2: // notification
|
||||
log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {});
|
||||
console.info('notification');
|
||||
@ -180,7 +98,7 @@ export class EAPPacketHandler implements IPacketHandler {
|
||||
|
||||
console.error('unsupported type', type, `requesting: ${serverSupportedMethods}`);
|
||||
|
||||
return this.buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
|
||||
return buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
79
src/radius/handler/eap/EAPHelper.ts
Normal file
79
src/radius/handler/eap/EAPHelper.ts
Normal file
@ -0,0 +1,79 @@
|
||||
import { IPacketHandlerResult, PacketResponseCode } from '../../../types/PacketHandler';
|
||||
|
||||
export function buildEAP(identifier: number, msgType: number, data?: Buffer) {
|
||||
/** build a package according to this:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Type-Data ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
*/
|
||||
const buffer = Buffer.from([
|
||||
1, // request
|
||||
identifier,
|
||||
0, // length (1/2)
|
||||
0, // length (2/2)
|
||||
msgType // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK
|
||||
]);
|
||||
|
||||
const resBuffer = data ? Buffer.concat([buffer, data]) : buffer;
|
||||
|
||||
// set EAP length header
|
||||
resBuffer.writeUInt16BE(resBuffer.byteLength, 2);
|
||||
|
||||
return resBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param data
|
||||
* @param msgType 1 = identity, 21 = EAP-TTLS, 2 = notification, 4 = md5-challenge, 3 = NAK
|
||||
*/
|
||||
export function buildEAPResponse(
|
||||
identifier: number,
|
||||
msgType: number,
|
||||
data?: Buffer
|
||||
): IPacketHandlerResult {
|
||||
return {
|
||||
code: PacketResponseCode.AccessChallenge,
|
||||
attributes: [['EAP-Message', buildEAP(identifier, msgType, data)]]
|
||||
};
|
||||
}
|
||||
|
||||
export function decodeEAPHeader(msg: Buffer) {
|
||||
/**
|
||||
* parse msg according to this:
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Code | Identifier | Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Type | Type-Data ...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
|
||||
*/
|
||||
|
||||
/*
|
||||
code:
|
||||
1 Request
|
||||
2 Response
|
||||
3 Success
|
||||
4 Failure
|
||||
*/
|
||||
const code = msg.slice(0, 1).readUInt8(0);
|
||||
/* identifier is a number */
|
||||
const identifier = msg.slice(1, 2).readUInt8(0);
|
||||
const length = msg.slice(2, 4).readUInt16BE(0);
|
||||
/* EAP type */
|
||||
const type = msg.slice(4, 5).readUInt8(0);
|
||||
const data = msg.slice(5);
|
||||
|
||||
return {
|
||||
code,
|
||||
identifier,
|
||||
length,
|
||||
type,
|
||||
data
|
||||
};
|
||||
}
|
65
src/radius/handler/eap/eapMethods/EAP-GTC.ts
Normal file
65
src/radius/handler/eap/eapMethods/EAP-GTC.ts
Normal file
@ -0,0 +1,65 @@
|
||||
// https://tools.ietf.org/html/rfc5281 TTLS v0
|
||||
// https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented)
|
||||
/* eslint-disable no-bitwise */
|
||||
import * as NodeCache from 'node-cache';
|
||||
import debug from 'debug';
|
||||
import { IPacketHandlerResult, PacketResponseCode } from '../../../../types/PacketHandler';
|
||||
import { IEAPMethod } from '../../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../../types/Authentication';
|
||||
import { buildEAPResponse, decodeEAPHeader } from '../EAPHelper';
|
||||
|
||||
const log = debug('radius:eap:gtc');
|
||||
|
||||
export class EAPGTC implements IEAPMethod {
|
||||
private loginData = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds
|
||||
|
||||
getEAPType(): number {
|
||||
return 6;
|
||||
}
|
||||
|
||||
identify(identifier: number, stateID: string, msg?: Buffer): IPacketHandlerResult {
|
||||
if (msg) {
|
||||
const parsedMsg = msg.slice(
|
||||
0,
|
||||
msg.findIndex(v => v === 0)
|
||||
);
|
||||
log('identify', parsedMsg, parsedMsg.toString());
|
||||
this.loginData.set(stateID, parsedMsg); // use token til binary 0.);
|
||||
} else {
|
||||
log('no msg');
|
||||
}
|
||||
|
||||
return buildEAPResponse(identifier, 6, Buffer.from('Password: '));
|
||||
}
|
||||
|
||||
constructor(private authentication: IAuthentication) {}
|
||||
|
||||
async handleMessage(
|
||||
_identifier: number,
|
||||
stateID: string,
|
||||
msg: Buffer
|
||||
): Promise<IPacketHandlerResult> {
|
||||
const username = this.loginData.get(stateID) as Buffer | undefined;
|
||||
|
||||
const { data } = decodeEAPHeader(msg);
|
||||
|
||||
let tillBinary0 = data.findIndex(v => v === 0) || data.length;
|
||||
if (tillBinary0 < 0) {
|
||||
tillBinary0 = data.length - 1;
|
||||
}
|
||||
const token = data.slice(0, tillBinary0 + 1); // use token til binary 0.
|
||||
|
||||
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
|
||||
};
|
||||
}
|
||||
}
|
40
src/radius/handler/eap/eapMethods/EAP-MD5.ts
Normal file
40
src/radius/handler/eap/eapMethods/EAP-MD5.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// https://tools.ietf.org/html/rfc5281 TTLS v0
|
||||
// https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented)
|
||||
/* eslint-disable no-bitwise */
|
||||
import { RadiusPacket } from 'radius';
|
||||
import debug from 'debug';
|
||||
import { ResponseAuthHandler } from '../../../../types/Handler';
|
||||
import { IPacketHandlerResult } from '../../../../types/PacketHandler';
|
||||
import { IEAPMethod } from '../../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../../types/Authentication';
|
||||
|
||||
const log = debug('radius:eap:md5');
|
||||
|
||||
interface IEAPResponseHandlers {
|
||||
response: (respData?: Buffer, msgType?: number) => void;
|
||||
checkAuth: ResponseAuthHandler;
|
||||
}
|
||||
|
||||
export class EAPMD5 implements IEAPMethod {
|
||||
getEAPType(): number {
|
||||
return 4;
|
||||
}
|
||||
|
||||
identify(_identifier: number, _stateID: string): IPacketHandlerResult {
|
||||
// NOT IMPLEMENTED
|
||||
return {};
|
||||
}
|
||||
|
||||
constructor(private authentication: IAuthentication) {}
|
||||
|
||||
async handleMessage(
|
||||
_identifier: number,
|
||||
_stateID: string,
|
||||
_msg: Buffer,
|
||||
_orgRadiusPacket: RadiusPacket
|
||||
): Promise<IPacketHandlerResult> {
|
||||
// not implemented
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
@ -1,19 +1,20 @@
|
||||
// https://tools.ietf.org/html/rfc5281 TTLS v0
|
||||
// https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented)
|
||||
|
||||
/* eslint-disable no-bitwise */
|
||||
import * as tls from 'tls';
|
||||
import * as NodeCache from 'node-cache';
|
||||
import { RadiusPacket } from 'radius';
|
||||
import debug from 'debug';
|
||||
import { encodeTunnelPW, ITLSServer, startTLSServer } from '../../../tls/crypt';
|
||||
import { ResponseAuthHandler } from '../../../types/Handler';
|
||||
import { encodeTunnelPW, ITLSServer, startTLSServer } from '../../../../tls/crypt';
|
||||
import { ResponseAuthHandler } from '../../../../types/Handler';
|
||||
import { PAPChallenge } from './challenges/PAPChallenge';
|
||||
import { IPacketHandlerResult, PacketResponseCode } from '../../../types/PacketHandler';
|
||||
import { MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from '../../../helpers';
|
||||
import { IEAPMethod } from '../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../types/Authentication';
|
||||
import { secret } from '../../../../config';
|
||||
import { IPacketHandlerResult, PacketResponseCode } from '../../../../types/PacketHandler';
|
||||
import { MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from '../../../../helpers';
|
||||
import { IEAPMethod } from '../../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../../types/Authentication';
|
||||
import { secret } from '../../../../../config';
|
||||
import { EAPPacketHandler } from '../../EAPPacketHandler';
|
||||
import { EAPGTC } from './EAP-GTC';
|
||||
|
||||
const log = debug('radius:eap:ttls');
|
||||
|
||||
@ -38,6 +39,9 @@ export class EAPTTLS implements IEAPMethod {
|
||||
|
||||
private openTLSSockets = new NodeCache({ useClones: false, stdTTL: 3600 }); // keep sockets for about one hour
|
||||
|
||||
// EAP TUNNEL
|
||||
tunnelEAP = new EAPPacketHandler([new EAPGTC(this.authentication)]); // tunnel with GTC support
|
||||
|
||||
getEAPType(): number {
|
||||
return 21;
|
||||
}
|
||||
@ -48,23 +52,23 @@ export class EAPTTLS implements IEAPMethod {
|
||||
|
||||
constructor(private authentication: IAuthentication) {}
|
||||
|
||||
private buildEAPTTLSResponse(
|
||||
private buildEAPTTLS(
|
||||
identifier: number,
|
||||
msgType = 21,
|
||||
msgFlags = 0x00,
|
||||
stateID: string,
|
||||
data?: Buffer,
|
||||
newResponse = true
|
||||
): IPacketHandlerResult {
|
||||
const maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4;
|
||||
newResponse = true,
|
||||
maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4
|
||||
): Buffer {
|
||||
log('maxSize', maxSize);
|
||||
|
||||
/* it's the first one and we have more, therefore include length */
|
||||
const includeLength = data && newResponse && data.length > maxSize;
|
||||
const includeLength = maxSize > 0 && data && newResponse && data.length > maxSize;
|
||||
|
||||
// extract data party
|
||||
const dataToSend = data && data.length > 0 && data.slice(0, maxSize);
|
||||
const dataToQueue = data && data.length > maxSize && data.slice(maxSize);
|
||||
const dataToSend = maxSize > 0 ? data && data.length > 0 && data.slice(0, maxSize) : data;
|
||||
const dataToQueue = maxSize > 0 && data && data.length > maxSize && data.slice(maxSize);
|
||||
|
||||
/*
|
||||
0 1 2 3 4 5 6 7 8
|
||||
@ -129,6 +133,19 @@ export class EAPTTLS implements IEAPMethod {
|
||||
this.queueData.del(stateID);
|
||||
}
|
||||
|
||||
return resBuffer;
|
||||
}
|
||||
|
||||
private buildEAPTTLSResponse(
|
||||
identifier: number,
|
||||
msgType = 21,
|
||||
msgFlags = 0x00,
|
||||
stateID: string,
|
||||
data?: Buffer,
|
||||
newResponse = true
|
||||
): IPacketHandlerResult {
|
||||
const resBuffer = this.buildEAPTTLS(identifier, msgType, msgFlags, stateID, data, newResponse);
|
||||
|
||||
const attributes: any = [['State', Buffer.from(stateID)]];
|
||||
let sentDataSize = 0;
|
||||
do {
|
||||
@ -162,7 +179,7 @@ export class EAPTTLS implements IEAPMethod {
|
||||
Message Length | Data...
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
|
||||
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
|
||||
@ -191,10 +208,19 @@ export class EAPTTLS implements IEAPMethod {
|
||||
|
||||
let msglength;
|
||||
if (decodedFlags.lengthIncluded) {
|
||||
msglength = msg.slice(6, 10).readInt32BE(0); // .readDoubleLE(0); // .toString('hex');
|
||||
msglength = msg.slice(6, 10).readUInt32BE(0); // .readDoubleLE(0); // .toString('hex');
|
||||
}
|
||||
const data = msg.slice(decodedFlags.lengthIncluded ? 10 : 6, msg.length);
|
||||
|
||||
log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', {
|
||||
flags: `00000000${flags.toString(2)}`.substr(-8),
|
||||
decodedFlags,
|
||||
identifier,
|
||||
msglength,
|
||||
data
|
||||
// dataStr: data.toString()
|
||||
});
|
||||
|
||||
return {
|
||||
decodedFlags,
|
||||
msglength,
|
||||
@ -255,13 +281,10 @@ export class EAPTTLS implements IEAPMethod {
|
||||
msg: Buffer,
|
||||
orgRadiusPacket: RadiusPacket
|
||||
): Promise<IPacketHandlerResult> {
|
||||
const { decodedFlags, msglength, data } = this.decodeTTLSMessage(msg);
|
||||
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) {
|
||||
log(
|
||||
`>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS, ACK / NACK (no data, just a confirmation, ID: ${identifier})`
|
||||
);
|
||||
const queuedData = this.queueData.get(stateID);
|
||||
if (queuedData instanceof Buffer && queuedData.length > 0) {
|
||||
return this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, queuedData, false);
|
||||
@ -270,15 +293,6 @@ export class EAPTTLS implements IEAPMethod {
|
||||
return {};
|
||||
}
|
||||
|
||||
log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', {
|
||||
// flags: `00000000${flags.toString(2)}`.substr(-8),
|
||||
decodedFlags,
|
||||
identifier,
|
||||
msglength
|
||||
// data,
|
||||
// dataStr: data.toString()
|
||||
});
|
||||
|
||||
let connection = this.openTLSSockets.get(stateID) as ITLSServer;
|
||||
|
||||
if (!connection) {
|
||||
@ -295,11 +309,18 @@ export class EAPTTLS implements IEAPMethod {
|
||||
const sendResponsePromise = newDeferredPromise();
|
||||
|
||||
const incomingMessageHandler = async (incomingData: Buffer) => {
|
||||
const type = incomingData.slice(3, 4).readUInt8(0);
|
||||
const ret: any = {};
|
||||
ret.attributes = {};
|
||||
ret.raw_attributes = [];
|
||||
|
||||
const { type, data: AVPdata, length: AVPlength } = this.decodeAVP(incomingData);
|
||||
|
||||
console.log('AVP data', { AVPdata, AVPlength, AVPdataStr: AVPdata.toString() });
|
||||
|
||||
// const code = data.slice(4, 5).readUInt8(0);
|
||||
|
||||
switch (type) {
|
||||
case 1: // PAP / CHAP
|
||||
case 1: // PAP
|
||||
try {
|
||||
const { username, password } = this.papChallenge.decode(incomingData);
|
||||
const authResult = await this.authentication.authenticate(username, password);
|
||||
@ -315,18 +336,52 @@ export class EAPTTLS implements IEAPMethod {
|
||||
sendResponsePromise.resolve(this.buildEAPTTLSResponse(identifier, 3, 0, stateID));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
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());
|
||||
|
||||
// currentConnection!.events.emit('end');
|
||||
log('UNSUPPORTED AUTH TYPE, requesting identify again (we need PAP!)', type);
|
||||
|
||||
log('UNSUPPORTED AUTH TYPE, requesting PAP');
|
||||
// throw new Error(`unsupported auth type${type}`);
|
||||
sendResponsePromise.resolve(
|
||||
this.buildEAPTTLSResponse(identifier, 3, 0, stateID, Buffer.from([1]))
|
||||
connection.events.emit(
|
||||
'encrypt',
|
||||
this.buildAVP(79, this.buildEAPTTLS(identifier, 3, 0, stateID, Buffer.from([1])))
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const responseHandler = (encryptedResponseData: Buffer) => {
|
||||
@ -341,7 +396,7 @@ export class EAPTTLS implements IEAPMethod {
|
||||
connection.events.on('response', responseHandler);
|
||||
|
||||
// emit data to tls server
|
||||
connection.events.emit('send', data);
|
||||
connection.events.emit('decrypt', data);
|
||||
const responseData = await sendResponsePromise.promise;
|
||||
|
||||
// cleanup
|
||||
@ -351,4 +406,114 @@ export class EAPTTLS implements IEAPMethod {
|
||||
// send response
|
||||
return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData);
|
||||
}
|
||||
|
||||
private decodeAVP(buffer: Buffer) {
|
||||
/**
|
||||
* 4.1. AVP Header
|
||||
|
||||
The fields in the AVP header MUST be sent in network byte order. The
|
||||
format of the header is:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| AVP Code |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|V M P r r r r r| AVP Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Vendor-ID (opt) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Data ...
|
||||
+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
const type = buffer.slice(0, 4).readUInt32BE(0);
|
||||
const flags = buffer.slice(4, 5).readUInt8(0);
|
||||
const decodedFlags = {
|
||||
// L
|
||||
V: !!(flags & 0b10000000),
|
||||
// M
|
||||
M: !!(flags & 0b01000000)
|
||||
};
|
||||
|
||||
// const length = buffer.slice(5, 8).readUInt16BE(0); // actually a Int24BE
|
||||
const length = buffer.slice(6, 8).readUInt16BE(0); // actually a Int24BE
|
||||
|
||||
let vendorId;
|
||||
let data;
|
||||
if (flags & 0b010000000) {
|
||||
// V flag set
|
||||
vendorId = buffer.slice(8, 12).readUInt32BE(0);
|
||||
data = buffer.slice(8, 12);
|
||||
} else {
|
||||
data = buffer.slice(8);
|
||||
}
|
||||
|
||||
return {
|
||||
type,
|
||||
flags: `00000000${flags.toString(2)}`.substr(-8),
|
||||
decodedFlags,
|
||||
length,
|
||||
vendorId,
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
private buildAVP(
|
||||
code: number,
|
||||
data: Buffer,
|
||||
flags: { VendorSpecific?: boolean; Mandatory?: boolean } = { Mandatory: true }
|
||||
) {
|
||||
/**
|
||||
* 4.1. AVP Header
|
||||
|
||||
The fields in the AVP header MUST be sent in network byte order. The
|
||||
format of the header is:
|
||||
|
||||
0 1 2 3
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| AVP Code |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
|V M r r r r r r| AVP Length |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Vendor-ID (opt) |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
| Data ...
|
||||
+-+-+-+-+-+-+-+-+
|
||||
*/
|
||||
let b = Buffer.alloc(8);
|
||||
|
||||
b.writeInt32BE(code, 0); // EAP-Message
|
||||
/**
|
||||
* The 'V' (Vendor-Specific) bit indicates whether the optional
|
||||
Vendor-ID field is present. When set to 1, the Vendor-ID field is
|
||||
present and the AVP Code is interpreted according to the namespace
|
||||
defined by the vendor indicated in the Vendor-ID field.
|
||||
|
||||
The 'M' (Mandatory) bit indicates whether support of the AVP is
|
||||
required. If this bit is set to 0, this indicates that the AVP
|
||||
may be safely ignored if the receiving party does not understand
|
||||
or support it. If set to 1, this indicates that the receiving
|
||||
party MUST fail the negotiation if it does not understand the AVP;
|
||||
for a TTLS server, this would imply returning EAP-Failure, for a
|
||||
client, this would imply abandoning the negotiation.
|
||||
*/
|
||||
let flagValue = 0;
|
||||
if (flags.VendorSpecific) {
|
||||
flagValue += 0b10000000;
|
||||
}
|
||||
if (flags.Mandatory) {
|
||||
flagValue += 0b01000000;
|
||||
}
|
||||
|
||||
console.log('flagValue', flagValue, `00000000${flagValue.toString(2)}`.substr(-8));
|
||||
|
||||
b.writeInt8(flagValue, 4); // flags (set V..)
|
||||
|
||||
b = Buffer.concat([b, data]); // , Buffer.from('\0')]);
|
||||
|
||||
b.writeInt16BE(b.byteLength, 6); // write size (actually we would need a Int24BE here, but it is good to go with 16bits)
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
0
src/radius/handler/eap/eapMethods/EAP.ts
Normal file
0
src/radius/handler/eap/eapMethods/EAP.ts
Normal file
@ -1,5 +1,5 @@
|
||||
import debug from 'debug';
|
||||
import { IEAPChallenge } from '../../../../types/EAPChallenge';
|
||||
import { IEAPChallenge } from '../../../../../types/EAPChallenge';
|
||||
|
||||
const log = debug('radius:eap:papchallenge');
|
||||
|
@ -42,11 +42,16 @@ export function startTLSServer(): ITLSServer {
|
||||
});
|
||||
const encrypted = duplexpair.socket2;
|
||||
|
||||
emitter.on('send', (data: Buffer) => {
|
||||
emitter.on('decrypt', (data: Buffer) => {
|
||||
encrypted.write(data);
|
||||
// encrypted.sync();
|
||||
});
|
||||
|
||||
emitter.on('encrypt', (data: Buffer) => {
|
||||
cleartext.write(data);
|
||||
// encrypted.sync();
|
||||
});
|
||||
|
||||
encrypted.on('data', (data: Buffer) => {
|
||||
// log('encrypted data', data, data.toString());
|
||||
emitter.emit('response', data);
|
||||
|
@ -1,3 +1,3 @@
|
||||
export interface IEAPChallenge {
|
||||
decode(data: Buffer): { username: string; password: string };
|
||||
decode(data: Buffer, stateID: string): { username: string; password?: string };
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import { IPacketHandlerResult } from './PacketHandler';
|
||||
export interface IEAPMethod {
|
||||
getEAPType(): number;
|
||||
|
||||
identify(identifier: number, stateID: string): IPacketHandlerResult;
|
||||
identify(identifier: number, stateID: string, msg?: Buffer): IPacketHandlerResult;
|
||||
|
||||
handleMessage(
|
||||
identifier: number,
|
||||
|
Loading…
Reference in New Issue
Block a user