From 0cb807a555febec461edf1280fe1a7e1b72186b0 Mon Sep 17 00:00:00 2001 From: simon Date: Sat, 22 Feb 2020 02:32:12 +0100 Subject: [PATCH] fix: a lot of bug fixes, first running version for windows and android :) code is super ugly right now.. please don't judge --- config.js | 33 ++++++ package.json | 5 +- src/app.ts | 100 +++++++++++++----- src/{ldap.ts => auth/google-ldap.ts} | 7 +- src/eap.ts | 80 ++++++++------ src/eap/challenges/pap.ts | 4 +- src/eap/eap-ttls.ts | 88 +++++++++++---- src/helpers.ts | 30 ++++++ src/tls/crypt.ts | 22 +++- src/types/Authentication.ts | 3 + .../{AuthChallenge.ts => EAPChallenge.ts} | 2 +- src/types/EAPType.ts | 2 +- src/types/Handler.ts | 4 +- ssl/README.md | 8 ++ ssl/ca.cnf | 61 +++++++++++ ssl/cert/index.txt | 0 ssl/cert/serial | 1 + ssl/create.sh | 17 ++- ssl/server.cnf | 54 ++++++++++ ssl/sign.sh | 1 - ssl/xpextensions | 24 +++++ 21 files changed, 449 insertions(+), 97 deletions(-) create mode 100644 config.js rename src/{ldap.ts => auth/google-ldap.ts} (94%) create mode 100644 src/types/Authentication.ts rename src/types/{AuthChallenge.ts => EAPChallenge.ts} (65%) create mode 100644 ssl/README.md create mode 100644 ssl/ca.cnf create mode 100644 ssl/cert/index.txt create mode 100644 ssl/cert/serial create mode 100644 ssl/server.cnf delete mode 100644 ssl/sign.sh create mode 100644 ssl/xpextensions diff --git a/config.js b/config.js new file mode 100644 index 0000000..c61d0ad --- /dev/null +++ b/config.js @@ -0,0 +1,33 @@ +import * as fs from 'fs'; + +module.exports = { + // radius secret + secret: 'testing123', + + certificate: { + cert: fs.readFileSync('./ssl/cert/server.crt'), + key: [ + { + pem: fs.readFileSync('./ssl/cert/server.key'), + passphrase: 'whatever2020' + } + ] + }, + + // authentication + authentication: 'ldap', + authenticationOptions: { + url: 'ldap://127.0.0.1:1636', + base: 'dc=hokify,dc=com', + tlsOptions2: { + key: fs.readFileSync('ldap.gsuite.hokify.com.40567.key'), + cert: fs.readFileSync('ldap.gsuite.hokify.com.40567.crt'), + + // This is necessary only if using the client certificate authentication. + requestCert: true, + + // This is necessary only if the client uses the self-signed certificate. + ca: [fs.readFileSync('ldap.gsuite.hokify.com.40567.key')] + } + } +}; diff --git a/package.json b/package.json index 2c751cf..670e51b 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "description": "radius server for google LDAP and TTLT", "version": "0.0.1", "scripts": { - "start": "node dist/app.js", + "start": "/home/simon/Dev/others/node/node dist/app.js", "build": "tsc", "dev": "ts-node src/app.ts", "test-ttls-pap": "eapol_test -c ./ttls-pap.conf -s testing123", @@ -17,7 +17,8 @@ "ts-node": "^8.6.2", "type-cacheable": "^4.0.0", "yargs": "~15.1.0", - "md5": "^2.2.1" + "md5": "^2.2.1", + "passport-ldapauth": "^2.1.3" }, "devDependencies": { "@types/ldapjs": "^1.0.5", diff --git a/src/app.ts b/src/app.ts index 0c1d8b9..d0408d5 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,27 +3,15 @@ import * as radius from 'radius'; // import * as dgram from "dgram"; // import * as fs from 'fs'; import { EAPHandler } from './eap'; -import { makeid } from './helpers'; +import { IDeferredPromise, makeid, MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from './helpers'; -import { LDAPAuth } from './ldap'; +import { GoogleLDAPAuth } from './auth/google-ldap'; import { AdditionalAuthHandler } from './types/Handler'; const server = dgram.createSocket('udp4'); -// not used right now, using stunnel to connect to ldap -/* const tlsOptions = { - key: fs.readFileSync('ldap.gsuite.hokify.com.40567.key'), - cert: fs.readFileSync('ldap.gsuite.hokify.com.40567.crt'), - - // This is necessary only if using the client certificate authentication. - requestCert: true, - - // This is necessary only if the client uses the self-signed certificate. - ca: [fs.readFileSync('ldap.gsuite.hokify.com.40567.key')] -}; */ - const { argv } = require('yargs') - .usage('Simple Google LDAP <> RADIUS Server\nUsage: $0') + .usage('RADIUS Server\nUsage: $0') .example('$0 --port 1812 -s radiussecret') .default({ port: 1812, @@ -46,9 +34,39 @@ console.log(`LDAP Server: ${argv.ldapServer}`); // const ldap = new LDAPAuth({url: 'ldap://ldap.google.com', base: 'dc=hokify,dc=com', uid: 'uid', tlsOptions}); -const ldap = new LDAPAuth(argv.ldapServer, argv.baseDN); +const ldap = new GoogleLDAPAuth(argv.ldapServer, argv.baseDN); const eapHandler = new EAPHandler(); +const timeout: { [key: string]: NodeJS.Timeout } = {}; +const waitForNextMsg: { [key: string]: IDeferredPromise } = {}; + +function sendToClient( + msg: string | Uint8Array, + offset: number, + length: number, + port?: number, + address?: string, + callback?: (error: Error | null, bytes: number) => void, + stateForRetry?: string +): void { + let retried = 0; + + function sendResponse() { + console.log(`sending response... (try: ${retried})`); + server.send(msg, offset, length, port, address, (error: Error | null, bytes: number) => { + // all good + + if (callback) callback(error, bytes); + }); + + if (stateForRetry && retried < 3) { + // timeout[stateForRetry] = setTimeout(sendResponse, 600 * (retried+1)); + } + retried++; + } + + sendResponse(); +} server.on('message', async function(msg, rinfo) { const packet = radius.decode({ packet: msg, secret: argv.secret }); @@ -57,7 +75,6 @@ server.on('message', async function(msg, rinfo) { console.log('unknown packet type: ', packet.code); return; } - // console.log('packet.attributes', packet.attributes); // console.log('rinfo', rinfo); @@ -90,7 +107,7 @@ server.on('message', async function(msg, rinfo) { }); console.log(`Sending ${success ? 'accept' : 'reject'} for user ${username}`); - server.send(response, 0, response.length, rinfo.port, rinfo.address, function(err, _bytes) { + sendToClient(response, 0, response.length, rinfo.port, rinfo.address, function(err, _bytes) { if (err) { console.log('Error sending response to ', rinfo); } @@ -99,15 +116,22 @@ server.on('message', async function(msg, rinfo) { if (packet.attributes['EAP-Message']) { const state = (packet.attributes.State && packet.attributes.State.toString()) || makeid(16); - // EAP MESSAGE - eapHandler.handleEAPMessage(packet.attributes['EAP-Message'], state, { + + if (timeout[state]) { + clearTimeout(timeout[state]); + } + + const handlers = { response: (EAPMessage: Buffer) => { const attributes: any = [['State', Buffer.from(state)]]; let sentDataSize = 0; do { if (EAPMessage.length > 0) { - attributes.push(['EAP-Message', EAPMessage.slice(sentDataSize, sentDataSize + 253)]); - sentDataSize += 253; + attributes.push([ + 'EAP-Message', + EAPMessage.slice(sentDataSize, sentDataSize + MAX_RADIUS_ATTRIBUTE_SIZE) + ]); + sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE; } } while (sentDataSize < EAPMessage.length); @@ -118,14 +142,34 @@ server.on('message', async function(msg, rinfo) { attributes }); - server.send(response, 0, response.length, rinfo.port, rinfo.address, function(err, _bytes) { - if (err) { - console.log('Error sending response to ', rinfo); - } - }); + waitForNextMsg[state] = newDeferredPromise(); + + sendToClient( + response, + 0, + response.length, + rinfo.port, + rinfo.address, + function(err, _bytes) { + if (err) { + console.log('Error sending response to ', rinfo); + } + }, + state + ); + + return waitForNextMsg[state].promise; }, checkAuth - }); + }; + + if (waitForNextMsg[state]) { + const identifier = packet.attributes['EAP-Message'].slice(1, 2).readUInt8(0); // .toString('hex'); + waitForNextMsg[state].resolve({ response: handlers.response, identifier }); + } + + // EAP MESSAGE + eapHandler.handleEAPMessage(packet.attributes['EAP-Message'], state, handlers); } else { const username = packet.attributes['User-Name']; const password = packet.attributes['User-Password']; diff --git a/src/ldap.ts b/src/auth/google-ldap.ts similarity index 94% rename from src/ldap.ts rename to src/auth/google-ldap.ts index 56146b7..ce44b15 100644 --- a/src/ldap.ts +++ b/src/auth/google-ldap.ts @@ -1,13 +1,14 @@ import * as NodeCache from 'node-cache'; import { createClient, Client } from 'ldapjs'; +import { IAuthentication } from '../types/Authentication'; const usernameFields = ['posixUid', 'mail']; // TLS: // https://github.com/ldapjs/node-ldapjs/issues/307 -export class LDAPAuth { +export class GoogleLDAPAuth implements IAuthentication { cache = new NodeCache(); ldap: Client; @@ -24,7 +25,7 @@ export class LDAPAuth { this.fetchDNs(); } - async fetchDNs() { + private async fetchDNs() { const dns: { [key: string]: string } = {}; await new Promise((resolve, reject) => { @@ -119,6 +120,6 @@ export class LDAPAuth { this.cache.set(cacheKey, true, 86400); - return true; + return username; } } diff --git a/src/eap.ts b/src/eap.ts index 99e305f..9275092 100644 --- a/src/eap.ts +++ b/src/eap.ts @@ -4,6 +4,7 @@ // https://tools.ietf.org/html/draft-funk-eap-ttls-v1-00 TTLS v1 (not implemented) import { IResponseHandlers, ResponseHandler } from './types/Handler'; import { EAPTTLS } from './eap/eap-ttls'; +import { MAX_RADIUS_ATTRIBUTE_SIZE } from './helpers'; export class EAPHandler { eapTTLS: EAPTTLS; @@ -17,28 +18,35 @@ export class EAPHandler { * @param data * @param type 1 = identity, 21 = EAP-TTLS, 2 = notification, 4 = md5-challenge, 3 = NAK */ - private sendEAPResponse( + private async sendEAPResponse( response: ResponseHandler, identifier: number, data?: Buffer, msgType = 21, msgFlags = 0x00 ) { - const maxFragmentSize = 1400; // @todo .. take framed-mtu into account from AVPs let i = 0; + const maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4; + + let sentDataSize = 0; + + let currentIdentifier = identifier; + let currentResponse = response; + do { - const fragmentMaxPart = - data && (i + 1) * maxFragmentSize > data.length ? undefined : (i + 1) * maxFragmentSize; - const sslPart = data && data.slice(i * maxFragmentSize, fragmentMaxPart); - console.log('sslPart', sslPart, i, maxFragmentSize, i * maxFragmentSize, fragmentMaxPart); + // SLICE + + // const fragmentMaxPart = + // data && (i + 1) * maxFragmentSize > data.length ? (i + 1) * maxFragmentSize : undefined; - const includeLength = - data && - i === 0 && - fragmentMaxPart !== undefined; /* firsrt one and we have more, therefore include length */ + const dataPart = data && data.length > 0 && data.slice(sentDataSize, sentDataSize + maxSize); + + /* it's the first one and we have more, therefore include length */ + const includeLength = data && i === 0 && sentDataSize < data.length; + + sentDataSize += maxSize; - // console.log('includeLength', includeLength, fragmentMaxPart, i) i += 1; /* @@ -62,11 +70,13 @@ export class EAPHandler { const flags = msgFlags + (includeLength ? 0b10000000 : 0) + // set L bit - (fragmentMaxPart /* we have more */ ? 0b01000000 : 0); // set M bit + (data && sentDataSize < data.length /* we have more */ ? 0b01000000 : 0); // set M bit + + currentIdentifier++; let buffer = Buffer.from([ 1, // request - identifier + 1, + currentIdentifier, 0, // length (1/2) 0, // length (2/2) msgType, // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK @@ -81,15 +91,16 @@ export class EAPHandler { buffer = Buffer.concat([buffer, length]); } - const resBuffer = sslPart ? Buffer.concat([buffer, sslPart]) : buffer; + const resBuffer = dataPart ? Buffer.concat([buffer, dataPart]) : buffer; resBuffer.writeUInt16BE(resBuffer.byteLength, 2); - console.log('EAP RESPONSE', { + console.log('<<<<<<<<<<<< EAP RESPONSE TO CLIENT', { code: 1, - identifier: identifier + 1, - length: (includeLength && data && data.byteLength) || 0, - msgType, - flags, + currentIdentifier, + includeLength, + length: (data && data.byteLength) || 0, + msgType: msgType.toString(10), + flags: `00000000${flags.toString(2)}`.substr(-8), data }); @@ -98,13 +109,14 @@ export class EAPHandler { // buffer.writeInt8(21, 4); // eap-ttls // buffer.writeInt8(0, 5); // flags - /* - @todo: this is wrong, - if there are more messages, add them to a queue - and process the next one when client has ack. (message without data) - */ - response(resBuffer); - } while (data && i * maxFragmentSize < data.length); + console.log('sending message with identifier', currentIdentifier); + ({ identifier: currentIdentifier, response: currentResponse } = await currentResponse( + resBuffer + )); + console.log('next message got identifier', currentIdentifier); + } while (data && sentDataSize < data.length); + + console.log('DONE', sentDataSize, data && data.length); } handleEAPMessage(msg: Buffer, state: string, handlers: IResponseHandlers) { @@ -148,8 +160,7 @@ export class EAPHandler { Message Length | Data... +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - - console.log('RESPONDING WITH IDENTIDY / START'); + console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', {}); this.sendEAPResponse(handlers.response, identifier, undefined, 21, 0x20); @@ -167,14 +178,17 @@ export class EAPHandler { break; case 21: // EAP TTLS this.eapTTLS.handleMessage(msg, state, handlers, identifier); - return; + break; case 3: // nak - this.sendEAPResponse(handlers.response, identifier, undefined, 3); + // this.sendEAPResponse(handlers.response, identifier, undefined, 3); break; case 2: // notification + console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: notification', {}); console.info('notification'); break; case 4: // md5-challenge + console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: md5-challenge', {}); + console.info('md5-challenge'); break; case 254: // expanded type @@ -183,7 +197,11 @@ export class EAPHandler { break; default: - console.error('unsupported type', type); + // we do not support this auth type, ask for TTLS + console.error('unsupported type', type, 'requesting TTLS (21)'); + + this.sendEAPResponse(handlers.response, identifier, Buffer.from([21]), 3); + break; } break; diff --git a/src/eap/challenges/pap.ts b/src/eap/challenges/pap.ts index 4393a95..a756fbf 100644 --- a/src/eap/challenges/pap.ts +++ b/src/eap/challenges/pap.ts @@ -1,6 +1,6 @@ -import { IAuthChallenge } from '../../types/AuthChallenge'; +import { IEAPChallenge } from '../../types/EAPChallenge'; -export class PAPChallenge implements IAuthChallenge { +export class PAPChallenge implements IEAPChallenge { // i couldn't find any documentation about it, therefore best guess how this is processed... // http://www.networksorcery.com/enp/rfc/rfc1334.txt ? diff --git a/src/eap/eap-ttls.ts b/src/eap/eap-ttls.ts index 61739ed..9d954cd 100644 --- a/src/eap/eap-ttls.ts +++ b/src/eap/eap-ttls.ts @@ -1,10 +1,16 @@ +/* eslint-disable no-bitwise */ import * as events from 'events'; import * as tls from 'tls'; import { encodeTunnelPW, openTLSSockets, startTLSServer } from '../tls/crypt'; -import { AdditionalAuthHandler, IResponseHandlers } from '../types/Handler'; +import { AdditionalAuthHandler, ResponseAuthHandler } from '../types/Handler'; import { PAPChallenge } from './challenges/pap'; import { IEAPType } from '../types/EAPType'; +interface IEAPResponseHandlers { + response: (respData?: Buffer, msgType?: number) => void; + checkAuth: ResponseAuthHandler; +} + export class EAPTTLS implements IEAPType { papChallenge: PAPChallenge; @@ -12,23 +18,48 @@ export class EAPTTLS implements IEAPType { this.papChallenge = new PAPChallenge(); } - handleMessage(msg: Buffer, state: string, handlers, identifier: number) { - const flags = msg.slice(5, 6); // .toString('hex'); + decode(msg: Buffer) { + const flags = msg.slice(5, 6).readUInt8(0); // .toString('hex'); // if (flags) // @todo check if "L" flag is set in flags - const msglength = msg.slice(6, 10).readInt32BE(0); // .toString('hex'); - const data = msg.slice(6, msg.length); // 10); //.toString('hex'); + const decodedFlags = { + lengthIncluded: flags & 0b010000000, + moreFragments: flags & 0b001000000, + start: flags & 0b000100000, + reserved: flags & 0b000011000, + version: flags & 0b010000111 + }; + let msglength; + if (decodedFlags.lengthIncluded) { + msglength = msg.slice(6, 10).readInt32BE(0); // .readDoubleLE(0); // .toString('hex'); + } + const data = msg.slice(decodedFlags.lengthIncluded ? 10 : 6, msg.length); + + return { + decodedFlags, + msglength, + data + }; + } + + handleMessage(msg: Buffer, state: string, handlers, identifier: number) { + const { decodedFlags, msglength, data } = this.decode(msg); // check if no data package is there and we have something in the queue, if so.. empty the queue first - if (!data) { + if (!data || data.length === 0) { // @todo: queue processing - console.warn('no data, just a confirmation!'); + console.warn( + `>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS, ACK / NACK (no data, just a confirmation, ID: ${identifier})` + ); return; } - console.log('incoming EAP TTLS', { - flags /* + console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS', { + // flags: `00000000${flags.toString(2)}`.substr(-8), + decodedFlags, + identifier, + /* 0 1 2 3 4 5 6 7 +---+---+---+---+---+---+---+---+ | L | M | S | R | R | V | @@ -39,14 +70,15 @@ export class EAPTTLS implements IEAPType { S = Start R = Reserved V = Version (000 for EAP-TTLSv0) - */, + */ + msglength, data, dataStr: data.toString() }); let currentConnection = openTLSSockets.get(state) as - | { events: events.EventEmitter; tls: tls.TLSSocket; currentHandlers: IResponseHandlers } + | { events: events.EventEmitter; tls: tls.TLSSocket; currentHandlers: IEAPResponseHandlers } | undefined; if (!currentConnection) { const connection = startTLSServer(); @@ -71,12 +103,15 @@ export class EAPTTLS implements IEAPType { // pwd not found.. console.error('pwd not found', err); // NAK + currentConnection!.currentHandlers.response(undefined, 3); + + /* this.sendEAPResponse( currentConnection!.currentHandlers.response, identifier, undefined, 3 - ); + ); */ currentConnection!.events.emit('end'); throw new Error(`pwd not found`); } @@ -85,16 +120,28 @@ export class EAPTTLS implements IEAPType { console.log('data', incomingData); console.log('data str', incomingData.toString()); - currentConnection!.events.emit('end'); - throw new Error(`unsupported auth type${type}`); + // currentConnection!.events.emit('end'); + + console.log('UNSUPPORTED AUTH TYPE, requesting PAP'); + // throw new Error(`unsupported auth type${type}`); + currentConnection!.currentHandlers.response(Buffer.from([1]), 3); + + /* + this.sendEAPResponse( + currentConnection!.currentHandlers.response, + identifier, + Buffer.from([1]), + 3 + ); */ } }); currentConnection.events.on('response', (responseData: Buffer) => { - console.log('sending encrypted data back to client', responseData); + // console.log('sending encrypted data back to client', responseData); // send back... - this.sendEAPResponse(currentConnection!.currentHandlers.response, identifier, responseData); + currentConnection!.currentHandlers.response(responseData); + // this.sendEAPResponse(currentConnection!.currentHandlers.response, identifier, responseData); // this.sendMessage(TYPE.PRELOGIN, data, false); }); @@ -103,13 +150,14 @@ export class EAPTTLS implements IEAPType { console.log('ENDING SOCKET'); openTLSSockets.del(state); }); - } else { + } /* else { console.log('using existing socket'); - } + } */ // update handlers currentConnection.currentHandlers = { - ...handlers, + response: (respData?: Buffer, msgType?: number) => + this.sendEAPResponse(handlers.response, identifier, respData, msgType), checkAuth: (username: string, password: string) => { const additionalAuthHandler: AdditionalAuthHandler = (success, params) => { const buffer = Buffer.from([ @@ -138,7 +186,7 @@ export class EAPTTLS implements IEAPType { 'ttls keying material' ); - console.log('keyingMaterial', keyingMaterial); + // console.log('keyingMaterial', keyingMaterial); // eapKeyData + len params.attributes.push([ diff --git a/src/helpers.ts b/src/helpers.ts index 2a9080f..208d640 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -7,3 +7,33 @@ export function makeid(length) { } return result; } + +export const MAX_RADIUS_ATTRIBUTE_SIZE = 253; + +export interface IDeferredPromise { + promise: Promise; + resolve: (value?: unknown) => Promise; + reject: (reason?: any) => Promise; +} + +export const newDeferredPromise = (): IDeferredPromise => { + if (Promise && !('deferred' in Promise)) { + let fResolve; + let fReject; + + const P = new Promise((resolve, reject) => { + fResolve = resolve; + fReject = reject; + }); + return { + promise: P, + resolve: fResolve, + reject: fReject + }; + } + + return (Promise as any).deferred; +}; + +export const delay = (timeout: number) => + new Promise(resolve => setTimeout(() => resolve(), timeout)); diff --git a/src/tls/crypt.ts b/src/tls/crypt.ts index cf88280..608c4b8 100644 --- a/src/tls/crypt.ts +++ b/src/tls/crypt.ts @@ -5,14 +5,21 @@ import { createSecureContext } from 'tls'; import * as fs from 'fs'; import * as crypto from 'crypto'; import * as DuplexPair from 'native-duplexpair'; +import * as constants from 'constants'; import { makeid } from '../helpers'; +import * as config from '../../config'; // https://nodejs.org/api/tls.html -const tlsOptions = { - cert: fs.readFileSync('./ssl/public-cert.pem'), - key: fs.readFileSync('./ssl/private-key.pem'), - ecdhCurve: 'auto' +const tlsOptions: tls.SecureContextOptions = { + ...config.certificate, + // ca: fs.readFileSync('./ssl/server.pem'), + // eslint-disable-next-line no-bitwise + secureOptions: constants.SSL_OP_NO_TICKET // : constants.SSL_OP_NO_TLSv1_2 | constants.SSL_OP_NO_TLSv1_1, + // honorCipherOrder: true + // secureOptions: + // ecdhCurve: 'auto' }; +console.log('tlsOptions', tlsOptions); const secureContext = createSecureContext(tlsOptions); export const openTLSSockets = new NodeCache({ useClones: false, stdTTL: 3600 }); // keep sockets for about one hour @@ -22,7 +29,11 @@ export function startTLSServer(): { events: events.EventEmitter; tls: tls.TLSSoc const cleartext = new tls.TLSSocket(duplexpair.socket1, { secureContext, - isServer: true + isServer: true, + enableTrace: true, + rejectUnauthorized: false, + // handshakeTimeout: 10, + requestCert: false }); const encrypted = duplexpair.socket2; @@ -71,6 +82,7 @@ export function startTLSServer(): { events: events.EventEmitter; tls: tls.TLSSoc console.log('*********** new client connection established / secured ********'); // this.emit('secure', securePair.cleartext); // this.encryptAllFutureTraffic(); + console.log('GET FINSIHED', cleartext.getFinished()); }); cleartext.on('error', (err?: Error) => { diff --git a/src/types/Authentication.ts b/src/types/Authentication.ts new file mode 100644 index 0000000..f8ce7ef --- /dev/null +++ b/src/types/Authentication.ts @@ -0,0 +1,3 @@ +export interface IAuthentication { + authenticate(username: string, password: string): Promise; +} diff --git a/src/types/AuthChallenge.ts b/src/types/EAPChallenge.ts similarity index 65% rename from src/types/AuthChallenge.ts rename to src/types/EAPChallenge.ts index 9d3f2bc..d2c4ddb 100644 --- a/src/types/AuthChallenge.ts +++ b/src/types/EAPChallenge.ts @@ -1,3 +1,3 @@ -export interface IAuthChallenge { +export interface IEAPChallenge { decode(data: Buffer): { username: string; password: string }; } diff --git a/src/types/EAPType.ts b/src/types/EAPType.ts index ce9bf5e..fb50dba 100644 --- a/src/types/EAPType.ts +++ b/src/types/EAPType.ts @@ -1,3 +1,3 @@ export interface IEAPType { - handleMessage(msg: Buffer, state: string, handlers, identifier: number) + handleMessage(msg: Buffer, state: string, handlers, identifier: number); } diff --git a/src/types/Handler.ts b/src/types/Handler.ts index f368e61..165eb9a 100644 --- a/src/types/Handler.ts +++ b/src/types/Handler.ts @@ -1,6 +1,8 @@ import { RadiusPacket } from 'radius'; -export type ResponseHandler = (msg: Buffer) => void; +export type ResponseHandler = ( + msg: Buffer +) => Promise<{ identifier: number; response: ResponseHandler }>; export type ResponseAuthHandler = ( username: string, password: string, diff --git a/ssl/README.md b/ssl/README.md new file mode 100644 index 0000000..aa76d92 --- /dev/null +++ b/ssl/README.md @@ -0,0 +1,8 @@ +this is based on freeradius cert directory :) + +1.) edit ca.cnf +2.) edit server.cnf +3.) replace your choosen pwd (deafult is whatever2020) in create.sh +4.) run ./create.sh +5.) set your choosen pwd in ~/config.js + diff --git a/ssl/ca.cnf b/ssl/ca.cnf new file mode 100644 index 0000000..86fbf40 --- /dev/null +++ b/ssl/ca.cnf @@ -0,0 +1,61 @@ +[ ca ] +default_ca = CA_default + +[ CA_default ] +dir = ./cert/ +certs = $dir +crl_dir = $dir/crl +database = db/index.txt +new_certs_dir = $dir +certificate = $dir/ca.pem +serial = db/serial +crl = $dir/crl.pem +private_key = $dir/ca.key +RANDFILE = $dir/.rand +name_opt = ca_default +cert_opt = ca_default +default_days = 60 +default_crl_days = 30 +default_md = sha256 +preserve = no +policy = policy_match +crlDistributionPoints = URI:http://www.example.org/example_ca.crl + +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ req ] +prompt = no +distinguished_name = certificate_authority +default_bits = 2048 +input_password = whatever2020 +output_password = whatever2020 +x509_extensions = v3_ca + +[certificate_authority] +countryName = AT +stateOrProvinceName = Vienna +localityName = Vienna +organizationName = hokify.com +emailAddress = info@hokify.com +commonName = "hokify GmbH" + +[v3_ca] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +basicConstraints = critical,CA:true +crlDistributionPoints = URI:http://www.example.org/example_ca.crl diff --git a/ssl/cert/index.txt b/ssl/cert/index.txt new file mode 100644 index 0000000..e69de29 diff --git a/ssl/cert/serial b/ssl/cert/serial new file mode 100644 index 0000000..4daddb7 --- /dev/null +++ b/ssl/cert/serial @@ -0,0 +1 @@ +00 diff --git a/ssl/create.sh b/ssl/create.sh index c9ea6c9..292a3c9 100755 --- a/ssl/create.sh +++ b/ssl/create.sh @@ -1,2 +1,15 @@ -openssl genrsa -out private-key.pem 1024 -openssl req -new -key private-key.pem -out csr.pem +# generate private key +# openssl genrsa -out csr.key 2048 + +# CA +openssl req -new -x509 -keyout cert/ca.key -out cert/ca.pem -days 3600 -config ./ca.cnf + +# server +openssl req -new -out cert/server.csr -keyout cert/server.key -config ./server.cnf + +# sign it +# -key $(PASSWORD_CA) (default pwd is whatever2020) +openssl ca -batch -keyfile cert/ca.key -cert cert/ca.pem -in cert/server.csr -key whatever2020 -out cert/server.crt -extensions xpserver_ext -extfile xpextensions -config ./server.cnf + +# sign it +# openssl x509 -req -in csr.pem -signkey private-key.pem -out public-cert.pem diff --git a/ssl/server.cnf b/ssl/server.cnf new file mode 100644 index 0000000..4bcbc95 --- /dev/null +++ b/ssl/server.cnf @@ -0,0 +1,54 @@ +[ ca ] +default_ca = CA_default + +[ CA_default ] +dir = ./cert/ +certs = $dir +crl_dir = $dir/crl +database = db/index.txt +new_certs_dir = $dir +certificate = $dir/server.pem +serial = db/serial +crl = $dir/crl.pem +private_key = $dir/server.key +RANDFILE = $dir/.rand +name_opt = ca_default +cert_opt = ca_default +default_days = 6000 +default_crl_days = 30 +default_md = sha256 +preserve = no +policy = policy_match + +[ policy_match ] +countryName = match +stateOrProvinceName = match +organizationName = match +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +localityName = optional +organizationName = optional +organizationalUnitName = optional +commonName = supplied +emailAddress = optional + +[ req ] +prompt = no +distinguished_name = server +default_bits = 2048 +input_password = whatever2020 +output_password = whatever2020 + +[server] +countryName = AT +stateOrProvinceName = Vienna +localityName = Vienna +organizationName = hokify.com +emailAddress = info@hokify.com +commonName = "hokify GmbH" + diff --git a/ssl/sign.sh b/ssl/sign.sh deleted file mode 100644 index a5bf52a..0000000 --- a/ssl/sign.sh +++ /dev/null @@ -1 +0,0 @@ -openssl x509 -req -in csr.pem -signkey private-key.pem -out public-cert.pem diff --git a/ssl/xpextensions b/ssl/xpextensions new file mode 100644 index 0000000..8e4a9a2 --- /dev/null +++ b/ssl/xpextensions @@ -0,0 +1,24 @@ +# +# File containing the OIDs required for Windows. +# +# http://support.microsoft.com/kb/814394/en-us +# +[ xpclient_ext] +extendedKeyUsage = 1.3.6.1.5.5.7.3.2 +crlDistributionPoints = URI:http://www.example.com/example_ca.crl + +[ xpserver_ext] +extendedKeyUsage = 1.3.6.1.5.5.7.3.1 +crlDistributionPoints = URI:http://www.example.com/example_ca.crl + +# +# Add this to the PKCS#7 keybag attributes holding the client's private key +# for machine authentication. +# +# the presence of this OID tells Windows XP that the cert is intended +# for use by the computer itself, and not by an end-user. +# +# The other solution is to use Microsoft's web certificate server +# to generate these certs. +# +# 1.3.6.1.4.1.311.17.2