refactor: major code cleanups :-)

first working version
master
simon 4 years ago
parent c285a15bee
commit 42a05b069f

8
.gitignore vendored

@ -4,10 +4,8 @@ node_modules/*
# build files
dist
# certificates
ssl/*.pem
*.crt
*.key
# ts
tsconfig.tsbuildinfo
# custom certificates
/ssl-*/

@ -1,17 +1,18 @@
Basic RADIUS Server for node.js for Google LDAP Service and WPA2 Enteprise WLAN Authentification.
* Only implements LDAP as Authentification Backend
* Only WPA TTLS implemented (as this is the only one that works with Google LDAP Service)
- supports LDAP Authentification Backend
- supports WPA2 Entprise (TTLS over PAP)
Protect your WIFI access with a username and password by a credential provider you already use!
## Known Issues / Disclaimer
This is a first implementation draft, which is currently only working with a nodejs fork (see https://github.com/nodejs/node/pull/31814).
* PAP / CHAP RFC not found to implement this correctly
* Project needs more structure and interfaces to extend it more easily in the future (make a full radius server out of it ;)?)
* No package queuing or any kind of respsecting the MTU size
* a lot of bugs
- PAP / CHAP RFC not found to implement this correctly
- a lot of bugs
CONTRIBUTIONS WELCOME!
CONTRIBUTIONS WELCOME! If you are willing to help, just open a PR or contact me via bug system or simon.tretter@hokify.com.
## Installation
@ -21,17 +22,39 @@ CONTRIBUTIONS WELCOME!
## Introduction
This app provides a radius server to authenticate against google's SLDAP service. To get this running
you need:
1.) Running LDAP Service (E.g. Google Suite Enterprise or Gloud Identity Premium)
2.) Use stunnel to connect to the LDAP service and connect this app to the stunnel (I didn't get the client ldap authentication working in here yet)
3.) Install a SSL certificate (e.g. self signed via npm run create-certificate)
4.) Install und build server: npm install && npm run build
5.) Start server node dist/app.ts --secret {RADIUS secret} --baseDN dc=hokify,dc=com
you need:
1. Running LDAP Service (E.g. Google Suite Enterprise or Gloud Identity Premium)
2. Optional: Create your own SSL certificate (e.g. self signed via npm run create-certificate)
3. Check config.js and adapt to your needs
- configure authentication (passport config), e.g. for LDAP
```js
var config = {
// ....
authentication: 'ldap',
authenticationOptions: {
url: 'ldap://127.0.0.1:1636',
base: 'dc=hokify,dc=com'
}
};
```
- set radius secret
4. Install und build server: npm install && npm run build
5. Start server "npm run start"
## Authentications
right now only one simple ldap implementation is done,
the idea is though to use [passport.js](http://www.passportjs.org/) as authentication provider,
therefore it would be possible to use the radius server with your email provider authentication or any
other auth mechanismus you use (well everything with no 2factor or anything else that requries an extra step).
## Usage
You need to specify at least a radius password and the base DN for LDAP:
node dist/app.ts --secret {RADIUS secret} --baseDN dc=hokify,dc=com
npm run start

@ -1,14 +1,19 @@
import * as fs from 'fs';
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require('fs');
const path = require('path');
const SSL_CERT_DIRECTORY = './ssl/cert';
module.exports = {
port: 1812,
// radius secret
secret: 'testing123',
certificate: {
cert: fs.readFileSync('./ssl/cert/server.crt'),
cert: fs.readFileSync(path.join(SSL_CERT_DIRECTORY, '/server.crt')),
key: [
{
pem: fs.readFileSync('./ssl/cert/server.key'),
pem: fs.readFileSync(path.join(SSL_CERT_DIRECTORY, '/server.key')),
passphrase: 'whatever2020'
}
]

228
package-lock.json generated

@ -34,13 +34,13 @@
}
},
"@hokify/eslint-config": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@hokify/eslint-config/-/eslint-config-0.2.2.tgz",
"integrity": "sha512-FvVZd8hruAriQ84UR0MqxRtXXUT9Eqe9LTGoU90sQ6TgHXTGPQpOfY+8DAICX0HtRc5ntB/ZEgL6L9ims4NLxA==",
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/@hokify/eslint-config/-/eslint-config-0.2.4.tgz",
"integrity": "sha512-wUiJ1mBc+6TEqqB1qK0LsaMazg6h19MwA4jlMn4Qltgiql7S2EmWDSK0ID9wCmI4bgUB6WqZsNQqvkMxNG7hYw==",
"dev": true,
"requires": {
"@typescript-eslint/eslint-plugin": "^2.19.2",
"@typescript-eslint/parser": "^2.19.2",
"@typescript-eslint/eslint-plugin": "^2.20.0",
"@typescript-eslint/parser": "^2.20.0",
"eslint-config-airbnb-typescript": "^7.0.0",
"eslint-config-prettier": "^6.10.0",
"eslint-import-resolver-alias": "^1.1.2",
@ -50,11 +50,20 @@
"eslint-loader": "^3.0.3",
"eslint-plugin-import": "2.20.1",
"eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-mocha": "^6.2.2",
"eslint-plugin-mocha": "^6.3.0",
"eslint-plugin-node": "^11.0.0",
"eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.18.3",
"eslint-plugin-unicorn": "^16.0.0"
"eslint-plugin-unicorn": "^16.1.1"
}
},
"@types/body-parser": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
"requires": {
"@types/connect": "*",
"@types/node": "*"
}
},
"@types/color-name": {
@ -62,12 +71,39 @@
"resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz",
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
},
"@types/connect": {
"version": "3.4.33",
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
"requires": {
"@types/node": "*"
}
},
"@types/eslint-visitor-keys": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
"integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==",
"dev": true
},
"@types/express": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
"requires": {
"@types/body-parser": "*",
"@types/express-serve-static-core": "*",
"@types/serve-static": "*"
}
},
"@types/express-serve-static-core": {
"version": "4.17.2",
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz",
"integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==",
"requires": {
"@types/node": "*",
"@types/range-parser": "*"
}
},
"@types/ioredis": {
"version": "4.14.7",
"resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.14.7.tgz",
@ -92,11 +128,15 @@
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/ldapjs/-/ldapjs-1.0.5.tgz",
"integrity": "sha512-hrtMZjVfWNPxkwFkYhLAU0ITZ3/reDft4jDzLBvrGDSKCbEvW+GeZb4PgM3jlSwSsv0cXqnDeWcupFLIgW9E0Q==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/mime": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
},
"@types/node": {
"version": "13.7.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.1.tgz",
@ -116,6 +156,14 @@
"integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
"dev": true
},
"@types/passport": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.2.tgz",
"integrity": "sha512-Pf39AYKf8q+YoONym3150cEwfUD66dtwHJWvbeOzKxnA0GZZ/vAXhNWv9vMhKyRQBQZiQyWQnhYBEBlKW6G8wg==",
"requires": {
"@types/express": "*"
}
},
"@types/radius": {
"version": "0.0.28",
"resolved": "https://registry.npmjs.org/@types/radius/-/radius-0.0.28.tgz",
@ -125,6 +173,11 @@
"@types/node": "*"
}
},
"@types/range-parser": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
},
"@types/redis": {
"version": "2.8.15",
"resolved": "https://registry.npmjs.org/@types/redis/-/redis-2.8.15.tgz",
@ -133,13 +186,22 @@
"@types/node": "*"
}
},
"@types/serve-static": {
"version": "1.13.3",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
"requires": {
"@types/express-serve-static-core": "*",
"@types/mime": "*"
}
},
"@typescript-eslint/eslint-plugin": {
"version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.19.2.tgz",
"integrity": "sha512-HX2qOq2GOV04HNrmKnTpSIpHjfl7iwdXe3u/Nvt+/cpmdvzYvY0NHSiTkYN257jHnq4OM/yo+OsFgati+7LqJA==",
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.20.0.tgz",
"integrity": "sha512-cimIdVDV3MakiGJqMXw51Xci6oEDEoPkvh8ggJe2IIzcc0fYqAxOXN6Vbeanahz6dLZq64W+40iUEc9g32FLDQ==",
"dev": true,
"requires": {
"@typescript-eslint/experimental-utils": "2.19.2",
"@typescript-eslint/experimental-utils": "2.20.0",
"eslint-utils": "^1.4.3",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
@ -147,32 +209,32 @@
}
},
"@typescript-eslint/experimental-utils": {
"version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.19.2.tgz",
"integrity": "sha512-B88QuwT1wMJR750YvTJBNjMZwmiPpbmKYLm1yI7PCc3x0NariqPwqaPsoJRwU9DmUi0cd9dkhz1IqEnwfD+P1A==",
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.20.0.tgz",
"integrity": "sha512-fEBy9xYrwG9hfBLFEwGW2lKwDRTmYzH3DwTmYbT+SMycmxAoPl0eGretnBFj/s+NfYBG63w/5c3lsvqqz5mYag==",
"dev": true,
"requires": {
"@types/json-schema": "^7.0.3",
"@typescript-eslint/typescript-estree": "2.19.2",
"@typescript-eslint/typescript-estree": "2.20.0",
"eslint-scope": "^5.0.0"
}
},
"@typescript-eslint/parser": {
"version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.19.2.tgz",
"integrity": "sha512-8uwnYGKqX9wWHGPGdLB9sk9+12sjcdqEEYKGgbS8A0IvYX59h01o8os5qXUHMq2na8vpDRaV0suTLM7S8wraTA==",
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.20.0.tgz",
"integrity": "sha512-o8qsKaosLh2qhMZiHNtaHKTHyCHc3Triq6aMnwnWj7budm3xAY9owSZzV1uon5T9cWmJRJGzTFa90aex4m77Lw==",
"dev": true,
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
"@typescript-eslint/experimental-utils": "2.19.2",
"@typescript-eslint/typescript-estree": "2.19.2",
"@typescript-eslint/experimental-utils": "2.20.0",
"@typescript-eslint/typescript-estree": "2.20.0",
"eslint-visitor-keys": "^1.1.0"
}
},
"@typescript-eslint/typescript-estree": {
"version": "2.19.2",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.19.2.tgz",
"integrity": "sha512-Xu/qa0MDk6upQWqE4Qy2X16Xg8Vi32tQS2PR0AvnT/ZYS4YGDvtn2MStOh5y8Zy2mg4NuL06KUHlvCh95j9C6Q==",
"version": "2.20.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.20.0.tgz",
"integrity": "sha512-WlFk8QtI8pPaE7JGQGxU7nGcnk1ccKAJkhbVookv94ZcAef3m6oCE/jEDL6dGte3JcD7reKrA0o55XhBRiVT3A==",
"dev": true,
"requires": {
"debug": "^4.1.1",
@ -389,6 +451,11 @@
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==",
"dev": true
},
"bcryptjs": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
"integrity": "sha1-mrVie5PmBiH/fNrF2pczAn3x0Ms="
},
"big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@ -896,9 +963,9 @@
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
"emojis-list": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz",
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
"integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==",
"dev": true
},
"enhanced-resolve": {
@ -1361,12 +1428,24 @@
}
},
"eslint-plugin-mocha": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.2.2.tgz",
"integrity": "sha512-oNhPzfkT6Q6CJ0HMVJ2KLxEWG97VWGTmuHOoRcDLE0U88ugUyFNV9wrT2XIt5cGtqc5W9k38m4xTN34L09KhBA==",
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.3.0.tgz",
"integrity": "sha512-Cd2roo8caAyG21oKaaNTj7cqeYRWW1I2B5SfpKRp0Ip1gkfwoR1Ow0IGlPWnNjzywdF4n+kHL8/9vM6zCJUxdg==",
"dev": true,
"requires": {
"ramda": "^0.26.1"
"eslint-utils": "^2.0.0",
"ramda": "^0.27.0"
},
"dependencies": {
"eslint-utils": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz",
"integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==",
"dev": true,
"requires": {
"eslint-visitor-keys": "^1.1.0"
}
}
}
},
"eslint-plugin-node": {
@ -2159,6 +2238,25 @@
}
}
},
"ldapauth-fork": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/ldapauth-fork/-/ldapauth-fork-4.3.1.tgz",
"integrity": "sha512-IJUnkEDQg6D45jUKW3FFfMWZhUjZoGkN97WaMXF1cBod0gJq74d+iwRavPqiE3o/KNRgqwFesrdE4Ym4Fc1GIQ==",
"requires": {
"@types/ldapjs": "^1.0.0",
"@types/node": "^10.12.12",
"bcryptjs": "^2.4.0",
"ldapjs": "^1.0.2",
"lru-cache": "^5.1.1"
},
"dependencies": {
"@types/node": {
"version": "10.17.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.16.tgz",
"integrity": "sha512-A4283YSA1OmnIivcpy/4nN86YlnKRiQp8PYwI2KdPCONEBN093QTb0gCtERtkLyVNGKKIGazTZ2nAmVzQU51zA=="
}
}
},
"ldapjs": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz",
@ -2215,13 +2313,13 @@
}
},
"loader-utils": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz",
"integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz",
"integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==",
"dev": true,
"requires": {
"big.js": "^5.2.2",
"emojis-list": "^2.0.0",
"emojis-list": "^3.0.0",
"json5": "^1.0.1"
}
},
@ -2290,6 +2388,14 @@
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"requires": {
"yallist": "^3.0.2"
}
},
"make-error": {
"version": "1.3.5",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz",
@ -2509,9 +2615,9 @@
"dev": true
},
"object-hash": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.2.tgz",
"integrity": "sha512-b+2AKjAf6uQlxxv8ChHdM+VT4eeX+ZSwv+pk2xIXZWbo+yxn4/En1iC+GHe/OFYa9on0AhFF2PvuAcFHoiiHaA==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz",
"integrity": "sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg==",
"dev": true
},
"object-inspect": {
@ -2676,6 +2782,29 @@
"error-ex": "^1.2.0"
}
},
"passport-ldapauth": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/passport-ldapauth/-/passport-ldapauth-2.1.3.tgz",
"integrity": "sha512-23n425UTasN6XhcXG0qQ0h0YrS/zfo8kNIEhSLfPsNpglhYhhQFfB1pmDc5RrH+Kiz5fKLkki5BpvkKHCwkixg==",
"requires": {
"@types/node": "^10.12.26",
"@types/passport": "^1.0.0",
"ldapauth-fork": "^4.2.0",
"passport-strategy": "^1.0.0"
},
"dependencies": {
"@types/node": {
"version": "10.17.16",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.16.tgz",
"integrity": "sha512-A4283YSA1OmnIivcpy/4nN86YlnKRiQp8PYwI2KdPCONEBN093QTb0gCtERtkLyVNGKKIGazTZ2nAmVzQU51zA=="
}
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
},
"path-browserify": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz",
@ -2870,9 +2999,9 @@
"integrity": "sha512-UWuzdF6xf3NpsXFZZmUEkxtEalDXj8hdmMXgbGzn7vOk6zXNsiIY2I6SJ1euHt7PTQuMoz2qDEJB+AfJDJgQYw=="
},
"ramda": {
"version": "0.26.1",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz",
"integrity": "sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==",
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz",
"integrity": "sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==",
"dev": true
},
"randombytes": {
@ -3011,9 +3140,9 @@
"dev": true
},
"regexp-tree": {
"version": "0.1.19",
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.19.tgz",
"integrity": "sha512-mVeVLF/qg5qFbZSW0f7pXbuVX73dKIjuhOyC2JLKxzmpya75O4qLcvI9j0jp31Iz7IAkEVHa1UErDCAqaLKo5A==",
"version": "0.1.20",
"resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.20.tgz",
"integrity": "sha512-gSiH74kc00oTbQkN7tZogZe0ttKwyxyDVLAnU20aWoarbLE9AypbJHRlZ567h4Zi19q3cPVRWDYbfECElrHgsQ==",
"dev": true
},
"regexp.prototype.flags": {
@ -3587,9 +3716,9 @@
"dev": true
},
"typescript": {
"version": "3.7.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.7.5.tgz",
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
"version": "3.8.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.2.tgz",
"integrity": "sha512-EgOVgL/4xfVrCMbhYKUQTdF37SQn4Iw73H5BgCrF1Abdun7Kwy/QZsE/ssAy0y4LxBbvua3PIbFsbRczWWnDdQ==",
"dev": true
},
"universalify": {
@ -3761,6 +3890,11 @@
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz",
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
},
"yargs": {
"version": "15.1.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz",

@ -1,31 +1,32 @@
{
"name": "radius-server",
"description": "radius server for google LDAP and TTLT",
"version": "0.0.1",
"scripts": {
"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",
"create-certificate": "sh ./ssl/create.sh && sh ./ssl/sign.sh"
},
"dependencies": {
"native-duplexpair": "^1.0.0",
"ldapjs": "^1.0.2",
"node-cache": "^5.1.0",
"radius": "~1.1.4",
"ts-node": "^8.6.2",
"type-cacheable": "^4.0.0",
"yargs": "~15.1.0",
"md5": "^2.2.1",
"passport-ldapauth": "^2.1.3"
},
"devDependencies": {
"@types/ldapjs": "^1.0.5",
"@types/radius": "0.0.28",
"@hokify/eslint-config": "^0.2.2",
"eslint": "^6.8.0",
"prettier": "^1.19.1",
"typescript": "^3.7.5"
}
"name": "radius-server",
"description": "radius server for google LDAP and TTLT",
"version": "0.0.1",
"scripts": {
"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",
"create-certificate": "sh ./ssl/create.sh && sh ./ssl/sign.sh"
},
"dependencies": {
"native-duplexpair": "^1.0.0",
"ldapjs": "^1.0.2",
"node-cache": "^5.1.0",
"radius": "~1.1.4",
"ts-node": "^8.6.2",
"type-cacheable": "^4.0.0",
"yargs": "~15.1.0",
"md5": "^2.2.1",
"passport-ldapauth": "^2.1.3"
},
"license": "GPLv3",
"devDependencies": {
"@types/ldapjs": "^1.0.5",
"@types/radius": "0.0.28",
"@hokify/eslint-config": "^0.2.4",
"eslint": "^6.8.0",
"prettier": "^1.19.1",
"typescript": "^3.8.2"
}
}

@ -1,186 +1,42 @@
import * as dgram from 'dgram';
import * as radius from 'radius';
// import * as dgram from "dgram";
// import * as fs from 'fs';
import { EAPHandler } from './eap';
import { IDeferredPromise, makeid, MAX_RADIUS_ATTRIBUTE_SIZE, newDeferredPromise } from './helpers';
import { GoogleLDAPAuth } from './auth/google-ldap';
import { AdditionalAuthHandler } from './types/Handler';
const server = dgram.createSocket('udp4');
import { UDPServer } from './server/UDPServer';
import { RadiusService } from './radius/RadiusService';
const { argv } = require('yargs')
.usage('RADIUS Server\nUsage: $0')
.example('$0 --port 1812 -s radiussecret')
.default({
port: 1812,
s: 'testing123',
baseDN: 'dc=hokify,dc=com',
ldapServer: 'ldap://127.0.0.1:1636'
})
.describe('baseDN', 'LDAP Base DN')
.describe('ldapServer', 'LDAP Server')
.describe('port', 'RADIUS server listener port')
.alias('s', 'secret')
.describe('secret', 'RADIUS secret')
.string(['secret', 'baseDN'])
.demand('secret');
import * as config from '../config';
console.log(`Listener Port: ${argv.port}`);
console.log(`RADIUS Secret: ${argv.secret}`);
console.log(`LDAP Base DN: ${argv.baseDN}`);
console.log(`LDAP Server: ${argv.ldapServer}`);
console.log(`Listener Port: ${config.port || 1812}`);
console.log(`RADIUS Secret: ${config.secret}`);
console.log(`Auth Mode: ${config.authentication}`);
// const ldap = new LDAPAuth({url: 'ldap://ldap.google.com', base: 'dc=hokify,dc=com', uid: 'uid', tlsOptions});
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 });
if (packet.code !== 'Access-Request') {
console.log('unknown packet type: ', packet.code);
return;
}
// console.log('packet.attributes', packet.attributes);
// console.log('rinfo', rinfo);
async function checkAuth(
username: string,
password: string,
additionalAuthHandler?: AdditionalAuthHandler
) {
console.log(`Access-Request for ${username}`);
let success = false;
try {
await ldap.authenticate(username, password);
success = true;
} catch (err) {
console.error(err);
}
const attributes: any[] = [];
if (additionalAuthHandler) {
await additionalAuthHandler(success, { packet, attributes, secret: argv.secret });
}
const response = radius.encode_response({
packet,
code: success ? 'Access-Accept' : 'Access-Reject',
secret: argv.secret,
attributes
});
console.log(`Sending ${success ? 'accept' : 'reject'} for user ${username}`);
sendToClient(response, 0, response.length, rinfo.port, rinfo.address, function(err, _bytes) {
if (err) {
console.log('Error sending response to ', rinfo);
}
});
}
if (packet.attributes['EAP-Message']) {
const state = (packet.attributes.State && packet.attributes.State.toString()) || makeid(16);
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 + MAX_RADIUS_ATTRIBUTE_SIZE)
]);
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
const ldap = new GoogleLDAPAuth(
config.authenticationOptions.url,
config.authenticationOptions.base
);
const server = new UDPServer(config.port);
const radiusService = new RadiusService(config.secret, ldap);
(async () => {
server.on('message', async (msg, rinfo) => {
const response = await radiusService.handleMessage(msg);
if (response) {
server.sendToClient(
response.data,
rinfo.port,
rinfo.address,
(err, _bytes) => {
if (err) {
console.log('Error sending response to ', rinfo);
}
} while (sentDataSize < EAPMessage.length);
const response = radius.encode_response({
packet,
code: 'Access-Challenge',
secret: argv.secret,
attributes
});
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 });
},
response.expectAcknowledgment
);
}
});
// EAP MESSAGE
eapHandler.handleEAPMessage(packet.attributes['EAP-Message'], state, handlers);
} else {
const username = packet.attributes['User-Name'];
const password = packet.attributes['User-Password'];
checkAuth(username, password);
}
});
server.on('listening', function() {
const address = server.address();
console.log(`radius server listening ${address.address}:${address.port}`);
});
server.bind(argv.port);
// start server
await server.start();
})();

