From 42a05b069fca8f874c489a158270882fad3b1bb6 Mon Sep 17 00:00:00 2001 From: simon Date: Sun, 23 Feb 2020 00:29:33 +0100 Subject: [PATCH] refactor: major code cleanups :-) first working version --- .gitignore | 8 +- README.md | 57 ++- config.js | 11 +- package-lock.json | 228 +++++++-- package.json | 59 +-- src/app.ts | 210 ++------ src/auth/google-ldap.ts | 4 +- src/eap.ts | 219 --------- src/eap/eap-ttls.ts | 233 --------- src/helpers.ts | 2 + src/radius/RadiusService.ts | 95 ++++ src/radius/handler/DefaultPacketHandler.ts | 36 ++ src/radius/handler/EAPPacketHandler.ts | 193 ++++++++ src/radius/handler/eapMethods/EAPTTLS.ts | 460 ++++++++++++++++++ .../eapMethods/challenges/PAPChallenge.ts} | 2 +- src/server/UDPServer.ts | 80 +++ src/tls/crypt.ts | 12 +- src/types/Authentication.ts | 2 +- src/types/EAPMethod.ts | 15 + src/types/EAPType.ts | 3 - src/types/PacketHandler.ts | 19 + src/types/Server.ts | 32 ++ ssl/README.md | 12 +- ssl/cert/00.pem | 98 ++-- ssl/cert/ca.key | 30 ++ ssl/cert/ca.pem | 36 +- ssl/cert/server.crt | 80 +++ ssl/cert/server.csr | 24 +- ssl/cert/server.key | 30 ++ ssl/db/index.txt | 2 +- tsconfig.json | 2 +- 31 files changed, 1466 insertions(+), 828 deletions(-) delete mode 100644 src/eap.ts delete mode 100644 src/eap/eap-ttls.ts create mode 100644 src/radius/RadiusService.ts create mode 100644 src/radius/handler/DefaultPacketHandler.ts create mode 100644 src/radius/handler/EAPPacketHandler.ts create mode 100644 src/radius/handler/eapMethods/EAPTTLS.ts rename src/{eap/challenges/pap.ts => radius/handler/eapMethods/challenges/PAPChallenge.ts} (95%) create mode 100644 src/server/UDPServer.ts create mode 100644 src/types/EAPMethod.ts delete mode 100644 src/types/EAPType.ts create mode 100644 src/types/PacketHandler.ts create mode 100644 src/types/Server.ts create mode 100644 ssl/cert/ca.key create mode 100644 ssl/cert/server.crt create mode 100644 ssl/cert/server.key diff --git a/.gitignore b/.gitignore index 6826c54..819fd6c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,10 +4,8 @@ node_modules/* # build files dist -# certificates -ssl/*.pem -*.crt -*.key - # ts tsconfig.tsbuildinfo + +# custom certificates +/ssl-*/ diff --git a/README.md b/README.md index f207b70..acd122a 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/config.js b/config.js index c61d0ad..c2cb426 100644 --- a/config.js +++ b/config.js @@ -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' } ] diff --git a/package-lock.json b/package-lock.json index 2b646a0..58bf99c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 670e51b..0b0b195 100644 --- a/package.json +++ b/package.json @@ -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" + } } diff --git a/src/app.ts b/src/app.ts index d0408d5..af62654 100644 --- a/src/app.ts +++ b/src/app.ts @@ -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(); +})(); diff --git a/src/auth/google-ldap.ts b/src/auth/google-ldap.ts index ce44b15..1c9deef 100644 --- a/src/auth/google-ldap.ts +++ b/src/auth/google-ldap.ts @@ -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; diff --git a/src/eap.ts b/src/eap.ts deleted file mode 100644 index 9275092..0000000 --- a/src/eap.ts +++ /dev/null @@ -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; - } - } -} diff --git a/src/eap/eap-ttls.ts b/src/eap/eap-ttls.ts deleted file mode 100644 index 9d954cd..0000000 --- a/src/eap/eap-ttls.ts +++ /dev/null @@ -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); - } -} diff --git a/src/helpers.ts b/src/helpers.ts index 208d640..29b5731 100644 --- a/src/helpers.ts +++ b/src/helpers.ts @@ -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 { diff --git a/src/radius/RadiusService.ts b/src/radius/RadiusService.ts new file mode 100644 index 0000000..30dbe13 --- /dev/null +++ b/src/radius/RadiusService.ts @@ -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 + }; + } +} diff --git a/src/radius/handler/DefaultPacketHandler.ts b/src/radius/handler/DefaultPacketHandler.ts new file mode 100644 index 0000000..6f87643 --- /dev/null +++ b/src/radius/handler/DefaultPacketHandler.ts @@ -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 { + 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 + }; + } +} diff --git a/src/radius/handler/EAPPacketHandler.ts b/src/radius/handler/EAPPacketHandler.ts new file mode 100644 index 0000000..e4b458d --- /dev/null +++ b/src/radius/handler/EAPPacketHandler.ts @@ -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 { + /** 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 { + 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 {}; + } +} diff --git a/src/radius/handler/eapMethods/EAPTTLS.ts b/src/radius/handler/eapMethods/EAPTTLS.ts new file mode 100644 index 0000000..9fb7c09 --- /dev/null +++ b/src/radius/handler/eapMethods/EAPTTLS.ts @@ -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 { + 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); + } +} diff --git a/src/eap/challenges/pap.ts b/src/radius/handler/eapMethods/challenges/PAPChallenge.ts similarity index 95% rename from src/eap/challenges/pap.ts rename to src/radius/handler/eapMethods/challenges/PAPChallenge.ts index a756fbf..3b4b893 100644 --- a/src/eap/challenges/pap.ts +++ b/src/radius/handler/eapMethods/challenges/PAPChallenge.ts @@ -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... diff --git a/src/server/UDPServer.ts b/src/server/UDPServer.ts new file mode 100644 index 0000000..d7311f5 --- /dev/null +++ b/src/server/UDPServer.ts @@ -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 { + 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)); + } +} diff --git a/src/tls/crypt.ts b/src/tls/crypt.ts index 608c4b8..f994953 100644 --- a/src/tls/crypt.ts +++ b/src/tls/crypt.ts @@ -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 diff --git a/src/types/Authentication.ts b/src/types/Authentication.ts index f8ce7ef..3f40f2d 100644 --- a/src/types/Authentication.ts +++ b/src/types/Authentication.ts @@ -1,3 +1,3 @@ export interface IAuthentication { - authenticate(username: string, password: string): Promise; + authenticate(username: string, password: string): Promise; } diff --git a/src/types/EAPMethod.ts b/src/types/EAPMethod.ts new file mode 100644 index 0000000..97113f5 --- /dev/null +++ b/src/types/EAPMethod.ts @@ -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; +} diff --git a/src/types/EAPType.ts b/src/types/EAPType.ts deleted file mode 100644 index fb50dba..0000000 --- a/src/types/EAPType.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface IEAPType { - handleMessage(msg: Buffer, state: string, handlers, identifier: number); -} diff --git a/src/types/PacketHandler.ts b/src/types/PacketHandler.ts new file mode 100644 index 0000000..3bd6f5b --- /dev/null +++ b/src/types/PacketHandler.ts @@ -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; +} diff --git a/src/types/Server.ts b/src/types/Server.ts new file mode 100644 index 0000000..5d30d38 --- /dev/null +++ b/src/types/Server.ts @@ -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; +} diff --git a/ssl/README.md b/ssl/README.md index aa76d92..16f0876 100644 --- a/ssl/README.md +++ b/ssl/README.md @@ -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 diff --git a/ssl/cert/00.pem b/ssl/cert/00.pem index 145f3e4..868276d 100644 --- a/ssl/cert/00.pem +++ b/ssl/cert/00.pem @@ -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----- diff --git a/ssl/cert/ca.key b/ssl/cert/ca.key new file mode 100644 index 0000000..f6ca8b1 --- /dev/null +++ b/ssl/cert/ca.key @@ -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----- diff --git a/ssl/cert/ca.pem b/ssl/cert/ca.pem index 9ae9f81..53246a8 100644 --- a/ssl/cert/ca.pem +++ b/ssl/cert/ca.pem @@ -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----- diff --git a/ssl/cert/server.crt b/ssl/cert/server.crt new file mode 100644 index 0000000..868276d --- /dev/null +++ b/ssl/cert/server.crt @@ -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----- diff --git a/ssl/cert/server.csr b/ssl/cert/server.csr index d81abea..03d5b20 100644 --- a/ssl/cert/server.csr +++ b/ssl/cert/server.csr @@ -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----- diff --git a/ssl/cert/server.key b/ssl/cert/server.key new file mode 100644 index 0000000..4811c06 --- /dev/null +++ b/ssl/cert/server.key @@ -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----- diff --git a/ssl/db/index.txt b/ssl/db/index.txt index a362bb7..a5d9f52 100644 --- a/ssl/db/index.txt +++ b/ssl/db/index.txt @@ -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 diff --git a/tsconfig.json b/tsconfig.json index b62e938..e7e0175 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "outDir": "./dist", "rootDir": "./src", - // target settings for node 10 + // target settings for node js "module": "commonjs", "target": "es2018", "lib": ["es2018"],