From ffbcf1b514ca51964229e6bf293366a9d1c2ccde Mon Sep 17 00:00:00 2001 From: garrettmills Date: Mon, 22 Nov 2021 09:08:22 -0600 Subject: [PATCH] More RADIUS work --- .gitignore | 1 + app/controllers/api/v1/Radius.controller.js | 7 ++- app/unit/RadiusUnit.js | 70 ++++++++++++++++++--- package.json | 2 +- yarn.lock | 54 ++++++++-------- 5 files changed, 96 insertions(+), 38 deletions(-) diff --git a/.gitignore b/.gitignore index 1b1010e..c9dd28e 100644 --- a/.gitignore +++ b/.gitignore @@ -150,3 +150,4 @@ tmp.uploads/* !tmp.uploads/.gitkeep uploads/* !uploads/.gitkeep +ttls-pap.conf diff --git a/app/controllers/api/v1/Radius.controller.js b/app/controllers/api/v1/Radius.controller.js index 8f58388..32ab26a 100644 --- a/app/controllers/api/v1/Radius.controller.js +++ b/app/controllers/api/v1/Radius.controller.js @@ -9,6 +9,9 @@ class RadiusController extends Controller { const User = this.models.get('auth:User') const Client = this.models.get('radius:Client') + this.output.debug('RADIUS attempt:') + this.output.debug(req.body) + if ( !req.body.username || !req.body.password ) { this.output.error('RADIUS error: missing username or password') return this.fail(res) @@ -21,7 +24,9 @@ class RadiusController extends Controller { parts.reverse() const username = parts.join('@') - const password = req.body.password + const password = String(req.body.password).replace(/\0/g, '') + + this.output.debug(`clientId: ${clientId}, username: ${username}, password: ${password}`) const user = await User.findOne({ uid: username, active: true }) if ( !user ) { diff --git a/app/unit/RadiusUnit.js b/app/unit/RadiusUnit.js index f5f35ce..2d6a9b9 100644 --- a/app/unit/RadiusUnit.js +++ b/app/unit/RadiusUnit.js @@ -12,6 +12,38 @@ class RadiusUnit extends Unit { return [...super.services, 'configs', 'output', 'models'] } + getPacketDecoder() { + const RadiusClient = this.models.get('radius:Client') + + return async (msg) => { + const clients = await RadiusClient.find({ active: true }) + + // Try the secrets for all active clients. + // If we can successfully decode the packet with a client's secret, then we know + // that the message came from that client. + let authenticatedClient + let packet + for ( const client of clients ) { + try { + packet = radius.decode({ packet: msg, secret: client.secret }) + authenticatedClient = client + break + } catch (e) {} + } + + packet.credentialMiddleware = (username, password) => { + this.output.debug(`Called credential middleware: ${username}`) + return [`${username}@${authenticatedClient.id}`, password] + } + + return { + packet, + secret: authenticatedClient.secret || '', + } + } + + } + async go(app) { if ( !(await this.port_free()) ) { this.output.info('RADIUS server port is in use. Will not start!') @@ -19,15 +51,27 @@ class RadiusUnit extends Unit { } const config = this.getConfig() + const packageInterface = require('@coreid/radius-server/dist/interface').default.get() + packageInterface.setConfig(config) + packageInterface.packetDecoder = this.getPacketDecoder() + packageInterface.log = (...any) => any.forEach(x => this.output.info(x)) + + const { RadiusServer } = require('@coreid/radius-server/dist/radius/RadiusServer') + const server = RadiusServer.get() + + await server.up() + this.output.success('Started RADIUS server!') + + // await packageInterface.up() // Overwrite radius-server's global config object with the user-provided values - const radiusConfig = require('radius-server/config') + /*const radiusConfig = require('radius-server/config') for ( const key in config ) { if ( !Object.hasOwnProperty.apply(config, [key]) ) continue radiusConfig[key] = config[key] - } + }*/ - const { Authentication } = require('radius-server/dist/auth') + /*const { Authentication } = require('radius-server/dist/auth') const { UDPServer } = require('radius-server/dist/server/UDPServer') const { RadiusService } = require('radius-server/dist/radius/RadiusService') @@ -65,15 +109,21 @@ class RadiusUnit extends Unit { // Start the radius server await this.server.start() - this.output.success('Started RADIUS server!') + this.output.success('Started RADIUS server!')*/ } async cleanup(app) { - if ( this.server ) { + const { RadiusServer } = require('@coreid/radius-server/dist/radius/RadiusServer') + const server = RadiusServer.get() + await server.down() + + // const packageInterface = require('@coreid/radius-server/dist/interface').get() + // await packageInterface.down() + /*if ( this.server ) { // radius-server doesn't expose a "close" method explicitly, which is annoying // instead, reach in and close the internal UDP socket this.server.server.close() - } + }*/ } /** @@ -92,7 +142,7 @@ class RadiusUnit extends Unit { let packet for ( const client of clients ) { try { - packet = radius.decode({ packet: msg, secret: client.secret }) + packet = radius.decode({ packet: msg, secret: this.getConfig().secret /*client.secret*/ }) authenticatedClient = client break } catch (e) {} @@ -107,12 +157,14 @@ class RadiusUnit extends Unit { // This allows us to check IAM access in the controller packet.attributes['User-Name'] = `${packet.attributes['User-Name']}@${authenticatedClient.id}` this.output.info(`RADIUS auth attempt: ${packet.attributes['User-Name']}`) + this.output.debug(packet) const response = await this.radiusService.packetHandler.handlePacket(packet) // still no response, we are done here if (!response || !response.code) { this.output.debug(`RADIUS error: no response / response code`) + this.output.debug(response) return } @@ -121,7 +173,7 @@ class RadiusUnit extends Unit { data: radius.encode_response({ packet, code: response.code, - secret: authenticatedClient.secret, // use the client's secret to encode the response + secret: this.getConfig().secret, // authenticatedClient.secret, // use the client's secret to encode the response attributes: response.attributes, }), @@ -150,7 +202,7 @@ class RadiusUnit extends Unit { const config = { port: this.configs.get('radius.port', 1812), - secret: uuid(), // this is never used - client-specific secrets are injected instead + secret: 'testing123', // uuid(), // this is never used - client-specific secrets are injected instead certificate: { cert: fs.readFileSync(this.configs.get('radius.cert_file.public')), key: [ diff --git a/package.json b/package.json index f1d02a7..51691b7 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "author": "Garrett Mills (https://garrettmills.dev/)", "license": "MIT", "dependencies": { + "@coreid/radius-server": "^1.2.6", "bullmq": "^1.8.8", "email-validator": "^2.0.4", "flitter-auth": "^0.19.6", @@ -40,7 +41,6 @@ "oidc-provider": "^6.29.0", "qrcode": "^1.4.4", "radius": "^1.1.4", - "radius-server": "^1.2.0", "samlp": "^3.4.1", "speakeasy": "^2.0.0", "uuid": "^8.3.0", diff --git a/yarn.lock b/yarn.lock index 5aefe68..c400f24 100644 --- a/yarn.lock +++ b/yarn.lock @@ -182,7 +182,24 @@ lodash "^4.17.13" to-fast-properties "^2.0.0" -"@hokify/node-ts-cache@^5.4.1": +"@coreid/radius-server@^1.2.6": + version "1.2.6" + resolved "https://registry.yarnpkg.com/@coreid/radius-server/-/radius-server-1.2.6.tgz#51914c20740a5ba7a3eed36deb67f3def4e69ced" + integrity sha512-oYmEytDUf1uiTd+pnqHlODXoz/ybjkPZpEjUmB9gjF/DQ/PwwiUDtRXfRLD6fjkY58Z4y4LF4YXDilAgSnPWlA== + dependencies: + "@hokify/node-ts-cache" "^5.5.1" + axios "^0.21.1" + debug "^4.3.1" + imap-simple "^5.0.0" + ldapauth-fork "^5.0.1" + ldapjs "^2.3.0" + native-duplexpair "^1.0.0" + node-cache "^5.1.2" + radius "~1.1.4" + smtp-client "^0.4.0" + yargs "~17.0.1" + +"@hokify/node-ts-cache@^5.5.1": version "5.6.0" resolved "https://registry.yarnpkg.com/@hokify/node-ts-cache/-/node-ts-cache-5.6.0.tgz#47a7b9f2be426c03f1c1d92adcecfda06e3fa60c" integrity sha512-VdjeFjLAT9gGrRlzjZsMeY9ZWJyfz8y8ODeZsUdSnDdwxqb0keZNAtS3PMswBzSECBfNV1RT+NGiC8j6P1Cv6w== @@ -3310,7 +3327,7 @@ ldapjs@^1.0.2: optionalDependencies: dtrace-provider "~0.8" -ldapjs@^2.2.1, ldapjs@^2.2.3: +ldapjs@^2.2.1, ldapjs@^2.3.0: version "2.3.1" resolved "https://registry.yarnpkg.com/ldapjs/-/ldapjs-2.3.1.tgz#04136815fb1f21d692ac87fab5961a04d86e8b04" integrity sha512-kf0tHHLrpwKaBAQOhYHXgdeh2PkFuCCxWgLb1MRn67ZQVo787D2pij3mmHVZx193GIdM8xcfi8HF6AIYYnj0fQ== @@ -4576,23 +4593,6 @@ quoted-printable@^1.0.0: dependencies: utf8 "^2.1.0" -radius-server@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/radius-server/-/radius-server-1.2.0.tgz#74d5932b66906c0b5ea7763bb11bb1c6a6a219e6" - integrity sha512-JRscm5uafissZMgJYFOemiUjnwpmIWEjVAL0O/tn9Fiy2mqPAX+xVKqWEopu2BZ7NZzHScqlQnVomLyzyHk08Q== - dependencies: - "@hokify/node-ts-cache" "^5.4.1" - axios "^0.21.1" - debug "^4.3.1" - imap-simple "^5.0.0" - ldapauth-fork "^5.0.1" - ldapjs "^2.2.3" - native-duplexpair "^1.0.0" - node-cache "^5.1.2" - radius "~1.1.4" - smtp-client "^0.3.3" - yargs "~16.2.0" - radius@^1.1.4, radius@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/radius/-/radius-1.1.4.tgz#3ce58d18a497d615618bc7d9286464ebecb50cb5" @@ -5136,10 +5136,10 @@ smtp-channel@0.2.3: line-buffer "^0.1.4" promised-timeout "^0.2.0" -smtp-client@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/smtp-client/-/smtp-client-0.3.3.tgz#54f5211b5613f86cb801892e298c53f60cdde515" - integrity sha512-bneg34/4sug5MAVROUaOgQFEEDQDcmv5qXa/E9mDdwjV9jOvexidMzGH9jR1cgQCsFh1yWrVToY0MCWvoVwWSA== +smtp-client@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/smtp-client/-/smtp-client-0.4.0.tgz#2015da1f4d9821e67bf22ced01568b027d305245" + integrity sha512-TvxdX02b96hN6732TGkXcXHsaTeXgVMXjdAhYzz/pLGE6V1p/5jh1XYJa/PfE+O21jHLmwHYWVP3c7JNFWwmOg== dependencies: promised-timeout "0.5.1" smtp-channel "0.2.3" @@ -5977,10 +5977,10 @@ yargs@^15.0.2: y18n "^4.0.0" yargs-parser "^18.1.1" -yargs@~16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== +yargs@~17.0.1: + version "17.0.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb" + integrity sha512-xBBulfCc8Y6gLFcrPvtqKz9hz8SO0l1Ni8GgDekvBX2ro0HRQImDGnikfc33cgzcYUSncapnNcZDjVFIH3f6KQ== dependencies: cliui "^7.0.2" escalade "^3.1.1"