@ -1,6 +1,6 @@
import * as NodeCache from 'node-cache';
import { createClient, Client } from 'ldapjs';
import { Client, createClient } from 'ldapjs';
import { IAuthentication } from '../types/Authentication';
const usernameFields = ['posixUid', 'mail'];
@ -58,7 +58,7 @@ export class GoogleLDAPAuth implements IAuthentication {
});
res.on('end', result => {
console.log(`status: ${result?.status}`);
console.log(`ldap status: ${result?.status}`);
// replace with new dns
this.allValidDNsCache = dns;

@ -1,219 +0,0 @@
// https://tools.ietf.org/html/rfc3748#section-4.1
// https://tools.ietf.org/html/rfc5281 TTLS v0
// 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;
constructor() {
this.eapTTLS = new EAPTTLS(this.sendEAPResponse);
}
/**
*
* @param data
* @param type 1 = identity, 21 = EAP-TTLS, 2 = notification, 4 = md5-challenge, 3 = NAK
*/
private async sendEAPResponse(
response: ResponseHandler,
identifier: number,
data?: Buffer,
msgType = 21,
msgFlags = 0x00
) {
let i = 0;
const maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4;
let sentDataSize = 0;
let currentIdentifier = identifier;
let currentResponse = response;
do {
// SLICE
// const fragmentMaxPart =
// data && (i + 1) * maxFragmentSize > data.length ? (i + 1) * maxFragmentSize : undefined;
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;
i += 1;
/*
0 1 2 3 4 5 6 7 8
+-+-+-+-+-+-+-+-+
|L M R R R R R R|
+-+-+-+-+-+-+-+-+
L = Length included
M = More fragments
R = Reserved
The L bit (length included) is set to indicate the presence of the
four-octet TLS Message Length field, and MUST be set for the first
fragment of a fragmented TLS message or set of messages. The M
bit (more fragments) is set on all but the last fragment.
Implementations of this specification MUST set the reserved bits
to zero, and MUST ignore them on reception.
*/
const flags =
msgFlags +
(includeLength ? 0b10000000 : 0) + // set L bit
(data && sentDataSize < data.length /* we have more */ ? 0b01000000 : 0); // set M bit
currentIdentifier++;
let buffer = Buffer.from([
1, // request
currentIdentifier,
0, // length (1/2)
0, // length (2/2)
msgType, // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK
flags // flags: 000000 (L include lenghts, M .. more to come)
]);
// append length
if (includeLength && data) {
const length = Buffer.alloc(4);
length.writeInt32BE(data.byteLength, 0);
buffer = Buffer.concat([buffer, length]);
}
const resBuffer = dataPart ? Buffer.concat([buffer, dataPart]) : buffer;
resBuffer.writeUInt16BE(resBuffer.byteLength, 2);
console.log('<<<<<<<<<<<< EAP RESPONSE TO CLIENT', {
code: 1,
currentIdentifier,
includeLength,
length: (data && data.byteLength) || 0,
msgType: msgType.toString(10),
flags: `00000000${flags.toString(2)}`.substr(-8),
data
});
// uffer.from([1,identifier, 0, 0, 21, 0]);
// buffer.writeUInt16BE(sslResponse.length, 2); // length
// buffer.writeInt8(21, 4); // eap-ttls
// buffer.writeInt8(0, 5); // flags
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) {
// const b = Buffer.from([2,0x242,0x0,0x18,0x1,0x115,0x105,0x109,0x111,0x110,0x46,0x116,0x114,0x101,0x116,0x116,0x101,0x114]);
// const msg = Buffer.from([2, 162, 0, 18, 1, 115, 105, 109, 111, 110, 46, 116, 114, 101, 116, 116, 101, 114])
/*
1 Request
2 Response
3 Success
4 Failure
*/
const code = msg.slice(0, 1).readUInt8(0);
const identifier = msg.slice(1, 2).readUInt8(0); // .toString('hex');
// const length = msg.slice(2, 4).readInt16BE(0); // .toString('binary');
const type = msg.slice(4, 5).readUInt8(0); // .slice(3,0x5).toString('hex');
/*
console.log("CODE", code);
console.log('ID', identifier);
console.log('length', length);
*/
switch (code) {
case 1: // for request
case 2: // for response
switch (type) {
case 1: // identifiy
/**
* The EAP-TTLS packet format is shown below. The fields are
transmitted left to right.
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 | Flags | Message Length
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Message Length | Data...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', {});
this.sendEAPResponse(handlers.response, identifier, undefined, 21, 0x20);
/*
handlers.response(
Buffer.from([
1, // request
identifier + 1,
0, // length (1/2)
6, // length (2/2)
21, // EAP-TTLS
0x20 // flags: 001000 start flag
])
); */
break;
case 21: // EAP TTLS
this.eapTTLS.handleMessage(msg, state, handlers, identifier);
break;
case 3: // nak
// 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
console.error('not implemented type', type);
break;
default:
// 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;
case 3:
console.log('Client Auth Success');
break;
case 4:
console.log('Client Auth FAILURE');
break;
default:
break;
// silently ignor;
}
}
}

@ -1,233 +0,0 @@
/* eslint-disable no-bitwise */
import * as events from 'events';
import * as tls from 'tls';
import { encodeTunnelPW, openTLSSockets, startTLSServer } from '../tls/crypt';
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;
constructor(private sendEAPResponse) {
this.papChallenge = new PAPChallenge();
}
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 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 || data.length === 0) {
// @todo: queue processing
console.warn(
`>>>>>>>>>>>> REQUEST FROM CLIENT: EAP TTLS, ACK / NACK (no data, just a confirmation, ID: ${identifier})`
);
return;
}
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 |
+---+---+---+---+---+---+---+---+
L = Length included
M = More fragments
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: IEAPResponseHandlers }
| undefined;
if (!currentConnection) {
const connection = startTLSServer();
currentConnection = {
events: connection.events,
tls: connection.tls,
currentHandlers: handlers
};
openTLSSockets.set(state, currentConnection);
// register event listeners
currentConnection.events.on('incoming', (incomingData: Buffer) => {
const type = incomingData.slice(3, 4).readUInt8(0);
// const code = data.slice(4, 5).readUInt8(0);
switch (type) {
case 1: // PAP / CHAP
try {
const { username, password } = this.papChallenge.decode(incomingData);
currentConnection!.currentHandlers.checkAuth(username, password);
} catch (err) {
// 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`);
}
break;
default:
console.log('data', incomingData);
console.log('data str', incomingData.toString());
// 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);
// send back...
currentConnection!.currentHandlers.response(responseData);
// this.sendEAPResponse(currentConnection!.currentHandlers.response, identifier, responseData);
// this.sendMessage(TYPE.PRELOGIN, data, false);
});
currentConnection.events.on('end', () => {
// cleanup socket
console.log('ENDING SOCKET');
openTLSSockets.del(state);
});
} /* else {
console.log('using existing socket');
} */
// update handlers
currentConnection.currentHandlers = {
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([
success ? 3 : 4, // 3.. success, 4... failure
identifier,
0, // length (1/2)
4 // length (2/2)
]);
params.attributes.push(['EAP-Message', buffer]);
if (params.packet.attributes && params.packet.attributes['User-Name']) {
// reappend username to response
params.attributes.push(['User-Name', params.packet.attributes['User-Name']]);
}
/*
if (sess->eap_if->eapKeyDataLen > 64) {
len = 32;
} else {
len = sess->eap_if->eapKeyDataLen / 2;
}
*/
const keyingMaterial = (currentConnection?.tls as any).exportKeyingMaterial(
128,
'ttls keying material'
);
// console.log('keyingMaterial', keyingMaterial);
// eapKeyData + len
params.attributes.push([
'Vendor-Specific',
311,
[
[
16,
encodeTunnelPW(
keyingMaterial.slice(64),
(params.packet as any).authenticator,
// params.packet.attributes['Message-Authenticator'],
params.secret
)
]
]
]); // MS-MPPE-Send-Key
// eapKeyData
params.attributes.push([
'Vendor-Specific',
311,
[
[
17,
encodeTunnelPW(
keyingMaterial.slice(0, 64),
(params.packet as any).authenticator,
// params.packet.attributes['Message-Authenticator'],
params.secret
)
]
]
]); // MS-MPPE-Recv-Key
};
return handlers.checkAuth(username, password, additionalAuthHandler);
}
};
// emit data to tls server
currentConnection.events.emit('send', data);
}
}

