Initial CoreID changes to allow code-based integration
This commit is contained in:
parent
1149fc054a
commit
78c57d7747
2
.gitignore
vendored
2
.gitignore
vendored
@ -9,3 +9,5 @@ tsconfig.tsbuildinfo
|
||||
|
||||
# custom certificates
|
||||
/ssl-*/
|
||||
|
||||
.idea
|
||||
|
39
LICENSE
Normal file
39
LICENSE
Normal file
@ -0,0 +1,39 @@
|
||||
@coreid/node-radius-server - CoreID maintained fork
|
||||
Copyright (C) 2021 Simon Tretter, Garrett Mills
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
--
|
||||
|
||||
Note: this is a fork of Simon Tretter's node-radius-server package
|
||||
modified for use in Starship CoreID. Per the terms of GPLv3, the
|
||||
modifications made are documented below:
|
||||
|
||||
- Introduced the PackageInterface helper class
|
||||
- enables integrating the library into code rather than as a
|
||||
standalone CLI app
|
||||
|
||||
- Modified existing code to use configuration on the PackageInterface
|
||||
class instead of global imports
|
||||
|
||||
- Modified existing code to log through PackageInterface.logger
|
||||
|
||||
- Modified existing code to introduce a credentialMiddleware method
|
||||
to IPacket, which allows external code to customize the credentials
|
||||
before they are sent
|
||||
|
||||
- Modified RadiusService to allow specifying a custom packet decoder
|
||||
method on PackageInterface.
|
||||
|
||||
- Modified UDPServer class to expose a stop() method
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "radius-server",
|
||||
"description": "radius server for google LDAP and TTLS",
|
||||
"name": "@coreid/radius-server",
|
||||
"description": "CoreID fork of radius server for google LDAP and TTLS",
|
||||
"version": "1.2.1",
|
||||
"engines": {
|
||||
"node": ">13.10.1"
|
||||
@ -14,7 +14,7 @@
|
||||
"dist",
|
||||
"ssl"
|
||||
],
|
||||
"homepage": "https://github.com/simllll/node-radius-server",
|
||||
"homepage": "https://code.garrettmills.dev/starship/node-radius-server",
|
||||
"scripts": {
|
||||
"release": "npm run build && standard-version",
|
||||
"debug": "DEBUG=radius:* node --tls-min-v1.0 dist/app.js",
|
||||
|
72
src/app.ts
72
src/app.ts
@ -6,39 +6,44 @@ import * as config from '../config';
|
||||
import { Authentication } from './auth';
|
||||
import { IAuthentication } from './types/Authentication';
|
||||
import { startTLSServer } from './tls/crypt';
|
||||
import PackageInterface from './interface';
|
||||
|
||||
/* test node version */
|
||||
const testSocket = startTLSServer();
|
||||
if (typeof testSocket.tls.exportKeyingMaterial !== 'function') {
|
||||
console.error(`UNSUPPORTED NODE VERSION (${process.version}) FOUND!!`);
|
||||
const packageInterface = PackageInterface.get();
|
||||
|
||||
console.log('min version supported is node js 14. run "sudo npx n 14"');
|
||||
process.exit(-1);
|
||||
}
|
||||
const prestartServer = () => {
|
||||
/* test node version */
|
||||
const testSocket = startTLSServer();
|
||||
if (typeof testSocket.tls.exportKeyingMaterial !== 'function') {
|
||||
packageInterface.log(`UNSUPPORTED NODE VERSION (${process.version}) FOUND!!`);
|
||||
|
||||
const { argv } = yargs
|
||||
.usage('NODE RADIUS Server\nUsage: radius-server')
|
||||
.example('radius-server --port 1812 -s radiussecret', 'start on port 1812 with a secret')
|
||||
.default({
|
||||
port: config.port || 1812,
|
||||
s: config.secret || 'testing123',
|
||||
authentication: config.authentication,
|
||||
authenticationOptions: config.authenticationOptions,
|
||||
})
|
||||
.describe('port', 'RADIUS server listener port')
|
||||
.alias('s', 'secret')
|
||||
.describe('secret', 'RADIUS secret')
|
||||
.number('port')
|
||||
.string(['secret', 'authentication']) as {
|
||||
argv: { port?: number; secret?: string; authentication?: string; authenticationOptions?: any };
|
||||
packageInterface.log('min version supported is node js 14. run "sudo npx n 14"');
|
||||
process.exit(-1);
|
||||
}
|
||||
|
||||
const { argv } = yargs
|
||||
.usage('NODE RADIUS Server\nUsage: radius-server')
|
||||
.example('radius-server --port 1812 -s radiussecret', 'start on port 1812 with a secret')
|
||||
.default({
|
||||
port: config.port || 1812,
|
||||
s: config.secret || 'testing123',
|
||||
authentication: config.authentication,
|
||||
authenticationOptions: config.authenticationOptions,
|
||||
})
|
||||
.describe('port', 'RADIUS server listener port')
|
||||
.alias('s', 'secret')
|
||||
.describe('secret', 'RADIUS secret')
|
||||
.number('port')
|
||||
.string(['secret', 'authentication']) as {
|
||||
argv: { port?: number; secret?: string; authentication?: string; authenticationOptions?: any };
|
||||
};
|
||||
|
||||
packageInterface.log(`Listener Port: ${argv.port || 1812}`);
|
||||
packageInterface.log(`RADIUS Secret: ${argv.secret}`);
|
||||
packageInterface.log(`Auth ${argv.authentication}`);
|
||||
packageInterface.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined, 3)}`);
|
||||
};
|
||||
|
||||
console.log(`Listener Port: ${argv.port || 1812}`);
|
||||
console.log(`RADIUS Secret: ${argv.secret}`);
|
||||
console.log(`Auth ${argv.authentication}`);
|
||||
console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined, 3)}`);
|
||||
|
||||
(async () => {
|
||||
const startServer = async () => {
|
||||
/* configure auth mechansim */
|
||||
let auth: IAuthentication;
|
||||
try {
|
||||
@ -47,7 +52,7 @@ console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined
|
||||
];
|
||||
auth = new AuthMechanismus(config.authenticationOptions);
|
||||
} catch (err) {
|
||||
console.error('cannot load auth mechanismus', config.authentication);
|
||||
packageInterface.log('cannot load auth mechanismus', config.authentication);
|
||||
throw err;
|
||||
}
|
||||
// start radius server
|
||||
@ -66,7 +71,7 @@ console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined
|
||||
rinfo.address,
|
||||
(err, _bytes) => {
|
||||
if (err) {
|
||||
console.log('Error sending response to ', rinfo);
|
||||
packageInterface.log('Error sending response to ', rinfo);
|
||||
}
|
||||
},
|
||||
response.expectAcknowledgment
|
||||
@ -76,4 +81,9 @@ console.log(`Auth Config: ${JSON.stringify(argv.authenticationOptions, undefined
|
||||
|
||||
// start server
|
||||
await server.start();
|
||||
})();
|
||||
};
|
||||
|
||||
if (packageInterface.start) {
|
||||
prestartServer();
|
||||
startServer();
|
||||
}
|
||||
|
18
src/auth.ts
18
src/auth.ts
@ -1,8 +1,11 @@
|
||||
import * as NodeCache from 'node-cache';
|
||||
import { Cache, ExpirationStrategy, MemoryStorage } from '@hokify/node-ts-cache';
|
||||
import { IAuthentication } from './types/Authentication';
|
||||
import PackageInterface from './interface';
|
||||
|
||||
const cacheStrategy = new ExpirationStrategy(new MemoryStorage());
|
||||
const packageInterface = PackageInterface.get();
|
||||
|
||||
/**
|
||||
* this is just a simple abstraction to provide
|
||||
* an application layer for caching credentials
|
||||
@ -12,18 +15,25 @@ export class Authentication implements IAuthentication {
|
||||
|
||||
constructor(private authenticator: IAuthentication) {}
|
||||
|
||||
@Cache(cacheStrategy, { ttl: 60000 })
|
||||
@Cache(cacheStrategy, { ttl: packageInterface.cacheTTL })
|
||||
async authenticate(username: string, password: string): Promise<boolean> {
|
||||
const cacheKey = `usr:${username}|pwd:${password}`;
|
||||
const fromCache = this.cache.get(cacheKey) as undefined | boolean;
|
||||
if (fromCache !== undefined) {
|
||||
console.log(`Cached Auth Result for user ${username}`, fromCache ? 'SUCCESS' : 'Failure');
|
||||
packageInterface.log(
|
||||
`Cached Auth Result for user ${username}`,
|
||||
fromCache ? 'SUCCESS' : 'Failure'
|
||||
);
|
||||
return fromCache;
|
||||
}
|
||||
|
||||
const authResult = await this.authenticator.authenticate(username, password);
|
||||
console.log(`Auth Result for user ${username}`, authResult ? 'SUCCESS' : 'Failure');
|
||||
this.cache.set(cacheKey, authResult, authResult ? 86400 : 60); // cache for one day on success, otherwise just for 60 seconds
|
||||
packageInterface.log(`Auth Result for user ${username}`, authResult ? 'SUCCESS' : 'Failure');
|
||||
this.cache.set(
|
||||
cacheKey,
|
||||
authResult,
|
||||
authResult ? packageInterface.cacheSuccessTTL : packageInterface.cacheFailTTL
|
||||
); // cache for one day on success, otherwise just for 60 seconds
|
||||
|
||||
return authResult;
|
||||
}
|
||||
|
@ -1,12 +1,14 @@
|
||||
import { ClientOptions, createClient } from 'ldapjs';
|
||||
import debug from 'debug';
|
||||
import * as tls from 'tls';
|
||||
import * as fs from 'fs';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
const usernameFields = ['posixUid', 'mail'];
|
||||
|
||||
const log = debug('radius:auth:google-ldap');
|
||||
// TLS:
|
||||
// https://github.com/ldapjs/node-ldapjs/issues/307
|
||||
|
||||
@ -55,7 +57,7 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
};
|
||||
|
||||
this.fetchDNs().catch((err) => {
|
||||
console.error('fatal error google ldap auth, cannot fetch DNs', err);
|
||||
log('fatal error google ldap auth, cannot fetch DNs', err);
|
||||
});
|
||||
}
|
||||
|
||||
@ -64,7 +66,7 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const ldapDNClient = createClient(this.config).on('error', (error) => {
|
||||
console.error('Error in ldap', error);
|
||||
log('Error in ldap', error);
|
||||
reject(error);
|
||||
});
|
||||
|
||||
@ -92,7 +94,7 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
});
|
||||
|
||||
res.on('error', (ldapErr) => {
|
||||
console.error(`error: ${JSON.stringify(ldapErr)}`);
|
||||
log(`error: ${JSON.stringify(ldapErr)}`);
|
||||
reject(ldapErr);
|
||||
});
|
||||
|
||||
@ -144,7 +146,7 @@ export class GoogleLDAPAuth implements IAuthentication {
|
||||
return this.authenticate(username, password, count, true);
|
||||
}
|
||||
// console.log('this.allValidDNsCache', this.allValidDNsCache);
|
||||
console.error(`invalid username, not found in DN: ${username}`); // , this.allValidDNsCache);
|
||||
log(`invalid username, not found in DN: ${username}`); // , this.allValidDNsCache);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,9 @@
|
||||
import axios from 'axios';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
interface IHTTPAuthOptions {
|
||||
url: string;
|
||||
@ -30,7 +34,7 @@ export class HTTPAuth implements IAuthentication {
|
||||
return true;
|
||||
}
|
||||
|
||||
console.log(`HTTP authentication failed, response code: ${result.status}`);
|
||||
log(`HTTP authentication failed, response code: ${result.status}`);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
import * as imaps from 'imap-simple';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
interface IIMAPAuthOptions {
|
||||
host: string;
|
||||
@ -34,7 +38,7 @@ export class IMAPAuth implements IAuthentication {
|
||||
if (this.validHosts) {
|
||||
const domain = username.split('@').pop();
|
||||
if (!domain || !this.validHosts.includes(domain)) {
|
||||
console.info('invalid or no domain in username', username, domain);
|
||||
log('invalid or no domain in username', username, domain);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -57,7 +61,7 @@ export class IMAPAuth implements IAuthentication {
|
||||
|
||||
connection.end();
|
||||
} catch (err) {
|
||||
console.error('imap auth failed', err);
|
||||
log('imap auth failed', err);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
@ -1,6 +1,10 @@
|
||||
import * as LdapAuth from 'ldapauth-fork';
|
||||
import * as fs from 'fs';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
interface ILDAPAuthOptions {
|
||||
/** ldap url
|
||||
@ -44,7 +48,7 @@ export class LDAPAuth implements IAuthentication {
|
||||
reconnect: true,
|
||||
});
|
||||
this.ldap.on('error', (err) => {
|
||||
console.error('LdapAuth: ', err);
|
||||
log('LdapAuth: ', err);
|
||||
});
|
||||
}
|
||||
|
||||
@ -53,7 +57,7 @@ export class LDAPAuth implements IAuthentication {
|
||||
this.ldap.authenticate(username, password, (err, user) => {
|
||||
if (err) {
|
||||
resolve(false);
|
||||
console.error('ldap error', err);
|
||||
log('ldap error', err);
|
||||
// reject(err);
|
||||
}
|
||||
if (user) resolve(user);
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { SMTPClient } from 'smtp-client';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const log = (...args) => PackageInterface.get().log(...args);
|
||||
|
||||
interface ISMTPAuthOptions {
|
||||
host: string;
|
||||
@ -37,7 +40,7 @@ export class SMTPAuth implements IAuthentication {
|
||||
if (this.validHosts) {
|
||||
const domain = username.split('@').pop();
|
||||
if (!domain || !this.validHosts.includes(domain)) {
|
||||
console.info('invalid or no domain in username', username, domain);
|
||||
log('invalid or no domain in username', username, domain);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -61,7 +64,7 @@ export class SMTPAuth implements IAuthentication {
|
||||
|
||||
s.close(); // runs QUIT command
|
||||
} catch (err) {
|
||||
console.error('imap auth failed', err);
|
||||
log('imap auth failed', err);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
47
src/interface.ts
Normal file
47
src/interface.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import * as radius from 'radius';
|
||||
import * as config from '../config';
|
||||
import { IPacket } from './types/PacketHandler';
|
||||
|
||||
export type PacketDecoder = (msg: Buffer) => {
|
||||
packet?: radius.RadiusPacket & IPacket;
|
||||
secret: string;
|
||||
};
|
||||
|
||||
export default class PackageInterface {
|
||||
private static _instance?: PackageInterface;
|
||||
|
||||
public static get(): PackageInterface {
|
||||
if (!this._instance) {
|
||||
this._instance = new PackageInterface();
|
||||
}
|
||||
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
public packetDecoder?: PacketDecoder;
|
||||
|
||||
public start = true;
|
||||
|
||||
public cacheTTL = 60000;
|
||||
|
||||
public cacheSuccessTTL = 86400;
|
||||
|
||||
public cacheFailTTL = 60;
|
||||
|
||||
public logger: (...any: unknown[]) => unknown = console.log; // eslint-disable-line no-console
|
||||
|
||||
private config: any = config;
|
||||
|
||||
public log(...any: unknown[]): void {
|
||||
this.logger(...any);
|
||||
}
|
||||
|
||||
public getConfig(): any {
|
||||
return this.config;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
public setConfig(inConfig: any) {
|
||||
this.config = inConfig;
|
||||
}
|
||||
}
|
@ -1,8 +1,11 @@
|
||||
import * as radius from 'radius';
|
||||
import { IAuthentication } from '../types/Authentication';
|
||||
import { IPacketHandlerResult, PacketResponseCode } from '../types/PacketHandler';
|
||||
import {IPacket, IPacketHandlerResult, PacketResponseCode} from '../types/PacketHandler';
|
||||
|
||||
import { PacketHandler } from './PacketHandler';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const packageInterface = PackageInterface.get();
|
||||
|
||||
export class RadiusService {
|
||||
private packetHandler: PacketHandler;
|
||||
@ -11,13 +14,29 @@ export class RadiusService {
|
||||
this.packetHandler = new PacketHandler(authentication);
|
||||
}
|
||||
|
||||
defaultDecoder(msg: Buffer): { packet?: radius.RadiusPacket & IPacket; secret: string } {
|
||||
const packet = radius.decode({ packet: msg, secret: this.secret });
|
||||
|
||||
return {
|
||||
packet,
|
||||
secret: this.secret,
|
||||
};
|
||||
}
|
||||
|
||||
async handleMessage(
|
||||
msg: Buffer
|
||||
): Promise<{ data: Buffer; expectAcknowledgment?: boolean } | undefined> {
|
||||
const packet = radius.decode({ packet: msg, secret: this.secret });
|
||||
const { packet, secret } = packageInterface.packetDecoder
|
||||
? packageInterface.packetDecoder(msg)
|
||||
: this.defaultDecoder(msg);
|
||||
|
||||
if (!packet) {
|
||||
packageInterface.log('Unable to parse packet from message.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (packet.code !== 'Access-Request') {
|
||||
console.error('unknown packet type: ', packet.code);
|
||||
packageInterface.log('unknown packet type: ', packet.code);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@ -33,7 +52,7 @@ export class RadiusService {
|
||||
data: radius.encode_response({
|
||||
packet,
|
||||
code: response.code,
|
||||
secret: this.secret,
|
||||
secret,
|
||||
attributes: response.attributes,
|
||||
}),
|
||||
// if message is accept or reject, we conside this as final message
|
||||
|
@ -1,13 +1,14 @@
|
||||
// https://tools.ietf.org/html/rfc3748#section-4.1
|
||||
|
||||
import * as NodeCache from 'node-cache';
|
||||
import debug from 'debug';
|
||||
import { makeid } from '../../helpers';
|
||||
import { IPacket, IPacketHandler, IPacketHandlerResult } from '../../types/PacketHandler';
|
||||
import { IEAPMethod } from '../../types/EAPMethod';
|
||||
import { buildEAPResponse, decodeEAPHeader } from './eap/EAPHelper';
|
||||
import PackageInterface from '../../interface';
|
||||
|
||||
const log = debug('radius:eap');
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
export class EAPPacketHandler implements IPacketHandler {
|
||||
private identities = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds
|
||||
@ -66,15 +67,15 @@ export class EAPPacketHandler implements IPacketHandler {
|
||||
return buildEAPResponse(identifier, 3); // NAK
|
||||
case 2: // notification
|
||||
log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {});
|
||||
console.info('notification');
|
||||
log('notification');
|
||||
break;
|
||||
case 4: // md5-challenge
|
||||
log('>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge', {});
|
||||
|
||||
console.info('md5-challenge');
|
||||
log('md5-challenge');
|
||||
break;
|
||||
case 254: // expanded type
|
||||
console.error('not implemented type', type);
|
||||
log('not implemented type', type);
|
||||
break;
|
||||
case 3: // nak
|
||||
// console.log('got NAK', data);
|
||||
@ -118,7 +119,7 @@ export class EAPPacketHandler implements IPacketHandler {
|
||||
method.getEAPType()
|
||||
);
|
||||
|
||||
console.error('unsupported type', type, `requesting: ${serverSupportedMethods}`);
|
||||
log('unsupported type', type, `requesting: ${serverSupportedMethods}`);
|
||||
|
||||
return buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
|
||||
}
|
||||
@ -135,7 +136,7 @@ export class EAPPacketHandler implements IPacketHandler {
|
||||
// silently ignore;
|
||||
return {};
|
||||
} catch (err) {
|
||||
console.error(
|
||||
log(
|
||||
'decoding of (generic) EAP package failed',
|
||||
msg,
|
||||
err,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import debug from 'debug';
|
||||
import { IAuthentication } from '../../types/Authentication';
|
||||
import {
|
||||
IPacket,
|
||||
@ -6,8 +5,10 @@ import {
|
||||
IPacketHandlerResult,
|
||||
PacketResponseCode,
|
||||
} from '../../types/PacketHandler';
|
||||
import PackageInterface from '../../interface';
|
||||
|
||||
const log = debug('radius:user-pwd');
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
export class UserPasswordPacketHandler implements IPacketHandler {
|
||||
constructor(private authentication: IAuthentication) {}
|
||||
@ -29,10 +30,11 @@ export class UserPasswordPacketHandler implements IPacketHandler {
|
||||
log('username', username, username.toString());
|
||||
log('token', password, password.toString());
|
||||
|
||||
const authenticated = await this.authentication.authenticate(
|
||||
username.toString(),
|
||||
password.toString()
|
||||
);
|
||||
const [strUsername, strPassword] = packet.credentialMiddleware
|
||||
? packet.credentialMiddleware(username.toString(), password.toString())
|
||||
: [username.toString(), password.toString()];
|
||||
|
||||
const authenticated = await this.authentication.authenticate(strUsername, strPassword);
|
||||
if (authenticated) {
|
||||
// success
|
||||
return {
|
||||
|
@ -1,13 +1,14 @@
|
||||
// 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 debug from 'debug';
|
||||
import { IPacketHandlerResult, PacketResponseCode } from '../../../../types/PacketHandler';
|
||||
import { IEAPMethod } from '../../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../../types/Authentication';
|
||||
import { buildEAPResponse, decodeEAPHeader } from '../EAPHelper';
|
||||
import PackageInterface from '../../../../interface';
|
||||
|
||||
const log = debug('radius:eap:gtc');
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
export class EAPGTC implements IEAPMethod {
|
||||
getEAPType(): number {
|
||||
@ -56,7 +57,7 @@ export class EAPGTC implements IEAPMethod {
|
||||
attributes: (success && [['User-Name', username]]) || undefined,
|
||||
};
|
||||
} catch (err) {
|
||||
console.error('decoding of EAP-GTC package failed', msg, err);
|
||||
log('decoding of EAP-GTC package failed', msg, err);
|
||||
return {
|
||||
code: PacketResponseCode.AccessReject,
|
||||
};
|
||||
|
@ -2,10 +2,12 @@
|
||||
// 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 { IPacketHandlerResult } from '../../../../types/PacketHandler';
|
||||
import { IEAPMethod } from '../../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../../types/Authentication';
|
||||
import PackageInterface from '../../../../interface';
|
||||
|
||||
const packageInterface = PackageInterface.get();
|
||||
|
||||
export class EAPMD5 implements IEAPMethod {
|
||||
getEAPType(): number {
|
||||
@ -27,7 +29,7 @@ export class EAPMD5 implements IEAPMethod {
|
||||
): Promise<IPacketHandlerResult> {
|
||||
// not implemented
|
||||
|
||||
debug('eap md5 not implemented...');
|
||||
packageInterface.log('eap md5 not implemented...');
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -6,8 +6,6 @@ import * as NodeCache from 'node-cache';
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
import { attr_id_to_name, attr_name_to_id } from 'radius';
|
||||
import debug from 'debug';
|
||||
|
||||
import { encodeTunnelPW, ITLSServer, startTLSServer } from '../../../../tls/crypt';
|
||||
import {
|
||||
IPacket,
|
||||
@ -20,8 +18,10 @@ import { MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from '../../../../helpe
|
||||
import { IEAPMethod } from '../../../../types/EAPMethod';
|
||||
import { IAuthentication } from '../../../../types/Authentication';
|
||||
import { secret } from '../../../../../config';
|
||||
import PackageInterface from '../../../../interface';
|
||||
|
||||
const log = debug('radius:eap:ttls');
|
||||
const packageInterface = PackageInterface.get();
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
function tlsHasExportKeyingMaterial(tlsSocket): tlsSocket is {
|
||||
exportKeyingMaterial: (length: number, label: string, context?: Buffer) => Buffer;
|
||||
@ -276,7 +276,7 @@ export class EAPTTLS implements IEAPMethod {
|
||||
[[17, encodeTunnelPW(keyingMaterial.slice(0, 64), packet.authenticator, secret)]],
|
||||
]); // MS-MPPE-Recv-Key
|
||||
} else {
|
||||
console.error(
|
||||
log(
|
||||
'FATAL: no exportKeyingMaterial method available!!!, you need latest NODE JS, see https://github.com/nodejs/node/pull/31814'
|
||||
);
|
||||
}
|
||||
@ -420,7 +420,7 @@ export class EAPTTLS implements IEAPMethod {
|
||||
// send response
|
||||
return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData);
|
||||
} catch (err) {
|
||||
console.error('decoding of EAP-TTLS package failed', msg, err);
|
||||
log('decoding of EAP-TTLS package failed', msg, err);
|
||||
return {
|
||||
code: PacketResponseCode.AccessReject,
|
||||
};
|
||||
|
@ -4,6 +4,9 @@ import * as events from 'events';
|
||||
import { EventEmitter } from 'events';
|
||||
import { newDeferredPromise } from '../helpers';
|
||||
import { IServer } from '../types/Server';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const packageInterface = PackageInterface.get();
|
||||
|
||||
export class UDPServer extends events.EventEmitter implements IServer {
|
||||
static MAX_RETRIES = 3;
|
||||
@ -53,7 +56,7 @@ export class UDPServer extends events.EventEmitter implements IServer {
|
||||
const startServer = newDeferredPromise();
|
||||
this.server.on('listening', () => {
|
||||
const address = this.server.address();
|
||||
console.log(`radius server listening ${address.address}:${address.port}`);
|
||||
packageInterface.log(`radius server listening ${address.address}:${address.port}`);
|
||||
|
||||
this.setupListeners();
|
||||
startServer.resolve();
|
||||
@ -72,6 +75,12 @@ export class UDPServer extends events.EventEmitter implements IServer {
|
||||
return startServer.promise;
|
||||
}
|
||||
|
||||
stop(): Promise<void> {
|
||||
return new Promise<void>((res) => {
|
||||
this.server.close(() => res());
|
||||
});
|
||||
}
|
||||
|
||||
private setupListeners() {
|
||||
this.server.on('message', (message, rinfo) => this.emit('message', message, rinfo));
|
||||
}
|
||||
|
@ -3,33 +3,36 @@ import * as tls from 'tls';
|
||||
import { createSecureContext } from 'tls';
|
||||
import * as crypto from 'crypto';
|
||||
import * as DuplexPair from 'native-duplexpair';
|
||||
import debug from 'debug';
|
||||
import * as NodeCache from 'node-cache';
|
||||
// import * as constants from 'constants';
|
||||
import * as config from '../../config';
|
||||
import PackageInterface from '../interface';
|
||||
|
||||
const log = debug('radius:tls');
|
||||
const packageInterface = PackageInterface.get();
|
||||
|
||||
const log = (...args) => packageInterface.log(...args);
|
||||
|
||||
// https://nodejs.org/api/tls.html
|
||||
const tlsOptions: tls.SecureContextOptions = {
|
||||
...config.certificate,
|
||||
};
|
||||
log('tlsOptions', tlsOptions);
|
||||
const secureContext = createSecureContext(tlsOptions);
|
||||
export function getTLSSecureContext(): tls.SecureContext {
|
||||
const tlsOptions: tls.SecureContextOptions = {
|
||||
...packageInterface.getConfig().certificate,
|
||||
};
|
||||
|
||||
return createSecureContext(tlsOptions);
|
||||
}
|
||||
|
||||
export interface ITLSServer {
|
||||
events: events.EventEmitter;
|
||||
tls: tls.TLSSocket;
|
||||
}
|
||||
|
||||
const resumeSessions = new NodeCache({ stdTTL: 86400 }); // session reidentification maximum 1 day
|
||||
const resumeSessions = new NodeCache({ stdTTL: packageInterface.cacheTTL }); // session reidentification maximum 1 day
|
||||
|
||||
export function startTLSServer(): ITLSServer {
|
||||
const duplexpair = new DuplexPair();
|
||||
const emitter = new events.EventEmitter();
|
||||
|
||||
const cleartext = new tls.TLSSocket(duplexpair.socket1, {
|
||||
secureContext,
|
||||
secureContext: getTLSSecureContext(),
|
||||
isServer: true,
|
||||
// enableTrace: true,
|
||||
rejectUnauthorized: false,
|
||||
@ -133,14 +136,14 @@ export function encodeTunnelPW(key: Buffer, authenticator: Buffer, secret: strin
|
||||
// https://tools.ietf.org/html/rfc2548
|
||||
|
||||
/**
|
||||
* Salt
|
||||
The Salt field is two octets in length and is used to ensure the
|
||||
uniqueness of the keys used to encrypt each of the encrypted
|
||||
attributes occurring in a given Access-Accept packet. The most
|
||||
significant bit (leftmost) of the Salt field MUST be set (1). The
|
||||
contents of each Salt field in a given Access-Accept packet MUST
|
||||
be unique.
|
||||
*/
|
||||
* Salt
|
||||
The Salt field is two octets in length and is used to ensure the
|
||||
uniqueness of the keys used to encrypt each of the encrypted
|
||||
attributes occurring in a given Access-Accept packet. The most
|
||||
significant bit (leftmost) of the Salt field MUST be set (1). The
|
||||
contents of each Salt field in a given Access-Accept packet MUST
|
||||
be unique.
|
||||
*/
|
||||
const salt = crypto.randomBytes(2);
|
||||
|
||||
// eslint-disable-next-line no-bitwise
|
||||
@ -183,7 +186,7 @@ export function encodeTunnelPW(key: Buffer, authenticator: Buffer, secret: strin
|
||||
is performed in the following manner ('+' indicates
|
||||
concatenation):
|
||||
|
||||
Zorn Informational [Page 21]
|
||||
Zorn Informational [Page 21]
|
||||
|
||||
RFC 2548 Microsoft Vendor-specific RADIUS Attributes March 1999
|
||||
|
||||
|
@ -16,6 +16,7 @@ export interface IPacketAttributes {
|
||||
export interface IPacket {
|
||||
attributes: { [key: string]: string | Buffer };
|
||||
authenticator?: Buffer;
|
||||
credentialMiddleware?: (username: string, password: string) => [string, string];
|
||||
}
|
||||
|
||||
export interface IPacketHandler {
|
||||
|
Loading…
Reference in New Issue
Block a user