@ -8,6 +8,8 @@ export function makeid(length) {
return result;
}
// by RFC Radius attributes have a max length
// https://tools.ietf.org/html/rfc6929#section-1.2
export const MAX_RADIUS_ATTRIBUTE_SIZE = 253;
export interface IDeferredPromise {

@ -0,0 +1,95 @@
import * as radius from 'radius';
import { IAuthentication } from '../types/Authentication';
import { EAPPacketHandler } from './handler/EAPPacketHandler';
import { DefaultPacketHandler } from './handler/DefaultPacketHandler';
import { IPacketHandler, IPacketHandlerResult, PacketResponseCode } from '../types/PacketHandler';
export class RadiusService {
radiusPacketHandlers: IPacketHandler[] = [];
constructor(private secret: string, private authentication: IAuthentication) {
this.radiusPacketHandlers.push(new EAPPacketHandler(authentication));
this.radiusPacketHandlers.push(new DefaultPacketHandler(authentication));
}
async handleMessage(
msg: Buffer
): Promise<{ data: Buffer; expectAcknowledgment?: boolean } | undefined> {
const packet = radius.decode({ packet: msg, secret: this.secret });
if (packet.code !== 'Access-Request') {
console.log('unknown packet type: ', packet.code);
return undefined;
}
// console.log('packet.attributes', packet.attributes);
// console.log('rinfo', rinfo);
/*
const checkAuth = async (
username: string,
password: string,
additionalAuthHandler?: AdditionalAuthHandler
) => {
console.log(`Access-Request for ${username}`);
let success = false;
try {
await this.authentication.authenticate(username, password);
success = true;
} catch (err) {
console.error(err);
}
const attributes: any[] = [];
if (additionalAuthHandler) {
await additionalAuthHandler(success, { packet, attributes, secret: this.secret });
}
const response = radius.encode_response({
packet,
code: success ? 'Access-Accept' : 'Access-Reject',
secret: this.secret,
attributes
});
console.log(`Sending ${success ? 'accept' : 'reject'} for user ${username}`);
this.server.sendToClient(response, rinfo.port, rinfo.address, function(err, _bytes) {
if (err) {
console.log('Error sending response to ', rinfo);
}
});
}; */
let response: IPacketHandlerResult;
let i = 0;
if (!this.radiusPacketHandlers[i]) {
throw new Error('no packet handlers registered');
}
// process packet handlers until we get a response
do {
/* response is of type IPacketHandlerResult */
response = await this.radiusPacketHandlers[i].handlePacket(packet.attributes, packet);
i++;
} while (this.radiusPacketHandlers[i] && (!response || !response.code));
// still no response, we are done here
if (!response || !response.code) {
return undefined;
}
// all fine, return radius encoded response
return {
data: radius.encode_response({
packet,
code: response.code,
secret: this.secret,
attributes: response.attributes
}),
// if message is accept or reject, we conside this as final message
// this means we do not expect a reponse from the client again (acknowledgement for package)
expectAcknowledgment: response.code === PacketResponseCode.AccessChallenge
};
}
}

@ -0,0 +1,36 @@
import { IAuthentication } from '../../types/Authentication';
import {
IPacketHandler,
IPacketHandlerResult,
PacketResponseCode
} from '../../types/PacketHandler';
export class DefaultPacketHandler implements IPacketHandler {
constructor(private authentication: IAuthentication) {}
async handlePacket(attributes: { [key: string]: Buffer }): Promise<IPacketHandlerResult> {
const username = attributes['User-Name'];
const password = attributes['User-Password'];
if (!username || !password) {
// params missing, this handler cannot continue...
return {};
}
const authenticated = await this.authentication.authenticate(
username.toString(),
password.toString()
);
if (authenticated) {
// success
return {
code: PacketResponseCode.AccessAccept
};
}
// Failed
return {
code: PacketResponseCode.AccessReject
};
}
}

@ -0,0 +1,193 @@
// https://tools.ietf.org/html/rfc3748#section-4.1
import * as NodeCache from 'node-cache';
import { RadiusPacket } from 'radius';
import { EAPTTLS } from './eapMethods/EAPTTLS';
import { makeid } from '../../helpers';
import {
IPacketHandler,
IPacketHandlerResult,
PacketResponseCode
} from '../../types/PacketHandler';
import { IAuthentication } from '../../types/Authentication';
import { IEAPMethod } from '../../types/EAPMethod';
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
};
}
async handlePacket(
attributes: { [key: string]: Buffer },
orgRadiusPacket: RadiusPacket
): Promise<IPacketHandlerResult> {
if (!attributes['EAP-Message']) {
// not an EAP message
return {};
}
const stateID = (attributes.State && attributes.State.toString()) || makeid(16);
if (!this.eapConnectionStates.get(stateID)) {
this.eapConnectionStates.set(stateID, {
validMethods: this.eapMethods // on init all registered eap methods are valid, we kick them out in case we get a NAK response
});
}
// EAP MESSAGE
const msg = attributes['EAP-Message'];
const { code, type, identifier, data } = this.decodeEAPHeader(msg);
const currentState = this.eapConnectionStates.get(stateID) as { validMethods: IEAPMethod[] };
switch (code) {
case 1: // for request
case 2: // for response
switch (type) {
case 1: // identifiy
console.log('>>>>>>>>>>>> REQUEST FROM CLIENT: IDENTIFY', {});
// start identify
if (currentState.validMethods.length > 0) {
return currentState.validMethods[0].identify(identifier, stateID);
}
return this.buildEAPResponse(identifier, 3);
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
console.error('not implemented type', type);
break;
case 3: // nak
if (data) {
const supportedEAPMethods: number[] = [];
for (const supportedMethod of data) {
supportedEAPMethods.push(supportedMethod);
}
this.eapConnectionStates.set(stateID, {
...currentState,
validMethods: currentState.validMethods.filter(method => {
return supportedEAPMethods.includes(method.getEAPType()); // kick it out?
})
});
}
// continue with responding a NAK and add rest of supported methods
// eslint-disable-next-line no-fallthrough
default: {
const eapMethod = currentState.validMethods.find(method => {
return type === method.getEAPType();
});
if (eapMethod) {
return eapMethod.handleMessage(identifier, stateID, msg, orgRadiusPacket);
}
// we do not support this auth type, ask for something we support
const serverSupportedMethods = currentState.validMethods.map(
method => method.getEAPType
);
console.error('unsupported type', type, `requesting: ${serverSupportedMethods}`);
return this.buildEAPResponse(identifier, 3, Buffer.from(serverSupportedMethods));
}
}
break;
case 3:
console.log('Client Auth Success');
break;
case 4:
console.log('Client Auth FAILURE');
break;
default:
}
// silently ignore;
return {};
}
}

@ -0,0 +1,460 @@
/* eslint-disable no-bitwise */
import * as tls from 'tls';
import * as NodeCache from 'node-cache';
import { RadiusPacket } from 'radius';
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';
interface IEAPResponseHandlers {
response: (respData?: Buffer, msgType?: number) => void;
checkAuth: ResponseAuthHandler;
}
/* 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 + MAX_RADIUS_ATTRIBUTE_SIZE)
]);
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
}
} while (sentDataSize < EAPMessage.length);
const response = radius.encode_response({
packet,
code: 'Access-Challenge',
secret: this.secret,
attributes
});
waitForNextMsg[state] = newDeferredPromise();
server.sendToClient(
response,
rinfo.port,
rinfo.address,
function(err, _bytes) {
if (err) {
console.log('Error sending response to ', rinfo);
}
},
state
);
return waitForNextMsg[state].promise;
},
checkAuth
};
const attributes: any = [['State', Buffer.from(stateID)]];
let sentDataSize = 0;
do {
if (EAPMessage.length > 0) {
attributes.push([
'EAP-Message',
EAPMessage.slice(sentDataSize, sentDataSize + MAX_RADIUS_ATTRIBUTE_SIZE)
]);
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
}
} while (sentDataSize < EAPMessage.length);
const response = radius.encode_response({
packet,
code: 'Access-Challenge',
secret: this.secret,
attributes
});
waitForNextMsg[stateID] = newDeferredPromise();
server.sendToClient(
response,
rinfo.port,
rinfo.address,
function(err, _bytes) {
if (err) {
console.log('Error sending response to ', rinfo);
}
},
stateID
);
return waitForNextMsg[stateID].promise;
*/
/* if (waitForNextMsg[state]) {
const identifier = attributes['EAP-Message'].slice(1, 2).readUInt8(0); // .toString('hex');
waitForNextMsg[state].resolve({ response: handlers.response, identifier });
} */
function tlsHasExportKeyingMaterial(
tlsSocket: any
): tlsSocket is {
exportKeyingMaterial: (length: number, label: string, context?: Buffer) => Buffer;
} {
return typeof (tlsSocket as any).exportKeyingMaterial === 'function';
}
export class EAPTTLS implements IEAPMethod {
private papChallenge: PAPChallenge = new PAPChallenge();
// { [key: string]: Buffer } = {};
private queueData = new NodeCache({ useClones: false, stdTTL: 60 }); // queue data maximum for 60 seconds
private openTLSSockets = new NodeCache({ useClones: false, stdTTL: 3600 }); // keep sockets for about one hour
getEAPType(): number {
return 21;
}
identify(identifier: number, stateID: string): IPacketHandlerResult {
return this.buildEAPTTLSResponse(identifier, 21, 0x20, stateID);
}
constructor(private authentication: IAuthentication) {}
private buildEAPTTLSResponse(
identifier: number,
msgType = 21,
msgFlags = 0x00,
stateID: string,
data?: Buffer,
newResponse = true
): IPacketHandlerResult {
const maxSize = (MAX_RADIUS_ATTRIBUTE_SIZE - 5) * 4;
console.log('maxSize', maxSize);
/* it's the first one and we have more, therefore include length */
const includeLength = 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);
/*
0 1 2 3 4 5 6 7 8
+-+-+-+-+-+-+-+-+
|L M R R R R R R|
+-+-+-+-+-+-+-+-+
L = Length included
M = More fragments
R = Reserved
The L bit (length included) is set to indicate the presence of the
four-octet TLS Message Length field, and MUST be set for the first
fragment of a fragmented TLS message or set of messages. The M
bit (more fragments) is set on all but the last fragment.
Implementations of this specification MUST set the reserved bits
to zero, and MUST ignore them on reception.
*/
const flags =
msgFlags +
(includeLength ? 0b10000000 : 0) + // set L bit
(dataToQueue && dataToQueue.length > 0 ? 0b01000000 : 0); // we have more data to come, set M bit
let buffer = Buffer.from([
1, // request
identifier + 1, // increase id by 1
0, // length (1/2)
0, // length (2/2)
msgType, // 1 = identity, 21 = EAP-TTLS, 2 = notificaiton, 4 = md5-challenge, 3 = NAK
flags // flags: 000000 (L include lenghts, M .. more to come)
]);
// append length
if (includeLength && data) {
const length = Buffer.alloc(4);
length.writeInt32BE(data.byteLength, 0);
buffer = Buffer.concat([buffer, length]);
}
// build final buffer with data
const resBuffer = dataToSend ? Buffer.concat([buffer, dataToSend]) : buffer;
// set EAP length header
resBuffer.writeUInt16BE(resBuffer.byteLength, 2);
console.log('<<<<<<<<<<<< EAP RESPONSE TO CLIENT', {
code: 1,
identifier: identifier + 1,
includeLength,
dataLength: (data && data.byteLength) || 0,
msgType: msgType.toString(10),
flags: `00000000${flags.toString(2)}`.substr(-8),
data
});
if (dataToQueue) {
// we couldn't send all at once, queue the rest and send later
this.queueData.set(stateID, dataToQueue);
} else {
this.queueData.del(stateID);
}
const attributes: any = [['State', Buffer.from(stateID)]];
let sentDataSize = 0;
do {
if (resBuffer.length > 0) {
attributes.push([
'EAP-Message',
resBuffer.slice(sentDataSize, sentDataSize + MAX_RADIUS_ATTRIBUTE_SIZE)
]);
sentDataSize += MAX_RADIUS_ATTRIBUTE_SIZE;
}
} while (sentDataSize < resBuffer.length);
return {
code: PacketResponseCode.AccessChallenge,
attributes
};
}
decodeTTLSMessage(msg: Buffer) {
/**
* The EAP-TTLS packet format is shown below. The fields are
transmitted left to right.
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 | Flags | Message Length
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Message Length | Data...
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
const flags = msg.slice(5, 6).readUInt8(0); // .toString('hex');
/*
0 1 2 3 4 5 6 7
+---+---+---+---+---+---+---+---+
| L | M | S | R | R | V |
+---+---+---+---+---+---+---+---+
L = Length included
M = More fragments
S = Start
R = Reserved
V = Version (000 for EAP-TTLSv0)
*/
const decodedFlags = {
// L
lengthIncluded: flags & 0b010000000,
// M
moreFragments: flags & 0b001000000,
// S
start: flags & 0b000100000,
// R
// reserved: flags & 0b000011000,
// V
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
};
}
authResponse(
identifier: number,
success: boolean,
socket: tls.TLSSocket,
packet: RadiusPacket
): IPacketHandlerResult {
const buffer = Buffer.from([
success ? 3 : 4, // 3.. success, 4... failure
identifier + 1,
0, // length (1/2)
4 // length (2/2)
]);
const attributes: any[] = [];
attributes.push(['EAP-Message', buffer]);
if (packet.attributes && packet.attributes['User-Name']) {
// reappend username to response
attributes.push(['User-Name', packet.attributes['User-Name']]);
}
/*
if (sess->eap_if->eapKeyDataLen > 64) {
len = 32;
} else {
len = sess->eap_if->eapKeyDataLen / 2;
}
*/
if (tlsHasExportKeyingMaterial(socket)) {
const keyingMaterial = (socket as any).exportKeyingMaterial(128, 'ttls keying material');
// console.log('keyingMaterial', keyingMaterial);
// eapKeyData + len
attributes.push([
'Vendor-Specific',
311,
[
[
16,
encodeTunnelPW(
keyingMaterial.slice(64),
(packet as any).authenticator,
// params.packet.attributes['Message-Authenticator'],
secret
)
]
]
]); // MS-MPPE-Send-Key
// eapKeyData
attributes.push([
'Vendor-Specific',
311,
[
[
17,
encodeTunnelPW(
keyingMaterial.slice(0, 64),
(packet as any).authenticator,
// params.packet.attributes['Message-Authenticator'],
secret
)
]
]
]); // MS-MPPE-Recv-Key
} else {
console.error(
'FATAL: no exportKeyingMaterial method available!!!, you need latest NODE JS, see https://github.com/nodejs/node/pull/31814'
);
}
return {
code: success ? PacketResponseCode.AccessAccept : PacketResponseCode.AccessReject,
attributes
};
}
async handleMessage(
identifier: number,
stateID: string,
msg: Buffer,
orgRadiusPacket: RadiusPacket
): Promise<IPacketHandlerResult> {
const { decodedFlags, msglength, 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) {
console.warn(
`>>>>>>>>>>>> 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);
}
return {};
}
console.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) {
connection = startTLSServer();
this.openTLSSockets.set(stateID, connection);
connection.events.on('end', () => {
// cleanup socket
console.log('ENDING SOCKET');
this.openTLSSockets.del(stateID);
});
}
const sendResponsePromise = newDeferredPromise();
const incomingMessageHandler = async (incomingData: Buffer) => {
const type = incomingData.slice(3, 4).readUInt8(0);
// const code = data.slice(4, 5).readUInt8(0);
switch (type) {
case 1: // PAP / CHAP
try {
const { username, password } = this.papChallenge.decode(incomingData);
const authResult = await this.authentication.authenticate(username, password);
sendResponsePromise.resolve(
this.authResponse(identifier, authResult, connection.tls, orgRadiusPacket)
);
} catch (err) {
// pwd not found..
console.error('pwd not found', err);
connection.events.emit('end');
// NAK
sendResponsePromise.resolve(this.buildEAPTTLSResponse(identifier, 3, 0, stateID));
}
break;
default:
console.log('data', incomingData);
console.log('data str', incomingData.toString());
// currentConnection!.events.emit('end');
console.log('UNSUPPORTED AUTH TYPE, requesting PAP');
// throw new Error(`unsupported auth type${type}`);
sendResponsePromise.resolve(
this.buildEAPTTLSResponse(identifier, 3, 0, stateID, Buffer.from([1]))
);
}
};
const responseHandler = (encryptedResponseData: Buffer) => {
// send back...
sendResponsePromise.resolve(
this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData)
);
};
// register event listeners
connection.events.on('incoming', incomingMessageHandler);
connection.events.on('response', responseHandler);
// emit data to tls server
connection.events.emit('send', data);
const responseData = await sendResponsePromise.promise;
// cleanup
connection.events.off('incoming', incomingMessageHandler);
connection.events.off('response', responseHandler);
// send response
return responseData; // this.buildEAPTTLSResponse(identifier, 21, 0x00, stateID, encryptedResponseData);
}
}

@ -1,4 +1,4 @@
import { IEAPChallenge } from '../../types/EAPChallenge';
import { IEAPChallenge } from '../../../../types/EAPChallenge';
export class PAPChallenge implements IEAPChallenge {
// i couldn't find any documentation about it, therefore best guess how this is processed...

@ -0,0 +1,80 @@
import * as dgram from 'dgram';
import { SocketType } from 'dgram';
import * as events from 'events';
import { EventEmitter } from 'events';
import { newDeferredPromise } from '../helpers';
import { IServer } from '../types/Server';
export class UDPServer extends events.EventEmitter implements IServer {
static MAX_RETRIES = 3;
private timeout: { [key: string]: NodeJS.Timeout } = {};
private server: dgram.Socket;
constructor(private port: number, type: SocketType = 'udp4') {
super();
this.server = dgram.createSocket(type);
}
sendToClient(
msg: string | Uint8Array,
port?: number,
address?: string,
callback?: (error: Error | null, bytes: number) => void,
expectAcknowledgment = true
): void {
let retried = 0;
const sendResponse = (): void => {
if (retried > 0) {
console.warn(
`no confirmation of last message from ${address}:${port}, re-sending response... (bytes: ${msg.length}, try: ${retried}/${UDPServer.MAX_RETRIES})`
);
}
// send message to client
this.server.send(msg, 0, msg.length, port, address, callback);
// retry up to MAX_RETRIES to send this message,
// we automatically retry if there is no confirmation (=any incoming message from client)
// if expectAcknowledgment (e.g. Access-Accept or Access-Reject) is set, we do not retry
const identifierForRetry = `${address}:${port}`;
if (expectAcknowledgment && retried < UDPServer.MAX_RETRIES) {
this.timeout[identifierForRetry] = setTimeout(sendResponse, 600 * (retried + 1));
}
retried += 1;
};
sendResponse();
}
async start(): Promise<EventEmitter> {
const startServer = newDeferredPromise();
this.server.on('listening', () => {
const address = this.server.address();
console.log(`radius server listening ${address.address}:${address.port}`);
this.setupListeners();
startServer.resolve();
});
this.server.on('message', (_msg, rinfo) => {
console.log('incoming message 2');
// message retrieved, reset timeout handler
const identifierForRetry = `${rinfo.address}:${rinfo.port}`;
if (this.timeout[identifierForRetry]) {
clearTimeout(this.timeout[identifierForRetry]);
}
});
this.server.bind(this.port);
return startServer.promise;
}
private setupListeners() {
this.server.on('message', (message, rinfo) => this.emit('message', message, rinfo));
}
}

@ -1,8 +1,6 @@
import * as NodeCache from 'node-cache';
import * as events from 'events';
import * as tls from 'tls';
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';
@ -21,16 +19,20 @@ const tlsOptions: tls.SecureContextOptions = {
};
console.log('tlsOptions', tlsOptions);
const secureContext = createSecureContext(tlsOptions);
export const openTLSSockets = new NodeCache({ useClones: false, stdTTL: 3600 }); // keep sockets for about one hour
export function startTLSServer(): { events: events.EventEmitter; tls: tls.TLSSocket } {
export interface ITLSServer {
events: events.EventEmitter;
tls: tls.TLSSocket;
}
export function startTLSServer(): ITLSServer {
const duplexpair = new DuplexPair();
const emitter = new events.EventEmitter();
const cleartext = new tls.TLSSocket(duplexpair.socket1, {
secureContext,
isServer: true,
enableTrace: true,
// enableTrace: true,
rejectUnauthorized: false,
// handshakeTimeout: 10,
requestCert: false

@ -1,3 +1,3 @@
export interface IAuthentication {
authenticate(username: string, password: string): Promise<string>;
authenticate(username: string, password: string): Promise<boolean>;
}

@ -0,0 +1,15 @@
import { RadiusPacket } from 'radius';
import { IPacketHandlerResult } from './PacketHandler';
export interface IEAPMethod {
getEAPType(): number;
identify(identifier: number, stateID: string): IPacketHandlerResult;
handleMessage(
identifier: number,
stateID: string,
msg: Buffer,
orgRadiusPacket?: RadiusPacket
): Promise<IPacketHandlerResult>;
}

@ -1,3 +0,0 @@
export interface IEAPType {
handleMessage(msg: Buffer, state: string, handlers, identifier: number);
}

@ -0,0 +1,19 @@
import { RadiusPacket } from 'radius';
export enum PacketResponseCode {
AccessChallenge = 'Access-Challenge',
AccessAccept = 'Access-Accept',
AccessReject = 'Access-Reject'
}
export interface IPacketHandlerResult {
code?: PacketResponseCode;
attributes?: [string, Buffer][];
}
export interface IPacketHandler {
handlePacket(
attributes: { [key: string]: Buffer },
orgRadiusPacket: RadiusPacket
): Promise<IPacketHandlerResult>;
}

@ -0,0 +1,32 @@
import { RemoteInfo } from 'dgram';
/**
* @fires IServer#message
*/
export interface IServer {
/**
*
* @param msg
* @param port
* @param address
* @param callback
* @param expectAcknowledgment: if set to false, message is not retried to send again if there is no confirmation
*/
sendToClient(
msg: string | Uint8Array,
port?: number,
address?: string,
callback?: (error: Error | null, bytes: number) => void,
expectAcknowledgment?: boolean
): void;
/**
* Message event.
*
* @event IServer#message
* @type {object}
* @property {message} data - the data of the incoming message
* @property {rinfo} optionally remote information
*/
on(event: 'message', listener: (msg: Buffer, rinfo?: RemoteInfo) => void): this;
}

@ -1,8 +1,10 @@
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
there is a default certificate already present, if you want to create your own, follow these steps:
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

@ -5,31 +5,31 @@ Certificate:
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AT, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.org, CN=Example Certificate Authority
Validity
Not Before: Feb 22 16:37:01 2020 GMT
Not After : Jul 27 16:37:01 2036 GMT
Not Before: Feb 22 17:05:38 2020 GMT
Not After : Jul 27 17:05:38 2036 GMT
Subject: C=AT, ST=Radius, O=Example Inc., CN=Example Certificate Authority/emailAddress=admin@example.org
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:eb:86:1d:bf:2e:b6:9a:52:72:3e:db:33:b1:dc:
b2:63:23:64:73:4f:d8:eb:32:25:f2:9c:19:a0:1b:
32:12:10:8a:d6:f3:c4:0a:10:25:be:0a:dc:14:1b:
94:b5:2b:16:df:87:09:c2:99:0b:be:18:5e:fb:0c:
0f:e0:d2:8a:6c:e5:22:0e:cd:aa:a8:57:91:e9:dc:
4c:dc:b7:11:2b:2c:c8:46:8e:a5:a2:41:90:8a:89:
1b:f1:d2:f7:cd:d9:bd:f7:12:19:4b:c9:05:b5:05:
99:76:3f:b1:6a:27:42:22:d5:fe:b9:65:aa:95:f2:
25:5b:b6:3c:f4:a8:be:59:ce:c5:35:02:39:ba:c8:
f6:bd:ce:95:60:e6:bd:1e:fd:a7:e3:8b:3f:e7:45:
e0:c9:6d:2d:78:e8:90:46:a6:88:90:04:13:84:6d:
a4:2f:b8:d3:fc:93:f8:31:9a:27:67:f4:fb:1d:55:
80:e4:20:df:8f:b8:b0:c1:42:88:9e:89:e0:29:6f:
36:2e:4e:18:ce:da:92:ad:4e:3e:eb:a3:4f:eb:0b:
20:e6:1d:89:51:cf:8f:43:06:e9:d6:5a:94:6f:6a:
07:b0:43:3e:d7:b1:a0:68:23:1a:e7:10:65:a1:75:
ea:19:bb:ed:7d:e4:9a:07:9b:10:99:0a:5d:75:b3:
15:83
00:b5:ad:78:5d:89:14:28:92:55:e7:c1:e0:15:46:
e7:ea:da:ea:98:bb:ec:33:17:ee:d6:e3:37:1c:34:
ea:4e:87:a1:c9:d3:19:de:95:6f:c1:76:13:e0:b4:
f5:08:3e:fe:5a:76:b8:4a:e4:94:07:8b:67:4b:b5:
d1:97:c1:d3:e1:b1:cd:84:5b:16:aa:dc:56:5b:ff:
0a:e7:f3:79:a3:47:4d:5b:6d:e4:d6:21:a0:eb:e1:
54:83:87:1c:c5:18:de:d8:52:40:93:7e:fb:c5:df:
73:3c:d6:0b:a3:1d:9f:36:c7:6f:59:90:d7:5f:82:
80:fe:82:05:1b:1b:77:92:04:c9:25:88:8f:9c:35:
91:cc:86:a0:8f:8f:9b:2e:2c:62:af:f3:8a:16:75:
30:61:47:8d:24:ea:35:84:43:33:01:ed:d7:33:2c:
29:4f:2c:8d:f5:b7:b3:bd:74:4f:2e:83:2d:e1:d6:
3d:5e:d9:14:c3:a1:89:38:4d:29:4b:ae:39:c8:a2:
dd:ad:f1:42:c8:0d:6c:29:eb:70:c8:dc:e7:41:59:
47:1c:89:52:62:53:9c:35:58:8a:39:16:87:61:f2:
11:26:3a:2b:a2:19:29:c2:77:31:de:1c:74:c6:57:
3a:15:8b:2f:29:61:a7:45:b4:d8:70:a4:d2:ef:da:
5d:a1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Extended Key Usage:
@ -40,41 +40,41 @@ Certificate:
URI:http://www.example.com/example_ca.crl
Signature Algorithm: sha256WithRSAEncryption
9f:4d:e9:78:d5:5f:c1:fb:e1:43:d1:9f:2d:d6:c3:f8:94:56:
0b:94:2d:b5:e7:d2:bd:f6:3b:e1:69:6b:cb:84:e4:8a:a9:fd:
1b:58:16:b5:2d:c7:00:d8:cb:56:37:72:f8:0c:17:a7:17:ec:
52:9c:17:b7:78:90:9e:8c:a9:a1:b4:72:87:96:3b:06:48:83:
27:8b:ca:8d:0c:8d:06:f9:4d:e9:bb:26:5e:76:a7:79:62:31:
d4:99:e2:e3:ad:83:3f:c0:79:5d:0f:60:33:9d:f9:05:cd:12:
25:c3:10:22:81:d4:e3:95:3f:b3:ab:21:84:e7:6b:f5:55:9f:
8d:30:a7:eb:c4:85:23:94:5d:59:08:c7:48:f9:cf:37:98:9e:
96:89:e0:8b:75:05:ce:27:76:e4:bf:e5:14:b6:7f:21:07:bc:
67:53:95:f0:c5:ac:bd:4f:9e:46:df:40:6d:8d:8a:80:c6:21:
80:ab:c4:bd:ee:77:01:16:9c:d5:7a:62:3b:d0:bd:38:6b:9d:
db:4e:0e:aa:cf:03:c8:a5:07:0a:c5:79:42:53:6a:34:74:ec:
01:e1:3b:38:fe:14:15:0a:a1:cf:3b:a7:90:76:ba:1b:34:b5:
70:77:78:0e:6d:2d:96:8b:d5:dc:1b:1e:21:a6:57:6a:97:5a:
67:49:64:81
60:49:5c:0c:1d:1a:c3:86:16:b7:d8:63:b7:b9:9b:4d:74:49:
25:05:c0:28:f4:c6:95:ce:63:95:b7:99:41:54:7f:64:5d:9a:
8f:f5:a7:96:09:8c:67:42:61:5d:c5:af:e4:d9:33:28:20:2a:
1f:76:5b:a7:1f:54:47:c4:94:4f:de:bb:6d:ea:36:51:44:bf:
90:82:b3:7c:91:28:6a:1e:dd:76:66:38:2c:0f:ed:8a:fd:b6:
91:28:3b:59:f0:b3:42:4b:bb:fb:88:d9:d8:6f:e2:a9:79:14:
df:0b:53:76:be:23:c1:0e:96:85:aa:d9:3f:d6:60:7a:a5:a9:
5a:1d:17:05:7e:84:7a:36:0b:a0:eb:96:8d:75:08:ab:ea:e2:
9b:4c:dc:92:05:b0:bc:2a:c0:ff:21:89:02:c2:cd:07:ee:85:
55:5b:cf:bd:bc:d7:b1:8a:54:51:5a:58:f6:77:e5:76:85:26:
fd:dd:e8:ec:aa:45:c8:cf:d1:10:16:68:97:e4:e7:06:e3:22:
9c:4d:f5:85:bb:37:26:d4:fc:47:af:26:03:f3:e1:17:cc:20:
33:c1:c4:e2:b3:b7:1e:d3:0a:81:85:ff:e6:79:70:07:a7:8b:
3c:1e:9e:1b:e8:f5:a1:3e:69:c5:90:f4:7f:49:a7:19:93:fa:
65:fc:0e:43
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCQVQx
DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUub3JnMSYw
JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMDAyMjIx
NjM3MDFaFw0zNjA3MjcxNjM3MDFaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS
NzA1MzhaFw0zNjA3MjcxNzA1MzhaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS
YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEmMCQGA1UEAwwdRXhhbXBsZSBD
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA64Ydvy62mlJy
PtszsdyyYyNkc0/Y6zIl8pwZoBsyEhCK1vPEChAlvgrcFBuUtSsW34cJwpkLvhhe
+wwP4NKKbOUiDs2qqFeR6dxM3LcRKyzIRo6lokGQiokb8dL3zdm99xIZS8kFtQWZ
dj+xaidCItX+uWWqlfIlW7Y89Ki+Wc7FNQI5usj2vc6VYOa9Hv2n44s/50XgyW0t
eOiQRqaIkAQThG2kL7jT/JP4MZonZ/T7HVWA5CDfj7iwwUKInongKW82Lk4YztqS
rU4+66NP6wsg5h2JUc+PQwbp1lqUb2oHsEM+17GgaCMa5xBloXXqGbvtfeSaB5sQ
mQpddbMVgwIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAta14XYkUKJJV
58HgFUbn6trqmLvsMxfu1uM3HDTqToehydMZ3pVvwXYT4LT1CD7+Wna4SuSUB4tn
S7XRl8HT4bHNhFsWqtxWW/8K5/N5o0dNW23k1iGg6+FUg4ccxRje2FJAk377xd9z
PNYLox2fNsdvWZDXX4KA/oIFGxt3kgTJJYiPnDWRzIagj4+bLixir/OKFnUwYUeN
JOo1hEMzAe3XMywpTyyN9bezvXRPLoMt4dY9XtkUw6GJOE0pS645yKLdrfFCyA1s
KetwyNznQVlHHIlSYlOcNViKORaHYfIRJjorohkpwncx3hx0xlc6FYsvKWGnRbTY
cKTS79pdoQIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt
MCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0G
CSqGSIb3DQEBCwUAA4IBAQCfTel41V/B++FD0Z8t1sP4lFYLlC2159K99jvhaWvL
hOSKqf0bWBa1LccA2MtWN3L4DBenF+xSnBe3eJCejKmhtHKHljsGSIMni8qNDI0G
+U3puyZedqd5YjHUmeLjrYM/wHldD2AznfkFzRIlwxAigdTjlT+zqyGE52v1VZ+N
MKfrxIUjlF1ZCMdI+c83mJ6WieCLdQXOJ3bkv+UUtn8hB7xnU5Xwxay9T55G30Bt
jYqAxiGAq8S97ncBFpzVemI70L04a53bTg6qzwPIpQcKxXlCU2o0dOwB4Ts4/hQV
CqHPO6eQdrobNLVwd3gObS2Wi9XcGx4hpldql1pnSWSB
CSqGSIb3DQEBCwUAA4IBAQBgSVwMHRrDhha32GO3uZtNdEklBcAo9MaVzmOVt5lB
VH9kXZqP9aeWCYxnQmFdxa/k2TMoICofdlunH1RHxJRP3rtt6jZRRL+QgrN8kShq
Ht12ZjgsD+2K/baRKDtZ8LNCS7v7iNnYb+KpeRTfC1N2viPBDpaFqtk/1mB6pala
HRcFfoR6Ngug65aNdQir6uKbTNySBbC8KsD/IYkCws0H7oVVW8+9vNexilRRWlj2
d+V2hSb93ejsqkXIz9EQFmiX5OcG4yKcTfWFuzcm1PxHryYD8+EXzCAzwcTis7ce
0wqBhf/meXAHp4s8Hp4b6PWhPmnFkPR/SacZk/pl/A5D
-----END CERTIFICATE-----

@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQI1Fm+/6EHcV8CAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECP14mHLiQP0EBIIEyFFD//WLNJgR
nADmlyQgvEtEH+Rbbd6OtUXyXVLk9OtDG3s/JTh8SeeieJbm3PaLanAcyV/DgB2Q
lseLe3WLLQSQ2lOjlU2e6KsRk+NDFaiJ5BZqISHfmrOJnZPg27rXN5EeCReRWwAr
Myyb9Ijbw9j1LL4EwoEKmJ59XEC5Cy4bIyEDROF7ONJvwzaRyPPT5ZsB5qxozjp7
C4YOr55AUhidEHBxmFdU7Sx1uJhxYFRccGV7m4C+00z2TKx81IWFtbj2404mBMQM
WGf7Ncfuw1SsRwCxv89Z6+eP38Un3egT+inTFeP4E3iMWdZiK9JpYRO6qZzYdw9o
4QyAl+oJIFjPImA+vX29J6C0BFvFAnZwmLe30nK9HMja/qynNjeF//sC3LpuC4Dx
f07Od5X6Bq7OMmMgBNPr1Y+MmwDomrthlJhUIovowHIKQnim/Awmwv613H0kI9Dp
bp04LMcY63cZL/ShyAG3hNqnDs6ULKv/35IA2HOPyyOzhrJl5OJbCxaoW4rp96nK
JTFAxI2oQZtNKX4tmxP0QZ5kmd3jmBtjmuqyIGQO3PRA465bxCO6Rajo1kOhygab
9L3dp3FRIRkWJzSxIteEmkv6RgT4CvS9d9dwk1t+GKqH7Qcg++2+/Yy7rWopxsRY
hjAygy/kLE3HXZUgHWuJhomSTNfkjj6sHEFUiIB8RJ9DWoRkfkloGUml5/4QoGWn
qo4qOs5vXt1iRnc65cXgT2h/IX/ohcPcZirI0gf9mKN470U5zzlUpDmCMVYgv+gh
Te0uhghT4GIk6fZWTUKTiLmqvG6hEdVeHAfboJ6aPzrQYT2XnonRf9hQfq2SmznC
J7MNHaLqeR0pULxxWY+UrS3oPhQ4ICDMDEGQxwVNpqLjzQJnZjI7ZYQKS324FDaH
PPrdG/a9bc3lVeBdcCjvH6Tz+rHUYW4x7WGUeYprz7rAl8XtYtl+zT3bngLxO3D5
mc6R+aGE1tcBY0EL1K2K0UzPqNLSUTrgnw4K+L2sgQJqKrpdyGcAZYnW0eH6ULIl
VgKL1Ef0lX7cD6sdxASYHgqyYfc4+vAo6drwKXwb98jQFe5q73d5f1SqOiC/APuR
ZIMRVMBydD3Mp0L7lokDpS6eraTZ+XeGwtbEzb2mC4urYoT9yEH4CdOwUe0GrKQE
B/6mV0MEyrzlWxFpxZFkByLFf1FBlqKHpd09Sj9TdNiZom+mc90D76FiyZ83H+Pp
D9tzelr8G5A5RVEPOaHOVL1LiDQA12B5tFhgFldUI4BXJu+LnR/4qNyNt2xedrHO
GY/FoJjlnTMf5/S3erH+1VeIwjrnwrl4w3o2zymE19Yb2zEt9fyP67R0q/trZOs+
umxTOxyFvJCzZoLkIFz9oGfo+IQYqqxfPC0gYYlkF1jWX1MarLwZoiXSHEkBlfez
t61ZqYxNMWt8cxwfb5MfRpeXaY4wH1o6+GW8DgNMeq5LxgLASkvNdG0dzXTK8DPG
sGx09dFGEDKYmljT+1chL+KjkxWIqgrq05NwpE/7OF3ZuiG2hv6ys5STY3URnINg
lz7zqzgt1gMeCV8kikXKWXwKqxH0L14AYMDhfPaXRM23nsjC3c/h4CfySUutlpxp
M2oE3tWQQ6TOv3yAqhRj+g==
-----END ENCRYPTED PRIVATE KEY-----

@ -1,29 +1,29 @@
-----BEGIN CERTIFICATE-----
MIIE+jCCA+KgAwIBAgIUVIBUu2vZ0TrxOwqlIyfxooaF4bIwDQYJKoZIhvcNAQEL
MIIE+jCCA+KgAwIBAgIUA7R4NLhoa6Mt/Zek66V7+Qnx9VEwDQYJKoZIhvcNAQEL
BQAwgZMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZSYWRpdXMxEjAQBgNVBAcMCVNv
bWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZIhvcNAQkBFhFh
ZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0aWZpY2F0ZSBB
dXRob3JpdHkwHhcNMjAwMjIyMTYzNzAxWhcNMjkxMjMxMTYzNzAxWjCBkzELMAkG
dXRob3JpdHkwHhcNMjAwMjIyMTcwNTM4WhcNMjkxMjMxMTcwNTM4WjCBkzELMAkG
A1UEBhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUw
EwYDVQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
bGUub3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTCC
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMg5QnJc+a8K0YGIcnZ0OfFZ
ZNQUMIBJIxdE3LnIa1Y6JNA89oVodtD/J4bFOHa8ehji7R6JzTCpDi6Sc61D9mT2
Fa1sm8wSrD4NU+tMlaK5U73C6nYrAtnTpFnDeQQEzgfyzoN988QXAknY++Ls0CU/
B2Pjd5D4Sk1VOOf88/XYyU5OM5jT5/skOuBULdGhbL9TME3PRieMLOokTFZjNnDy
uCKV3p5PD8MYAVh6qNEdHHnQMp559JfdzAh2FZPvdr88MbhO78pFCymdnOkhIr6M
ZvBqDLxaV7mjcduU/U1lq13CekZ2DUTDpbUa9z4w+BiqgFpWJdxy0ZTwpwPBqd8C
AwEAAaOCAUIwggE+MB0GA1UdDgQWBBR0nqW1x1vNwtdFezphgGtkz7B2RjCB0wYD
VR0jBIHLMIHIgBR0nqW1x1vNwtdFezphgGtkz7B2RqGBmaSBljCBkzELMAkGA1UE
ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMrt1oTLVGNfvZvmsQd7L0Ts
dttTKZcJynIlDX/2QJ7F77/sywrY69Bch14Gn2MhEhpRnn5GIKpEzE8KTDgg5iOV
FOwClfMwlhzksx7VpwSjSFhxbudxWgpk8RoGmuPaB4rJLHPo7B6Pv4DduxFpO5aF
IxKdPFCi1sKKzJAi+88SSX5KhX+MQgwiqbLjdeXxx+ofTZig7IymiJUUbcdtiQPq
KYXjcFH/XLhJMx52GKBk9YK5yvoDwYzGs+FKkPgWCbifUHnzkQ+/v3yKsvnFS/vq
Rb1uPA3PkJnbZwCLb0hGKh8YvGUIr+9WT4MLQPOBKgfRUHEjNxvdYCUaw6xz+8EC
AwEAAaOCAUIwggE+MB0GA1UdDgQWBBQi2Mb/nVfuJREdj/wvqVAtZNxhXzCB0wYD
VR0jBIHLMIHIgBQi2Mb/nVfuJREdj/wvqVAtZNxhX6GBmaSBljCBkzELMAkGA1UE
BhMCQVQxDzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYD
VQQKDAxFeGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUu
b3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eYIUVIBU
u2vZ0TrxOwqlIyfxooaF4bIwDwYDVR0TAQH/BAUwAwEB/zA2BgNVHR8ELzAtMCug
b3JnMSYwJAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eYIUA7R4
NLhoa6Mt/Zek66V7+Qnx9VEwDwYDVR0TAQH/BAUwAwEB/zA2BgNVHR8ELzAtMCug
KaAnhiVodHRwOi8vd3d3LmV4YW1wbGUub3JnL2V4YW1wbGVfY2EuY3JsMA0GCSqG
SIb3DQEBCwUAA4IBAQAdywbOcOa9XSx/hEOKkG4rZR8F6eb+z/u+BYfEhQE4unTR
3ihNarsHwteTQArabTRzuV8+phX7fgkQJHmp1NOpJVmMEr3JOs00SGbisxDmK0Z3
2HkE+DjpYo2Sz8b77YD1AWk705rJkJ0Jp5+d/BLk6CjCr524XsSKLwKRWKOit3eu
WKsbt+VMd7d8jDvgwrtKLDpGv/sBym5w9zQjUqOTPBr70BBbpynJD62mzdr5OjIc
JiZKjusr1fieOeBzo2us+hFdvoCmW8X4hwVAyWm4JgP1yZ9IT/KLnFtJo0k/BM3B
lzcz90I0zzpewC8xxaWJ7kz6h+eSIa3orlSIug4o
SIb3DQEBCwUAA4IBAQCOSSpCkK1kF4MlctIhNnMjP6AcxhPYFhDPrTCy+lGmNw0S
4L73ZHoTtmPsEc7p4d3qxy1WddRS3M6+0vyQz5Yhe1xQZy+SExeYxOX8sXqK1Uu3
37rY7oz26DkITOdI4FFtcNKtAcu8rVg2kZO/66RUiQSA6V3rzAVcFGrHHP4ZoPqV
qy+OqO8dcldlf70B7fLnf2N28drdpkUeJEGhGRzO42gN55Zj9RVH3NCZDZI1uz2B
ESIwjFWBFDeteux8O9BZhfNJ+Zmc9BS+1CvjMCCEAeRCjpAWdyLa+uquq5tII4Me
STKrTWjHUmq0zjDmjOowB8xWvoWONW19nsSSqGC8
-----END CERTIFICATE-----

@ -0,0 +1,80 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=AT, ST=Radius, L=Somewhere, O=Example Inc./emailAddress=admin@example.org, CN=Example Certificate Authority
Validity
Not Before: Feb 22 17:05:38 2020 GMT
Not After : Jul 27 17:05:38 2036 GMT
Subject: C=AT, ST=Radius, O=Example Inc., CN=Example Certificate Authority/emailAddress=admin@example.org
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
RSA Public-Key: (2048 bit)
Modulus:
00:b5:ad:78:5d:89:14:28:92:55:e7:c1:e0:15:46:
e7:ea:da:ea:98:bb:ec:33:17:ee:d6:e3:37:1c:34:
ea:4e:87:a1:c9:d3:19:de:95:6f:c1:76:13:e0:b4:
f5:08:3e:fe:5a:76:b8:4a:e4:94:07:8b:67:4b:b5:
d1:97:c1:d3:e1:b1:cd:84:5b:16:aa:dc:56:5b:ff:
0a:e7:f3:79:a3:47:4d:5b:6d:e4:d6:21:a0:eb:e1:
54:83:87:1c:c5:18:de:d8:52:40:93:7e:fb:c5:df:
73:3c:d6:0b:a3:1d:9f:36:c7:6f:59:90:d7:5f:82:
80:fe:82:05:1b:1b:77:92:04:c9:25:88:8f:9c:35:
91:cc:86:a0:8f:8f:9b:2e:2c:62:af:f3:8a:16:75:
30:61:47:8d:24:ea:35:84:43:33:01:ed:d7:33:2c:
29:4f:2c:8d:f5:b7:b3:bd:74:4f:2e:83:2d:e1:d6:
3d:5e:d9:14:c3:a1:89:38:4d:29:4b:ae:39:c8:a2:
dd:ad:f1:42:c8:0d:6c:29:eb:70:c8:dc:e7:41:59:
47:1c:89:52:62:53:9c:35:58:8a:39:16:87:61:f2:
11:26:3a:2b:a2:19:29:c2:77:31:de:1c:74:c6:57:
3a:15:8b:2f:29:61:a7:45:b4:d8:70:a4:d2:ef:da:
5d:a1
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Extended Key Usage:
TLS Web Server Authentication
X509v3 CRL Distribution Points:
Full Name:
URI:http://www.example.com/example_ca.crl
Signature Algorithm: sha256WithRSAEncryption
60:49:5c:0c:1d:1a:c3:86:16:b7:d8:63:b7:b9:9b:4d:74:49:
25:05:c0:28:f4:c6:95:ce:63:95:b7:99:41:54:7f:64:5d:9a:
8f:f5:a7:96:09:8c:67:42:61:5d:c5:af:e4:d9:33:28:20:2a:
1f:76:5b:a7:1f:54:47:c4:94:4f:de:bb:6d:ea:36:51:44:bf:
90:82:b3:7c:91:28:6a:1e:dd:76:66:38:2c:0f:ed:8a:fd:b6:
91:28:3b:59:f0:b3:42:4b:bb:fb:88:d9:d8:6f:e2:a9:79:14:
df:0b:53:76:be:23:c1:0e:96:85:aa:d9:3f:d6:60:7a:a5:a9:
5a:1d:17:05:7e:84:7a:36:0b:a0:eb:96:8d:75:08:ab:ea:e2:
9b:4c:dc:92:05:b0:bc:2a:c0:ff:21:89:02:c2:cd:07:ee:85:
55:5b:cf:bd:bc:d7:b1:8a:54:51:5a:58:f6:77:e5:76:85:26:
fd:dd:e8:ec:aa:45:c8:cf:d1:10:16:68:97:e4:e7:06:e3:22:
9c:4d:f5:85:bb:37:26:d4:fc:47:af:26:03:f3:e1:17:cc:20:
33:c1:c4:e2:b3:b7:1e:d3:0a:81:85:ff:e6:79:70:07:a7:8b:
3c:1e:9e:1b:e8:f5:a1:3e:69:c5:90:f4:7f:49:a7:19:93:fa:
65:fc:0e:43
-----BEGIN CERTIFICATE-----
MIID3TCCAsWgAwIBAgIBADANBgkqhkiG9w0BAQsFADCBkzELMAkGA1UEBhMCQVQx
DzANBgNVBAgMBlJhZGl1czESMBAGA1UEBwwJU29tZXdoZXJlMRUwEwYDVQQKDAxF
eGFtcGxlIEluYy4xIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1wbGUub3JnMSYw
JAYDVQQDDB1FeGFtcGxlIENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0yMDAyMjIx
NzA1MzhaFw0zNjA3MjcxNzA1MzhaMH8xCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZS
YWRpdXMxFTATBgNVBAoMDEV4YW1wbGUgSW5jLjEmMCQGA1UEAwwdRXhhbXBsZSBD
ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxIDAeBgkqhkiG9w0BCQEWEWFkbWluQGV4YW1w
bGUub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAta14XYkUKJJV
58HgFUbn6trqmLvsMxfu1uM3HDTqToehydMZ3pVvwXYT4LT1CD7+Wna4SuSUB4tn
S7XRl8HT4bHNhFsWqtxWW/8K5/N5o0dNW23k1iGg6+FUg4ccxRje2FJAk377xd9z
PNYLox2fNsdvWZDXX4KA/oIFGxt3kgTJJYiPnDWRzIagj4+bLixir/OKFnUwYUeN
JOo1hEMzAe3XMywpTyyN9bezvXRPLoMt4dY9XtkUw6GJOE0pS645yKLdrfFCyA1s
KetwyNznQVlHHIlSYlOcNViKORaHYfIRJjorohkpwncx3hx0xlc6FYsvKWGnRbTY
cKTS79pdoQIDAQABo08wTTATBgNVHSUEDDAKBggrBgEFBQcDATA2BgNVHR8ELzAt
MCugKaAnhiVodHRwOi8vd3d3LmV4YW1wbGUuY29tL2V4YW1wbGVfY2EuY3JsMA0G
CSqGSIb3DQEBCwUAA4IBAQBgSVwMHRrDhha32GO3uZtNdEklBcAo9MaVzmOVt5lB
VH9kXZqP9aeWCYxnQmFdxa/k2TMoICofdlunH1RHxJRP3rtt6jZRRL+QgrN8kShq
Ht12ZjgsD+2K/baRKDtZ8LNCS7v7iNnYb+KpeRTfC1N2viPBDpaFqtk/1mB6pala
HRcFfoR6Ngug65aNdQir6uKbTNySBbC8KsD/IYkCws0H7oVVW8+9vNexilRRWlj2
d+V2hSb93ejsqkXIz9EQFmiX5OcG4yKcTfWFuzcm1PxHryYD8+EXzCAzwcTis7ce
0wqBhf/meXAHp4s8Hp4b6PWhPmnFkPR/SacZk/pl/A5D
-----END CERTIFICATE-----

@ -3,16 +3,16 @@ MIIC2TCCAcECAQAwgZMxCzAJBgNVBAYTAkFUMQ8wDQYDVQQIDAZSYWRpdXMxEjAQ
BgNVBAcMCVNvbWV3aGVyZTEVMBMGA1UECgwMRXhhbXBsZSBJbmMuMSAwHgYJKoZI
hvcNAQkBFhFhZG1pbkBleGFtcGxlLm9yZzEmMCQGA1UEAwwdRXhhbXBsZSBDZXJ0
aWZpY2F0ZSBBdXRob3JpdHkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQDrhh2/LraaUnI+2zOx3LJjI2RzT9jrMiXynBmgGzISEIrW88QKECW+CtwUG5S1
KxbfhwnCmQu+GF77DA/g0ops5SIOzaqoV5Hp3EzctxErLMhGjqWiQZCKiRvx0vfN
2b33EhlLyQW1BZl2P7FqJ0Ii1f65ZaqV8iVbtjz0qL5ZzsU1Ajm6yPa9zpVg5r0e
/afjiz/nReDJbS146JBGpoiQBBOEbaQvuNP8k/gxmidn9PsdVYDkIN+PuLDBQoie
ieApbzYuThjO2pKtTj7ro0/rCyDmHYlRz49DBunWWpRvagewQz7XsaBoIxrnEGWh
deoZu+195JoHmxCZCl11sxWDAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAKYUI
sRw13r+FXj0cR7rvz2JQVBcJSphHrBK5O8xkO4zlOLVuxbUIA3/6YunmmnFMChTb
O/Be6HDHX0+SmqfrT0VUGZ2zF1QeDFtkK195r3gMCQ1qqUEBCdAWZG4u1XDEnVvV
fctXt3D/D8cYJ0Dw+xHMBq5RpxO6/dv+ML1QicRJ8h4uvtjcf3ePPF6cyrtLeCxM
Vw6RWUB0XbdSqA6ljFcT0ytI1f4hnu7pwp4Rhc6xAtZyGWn7Vgbm9kxlThzenT+r
fePBF77E2CY16kRzzKQOiyEqumkotEimvGsUsGx90TtPTMNX8GiXm9Ct1XRBL4SI
R+SlHRAayKRGPyhvOA==
AQC1rXhdiRQoklXnweAVRufq2uqYu+wzF+7W4zccNOpOh6HJ0xnelW/BdhPgtPUI
Pv5adrhK5JQHi2dLtdGXwdPhsc2EWxaq3FZb/wrn83mjR01bbeTWIaDr4VSDhxzF
GN7YUkCTfvvF33M81gujHZ82x29ZkNdfgoD+ggUbG3eSBMkliI+cNZHMhqCPj5su
LGKv84oWdTBhR40k6jWEQzMB7dczLClPLI31t7O9dE8ugy3h1j1e2RTDoYk4TSlL
rjnIot2t8ULIDWwp63DI3OdBWUcciVJiU5w1WIo5Fodh8hEmOiuiGSnCdzHeHHTG
VzoViy8pYadFtNhwpNLv2l2hAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAtTan
LS+AjvzL0raUHp/SWflMh37l2J60cjzjmbrws7MpQdqGg7H11tXhgKSDELMpuB4h
44PZeDZmZGfFJCtYWcOeTc4SvI69SXYnj6UaMKxM3/1sI0fd9JaPnCs7Sg5+B96A
/8AcF1hKXZ9y71XD3NeGGRDuzqHvGdeIfMoGP8GJLaR70ncLnBpg2KCHG6C6mP6R
4W5yrWRCcpVXBAXi4yoxesQRLwp/DEXZo6ExOJHDYpZLen6YjkTp/Lv7PySKopQP
SJrev/H6GL4qmLfl7dFebq+FE6sezIRzopFWnHu+TcUMVCWBUwJVl71Nvw009A1/
kfBQ/TsFvyRDzqqM5Q==
-----END CERTIFICATE REQUEST-----

@ -0,0 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIfUSPX6P3YcECAggA
MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECGX0Au3iFvSjBIIEyGye/tQdIfVv
HG9CBzyejddQTYgT5IsNzTFKsASeJi/0vlQP1mvEPScgMrNO62mBPHh0+sQGyR4v
ccrqEobKUgXYJWJd60MeHJVPvB6fVBS2kBv1dmHYYuGNqlsWE82nmX8mdEOjNR9e
ZTnuULqE8QHZd0EVWhdK83iUCCb4ksOF+bJYQe7gkRWeZ3ALqq4eLtlUs4LiIaAn
VrqFWdslZLBRHfDfC/beOtxp9a/clMaTI1g0jjAKuYKkydDKl6gk6HWkyw4qnJHX
37GgReCYE72DZSmuS6mTXjsvK2cLsRmvrwWnwBaiApE9P6+RLnYyQerDMpVc1z44
s+9VCoOmggPgy+YNB09tptCJaWJaiLu6FQzAZNo/Hu1hIf7Gx4b9IXnT6vsAsTkg
K4exni026uVXd/toeh1O+bH9lE85RVxkFgnv49WpcBvfVXa93BZbAZP286dEeKI4
8EueW8fh+l1HYht+6lKN0hW5rh3nBkOFHdG0GoczmbyAsz2N/cvW20KWc75cAjdW
HG3pAwoCxzjCUVk7s6+tueNAmANRtn1zo8RKHzQ4iUVWd6WmE9v5GrSrNmvDtqyN
yLbzDd3BZuYyUwWxMjgIeYcyqlR0zSlj12SvhPnNqwwPsuqUuB7Y5lv3oTKl7RNk
GGd08a5nioYMs+0Ob/Zr4ay3so5gAmYDfIVVVp7GzERK9h8FrBylZqyaA8BS95GM
JfWJGXjKbJ2nqNqc/Ic2mYVp6AT7V4FSmYV9I+rNuRI76DdJgA3UYhQOvtvmbbFI
EyNtHCL6M8ejfbg71c5rbj7nBYLiuk5STzIiZyKPK4QmNWQ+QLy3RIgK1fMgjlzP
rgh/1hhFRUVJxiRKnDbzrTlHEeD17U4Rpz/M3M0ZsWCWWJe+FMDaJNuNfvJW3ZYQ
hBFjVhZn3VTmreFyt6nshsfGU3PeFXSXx8el4M5Sja5MCqQ86nbv9W6miOVtvkOx
7kYvHXMQpsSzkjkL9hej3PxYHcchgjRnF+l0WIs7U/RE3pRiPkm0yD9Y1IEsZIix
farOZTtPZqrOYcHwCHdRY9oxcKLZM9Rrqi5tTMk3T1ellFuX8qUj2uqSNCbhAYpC
Qx4VfKyAi9iEyBP01X0r8gprMzqtpZatGAB70XC+Wa4lUYfRt1IbHQ0MhWMWyStL
iefHkHee9MVgI3hh08ww1OmQGTnpQgKG6udxSRObLbWlAgccWjnX5YE3nOmo4Vvo
v091utdEqUZ9nrIzIvlv1lqnm0qRTY4uf99dOyl/bIr1tfWJLawL9kUbN+jl4d4C
CnilsBFTuWQBMPFE3sit91U4Ycmi3U1lmiQTXLhlCiOkyuVLGjnb7RuY1Yjm9Gso
D/t55VgF0882uVgocU3l9OnXRZLRijXEU7WWM8YF+RXTRORykcYfL/oPF8/E/NF7
DbamK79gIJUXGEdDdujFGAkJr0nYdK77sMeWfqIgf7l+RNaz3dtv2NGfWjpymbMz
RkgPUypbE4JiJVnwrsVNm20wQbE839Raijchf8ZWBEWz8nNArwe094+MQxoZHg12
Iu742/ykANJD/xXq7Nfe0d8fXWEdG3+ZgcfKUIL2oCS7/y3p4URCl6TuujBAFO8I
IBR4bJcgdHWSqRS8usbzVg==
-----END ENCRYPTED PRIVATE KEY-----

@ -1 +1 @@
V 360727163701Z 00 unknown /C=AT/ST=Radius/O=Example Inc./CN=Example Certificate Authority/emailAddress=admin@example.org
V 360727170538Z 00 unknown /C=AT/ST=Radius/O=Example Inc./CN=Example Certificate Authority/emailAddress=admin@example.org

@ -3,7 +3,7 @@
"outDir": "./dist",
"rootDir": "./src",
// target settings for node 10
// target settings for node js
"module": "commonjs",
"target": "es2018",
"lib": ["es2018"],

Loading…
Cancel
Save