Initial push 0.2.0

This commit is contained in:
Alex Rennie-Lis 2024-04-05 22:57:26 +01:00
parent 93472ae250
commit 1d8bd0c989
55 changed files with 3515 additions and 0 deletions

178
code/index.js Normal file
View File

@ -0,0 +1,178 @@
// Baseline
const product = 'NetRadius';
const version = '0.2.0';
// Load dependencies
const dgram = require ('dgram');
const fs = require ('fs');
const http = require ('http');
// Load modules
log = require ('./lib/logger.js');
const handlers = require ('./lib/handlers.js');
// Load configuration
log.write (product + ' v' + version);
config = {};
try {
config = JSON.parse (fs.readFileSync ('./config.json').toString ());
}
catch (error) {
log.write ('Cannot open or read configuration file.');
log.write ('Using defaults');
config = {
ports: {
radius_authentication: 1812,
radius_accounting: 1813
},
storage: "json:./data.json",
client_secret: "password",
default_vlan_enabled: false
}
}
if (process.env['NETRADIUS_PORT_RADIUS_AUTH']) config.ports.radius_authentication = process.env['NETRADIUS_PORT_RADIUS_AUTH'];
if (process.env['NETRADIUS_PORT_RADIUS_ACCT']) config.ports.radius_accounting = process.env['NETRADIUS_PORT_RADIUS_ACCT'];
if (process.env['NETRADIUS_STORAGE']) config.storage = process.env['NETRADIUS_STORAGE'];
if (process.env['NETRADIUS_DEFAULT_VLAN']) config.default_vlan_enabled = process.env['NETRADIUS_DEFAULT_VLAN'];
if (process.env['NETRADIUS_DEFAULT_VLAN_ID']) config.default_vlan_id = process.env['NETRADIUS_DEFAULT_VLAN_ID'];
if (process.env['NETRADIUS_CLIENT_SECRET']) config.client_secret = process.env['NETRADIUS_CLIENT_SECRET'];
log.write ('Using configuration: ' + JSON.stringify (config));
// Listeners
var listeners = {
authentication: {
socket: dgram.createSocket ({ type: 'udp4', reuseAddr: true }),
handler: handlers.radius.authentication
},
accounting: {
socket: dgram.createSocket ({ type: 'udp4', reuseAddr: true }),
handler: handlers.radius.accounting
}
};
// RADIUS authentication
listeners.authentication.socket.on ('message', (msg, rinfo) => {
handlers.radius.authentication (msg, rinfo, (response, err) => {
if (!err) {
listeners.authentication.socket.send (response, 0, response.length, rinfo.port, rinfo.address, function (err, bytes) {
if (err) {
console.log ('Error sending response to ', rinfo);
}
});
}
});
});
listeners.authentication.socket.on ('listening', () => {
var address = listeners.authentication.socket.address ();
log.write ("Authentication listening on " + address.address + ":" + address.port);
});
listeners.authentication.socket.bind (config.ports.radius_authentication);
// RADIUS accounting
listeners.accounting.socket.on ('message', (msg, rinfo) => {
handlers.radius.accounting (msg, rinfo, (response, err) => {
if (!err) {
listeners.accounting.socket.send (response, 0, response.length, rinfo.port, rinfo.address, function (err, bytes) {
if (err) {
console.log ('Error sending response to ', rinfo);
}
});
}
});
});
listeners.accounting.socket.on ('listening', () => {
var address = listeners.accounting.socket.address ();
log.write ("Accounting listening on " + address.address + ":" + address.port);
});
listeners.accounting.socket.bind (config.ports.radius_accounting);
// HTTP listener
http.createServer(function (req, res) {
var url = req.url.substring (0, req.url.lastIndexOf ("/")) || req.url;
var endpoint = req.method + " " + url;
switch (endpoint) {
case "GET /health":
res.write ('OK\n\n');
res.statusCode = 200;
res.end ();
break;
case "POST /user":
var payload = '';
req.on ('data', chunk => {
payload += chunk.toString ();
});
req.on ('end', () => {
handlers.user.create (payload, (status, err) => {
if (err) {
res.write ('Error\n');
res.statusCode = 500;
res.end ();
}
else {
res.write (status);
res.statusCode = 200;
res.end ();
}
});
});
break;
case "UPDATE /user":
var payload = '';
req.on ('data', chunk => {
payload += chunk.toString ();
});
req.on ('end', () => {
handlers.user.update (payload, (status, err) => {
if (err) {
res.write ('Error\n');
res.statusCode = 500;
res.end ();
}
else {
res.write (status);
res.statusCode = 200;
res.end ();
}
});
});
break;
case "DELETE /user":
handlers.user.delete (req.url.substring (req.url.lastIndexOf ("/") + 1), (status, err) => {
if (err) {
res.write ('Error\n');
res.statusCode = 500;
res.end ();
}
else {
res.write (status);
res.statusCode = 200;
res.end ();
}
});
break;
default:
res.write ('Not found\n');
res.statusCode = 404;
res.end ();
}
}).listen (8080);
// Exit handles
const exitHandler = () => {
log.write ('Shutting down');
listeners.authentication.socket.close ();
listeners.accounting.socket.close ();
log.write ('Exiting');
process.exit ();
}
process.on ('SIGTERM', exitHandler);
process.on ('SIGINT', exitHandler);
process.on ('SIGUSR1', exitHandler);
process.on ('SIGUSR2', exitHandler);

81
code/lib/data.js Normal file
View File

@ -0,0 +1,81 @@
const fs = require ('fs');
try {
var data = JSON.parse (fs.readFileSync ('./data.json').toString ());
log.write ('Opened data file');
}
catch (error) {
var data = {
users: []
};
fs.writeFileSync ('./data.json', JSON.stringify (data, null, 2));
}
const persistData = () => {
var content = {
users: []
};
Object.keys (users).forEach ((user) => {
content.users.push ({
username: user,
password: users[user].password,
vlan: users[user].vlan
});
});
fs.writeFileSync ('./data.json', JSON.stringify (content, null, 2));
}
users = {};
data.users.forEach ((e) => {
users[e.username] = {
password: e.password,
vlan: e.vlan
}
});
module.exports = {
authUser: (username, password) => {
if (users[username] && users[username].password == password) {
return {
vlan: users[username].vlan
};
}
else {
return false;
}
},
createUser: (payload, callback) => {
try {
payload = JSON.parse (payload);
var username = payload.username;
var password = payload.password;
var vlan = payload.vlan;
users[username] = {
password: password,
vlan: vlan
};
persistData ();
callback ("OK\n\n", null);
}
catch (error) {
callback (null, "Error\n\n");
}
},
updateUser: (payload, callback) => {
payload = JSON.parse (payload);
callback ("OK\n\n", null);
},
deleteUser: (username, callback) => {
try {
delete users[username];
persistData ();
callback ("OK\n\n", null);
}
catch (error) {
callback (null, "Error\n\n");
}
}
}

63
code/lib/handlers.js Normal file
View File

@ -0,0 +1,63 @@
const radius = require ('radius');
const data = require ('./data.js');
module.exports = {
radius: {
authentication: (msg, info, callback) => {
try {
var decoded = radius.decode ({
packet: msg,
secret: config.client_secret
});
if (decoded.code == 'Access-Request') {
var username = decoded.attributes['User-Name'];
var password = decoded.attributes['User-Password'];
var user = data.authUser (username, password);
var vlan = false;
if (user) {
log.write (username + " access granted to VLAN " + user.vlan);
code = 'Access-Accept';
vlan = user.vlan;
}
else {
if (config.default_vlan_enabled && config.default_vlan_id) {
// Permit into default vlan if enabled
log.write (username + " unknown. Placing into default VLAN.");
code = 'Access-Accept';
vlan = config.default_vlan_id;
}
else {
log.write (username + " access denied.");
code = 'Access-Reject';
}
}
var response = radius.encode_response ({
packet: decoded,
code: code,
secret: config.client_secret,
attributes: {
"Tunnel-Medium-Type": 6,
"Tunnel-Type": 13,
"Tunnel-Private-Group-Id": vlan
}
});
callback (response, null);
}
}
catch (error) {
log.write ('Cannot read RADIUS packet');
log.write (error);
callback (null, "Error. Cannot read RADIUS packet.");
}
},
accounting: (msg, info, callback) => {
log.write ('Unsupported RADIUS packet');
callback (null, "Error. Unsupported RADIUS packet.");
}
},
user: {
create: data.createUser,
update: data.updateUser,
delete: data.deleteUser
}
}

6
code/lib/logger.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
write: (message) => {
let now = new Date ();
console.log (now.toISOString () + ' ' + message);
}
}

16
code/node_modules/.package-lock.json generated vendored Normal file
View File

@ -0,0 +1,16 @@
{
"name": "netradius",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"node_modules/radius": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/radius/-/radius-1.1.4.tgz",
"integrity": "sha512-UWuzdF6xf3NpsXFZZmUEkxtEalDXj8hdmMXgbGzn7vOk6zXNsiIY2I6SJ1euHt7PTQuMoz2qDEJB+AfJDJgQYw==",
"engines": {
"node": ">=0.8.0"
}
}
}
}

1
code/node_modules/radius/.npmignore generated vendored Normal file
View File

@ -0,0 +1 @@
node_modules

7
code/node_modules/radius/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,7 @@
language: node_js
node_js:
- lts/*
- 6
- "0.10"
- "0.12"
- "4.3"

24
code/node_modules/radius/LICENSE generated vendored Normal file
View File

@ -0,0 +1,24 @@
Copyright (c) 2012, Nearbuy Systems
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Nearbuy Systems nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL NEARBUY SYSTEMS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

258
code/node_modules/radius/README.md generated vendored Normal file
View File

@ -0,0 +1,258 @@
# node-radius [![Build Status](https://secure.travis-ci.org/retailnext/node-radius.png)](http://travis-ci.org/retailnext/node-radius) - A RADIUS library for node.js
node-radius is a RADIUS packet encoding/decoding library for node.js written in Javascript. With node-radius you can easily decode received packets, encode packets to send, and prepare responses to received packets. node-radius supports both RADIUS authentication and RADIUS accounting packets.
node-radius requires node.js v0.8.0. To install node-radius, simply run `npm install radius` in your project directory.
Let's look at some examples of how to use node-radius:
var radius = require('radius');
// ... receive raw_packet from UDP socket
var decoded = radius.decode({ packet: raw_packet, secret: "shared_secret" });
"decoded" might look something like this:
{
code: 'Access-Request',
identifer: 123,
length: 250,
attributes: {
'NAS-IP-Address': '10.1.2.3',
'User-Name': 'jlpicard',
'User-Password': 'beverly123',
'Vendor-Specific': {
'Federation-Starship': 'Enterprise'
}
}
}
To prepare your response packet, use the encode_response function:
var response = radius.encode_response({
packet: decoded,
code: "Access-Accept",
secret: "section31"
});
To prepare a stand-alone packet, try this:
var packet = radius.encode({
code: "Access-Request",
secret: "obsidian order",
attributes: [
['NAS-IP-Address', '10.5.5.5'],
['User-Name', 'egarak'],
['User-Password', 'tailoredfit'],
['Vendor-Specific', 555, [['Real-Name', 'arobinson']]]
]
});
## Method descriptions:
### radius.decode(\<args>)
decode takes as input an object with the following fields:
- packet (required): a Buffer containing the raw UDP RADIUS packet (as read off a socket)
- secret (required): a String containing the RADIUS shared secret
Using the dictionaries available, decode parses the raw packet and yields an object representation of the packet. The object has the following fields:
- code: string representation of the packet code ("Access-Request", "Accounting-Response", etc)
- identifier: packet identifier number (used for duplicate packet detection)
- length: RADIUS packet length
- attributes: an object containing all attributes node-radius knew how to parse. If an attribute is repeated, its value in the "attributes" object will become an Array containing each value. Unfortunately the dictionary files do not specify which attributes are repeatable, so if an attribute might be repeated then you need to check if the value in "attributes" is a scalar value or an Array.
- raw_attributes: an array of arrays containing each raw attribute (attribute type and a Buffer containing the attribute value). This is mainly used by node-radius for generating the response packet, and would only be useful to you if you are missing relevant dictionaries and/or want to decode attributes yourself.
When decoding requests (e.g. "Access-Request", "Accounting-Request"), decode will automatically verify the request authenticator and the Message-Authenticator attribute, if present. If the request doesn't check out, decode will raise an error. The error, an instance of Radius.InvalidSecretError, has a "decoded" field you can use to inspect the decoded but invalid message. The most common reason for an incorrect authenticator is using the wrong shared secret.
### radius.decode_without_secret(\<args>)
Identical to decode, but does not need the secret. This can be useful to "pre-decode" a message, in order to look-up (or calculate) the secret to be used to properly decode the message later.
A message decoded without a secret will contain null values for encrypted fields (typically passwords, etc.).
### radius.encode(\<args>)
encode takes an object for arguments and returns a Buffer ready to be sent over the wire. The accepted arguments are:
- code (required): string representation of the packet code ("Access-Request", "Accounting-Response", etc)
- secret (required): RADIUS shared secret
- identifier (optional): packet identifer number (defaults to a random number from 0 to 255)
- attributes (optional): RADIUS attributes you want to add to the packet
- add_message_authenticator (optional): a boolean value controlling whether the library adds the Message-Authenticator HMAC to the packet. See below for more details.
encode will automatically add the Message-Authenticator when:
- encoding a message with an "EAP-Message" attribute
- encoding a "Status-Server" message
- you manually pass the "add_message_authenticator: true" option
encode will not add the Message-Authenticator when:
- the attributes already contain a "Message-Authenticator" attribute
- you manually pass the "add_message_authenticator: false" option
The attributes will typically be like the following (see above example):
attributes: [
[<attribute name>, <attribute value>],
...
]
If you don't care about attribute ordering, you can use a hash for the attributes:
attributes: {
<attribute name>: <attribute value>,
...
}
If you want to send attributes that you haven't loaded a dictionary for, you can do:
attributes: [
[<attribute id>, <Buffer>],
...
]
Where the first item is the numeric attribute id and the second item is just a Buffer containing the value of the attribute (not including length).
You can specify Vendor-Specific attributes like so:
attributes: [
['Vendor-Specific', <vendor id>, [
[<attribute name>, <attribute value>],
[<attribute name>, <attribute value>]
],
...
]
Or if you want each vendor attribute as a separate attribute, try this:
attributes: [
['Vendor-Specific', <vendor id>, [[<attribute name>, <attribute value>]]],
['Vendor-Specific', <vendor id>, [[<attribute name>, <attribute value>]]]
...
]
Like regular attributes, you can also specify the attribute id and a raw Buffer value for VSAs. If your dictionary specifies vendor attributes using the BEGIN-VENDOR/END-VENDOR format, you can use the symbolic vendor name as defined in the dictionary in place of the numeric \<vendor id>.
You can specify the tag field-attribute like so (see RFC2868):
attributes: [
[<attribute name>, <tag number>, <attribute value>],
...
]
If the attribute has an optional tag and you don't want to send it, then only specify the \<attribute name> and the \<attribute value>.
### radius.encode\_response(\<args>)
encode_response prepares a response packet based on previously received and decoded packet. "args" is an object with the following properties:
- packet (required): the output of a previous call to radius.decode
- code (required): String representation of the packet code ("Access-Reject, "Accounting-Response", etc)
- attributes (optional): RADIUS attributes you want to add to the packet
encode_response does a few things for you to prepare the response:
1. sets the response packet's message identifier to the identifer of the previously received packet
1. copies any "Proxy-State" attributes from the previously received packet into the response packet
1. calculates the appropriate response authenticator based on the request's authenticator
1. calculates and adds a Message-Authenticator attribute if the request contained one
### radius.verify\_response(\<args>)
verify_response checks the authenticator and Message-Authenticator attribute, if applicable, of a response packet you receive. It returns true if the packet checks out, and false otherwise (likely because the other side's shared secret is wrong). "args" is an object with the following properties:
- request (required): the request packet you previously sent (should be the raw packet, i.e. the output of a call to radius.encode)
- response (required): the response you received to your request packet (again, the raw packet)
- secret (required): RADIUS shared secret
This method is useful if you are acting as the NAS. For example, if you send an "Access-Request", you can use this method to verify the response you get ("Reject" or "Accept") is legitimate.
Note that if the request contained a Message-Authenticator, the response must also contain a Message-Authenticator.
## Dictionaries
node-radius supports reading freeradius-style RADIUS dictionary files. node-radius comes with a slew of RFC dictionary files, so you should only worry about adding any vendor-specific dictionary files you have. node-radius will load all the dictionaries it knows about (the default RFC ones and any you added) automatically the first time it needs to, so you should add your dictionaries before you start to use the module.
### radius.add_dictionary(\<path>)
To add a dictionary to be loaded, use the **add_dictionary** function:
var radius = require('radius');
radius.add_dictionary('/path/to/my/dictionary');
add\_dictionary takes either a file or a directory (given a directory, it assumes everything in the directory is a dictionary file). add\_dictionary does not block or perform any IO. It simply adds the given path to a list which is used to load dictionaries later.
node-radius supports reading both the VENDORATTR and the BEGIN-VENDOR/END-VENDOR style for defining VSAs. node-radius also supports reading the following attribute modifiers: has_tag, encrypt=1.
node-radius will also follow "$INCLUDE" directives inside of dictionary files (to load other dictionary files).
## Example usage
The following is an example of a simple radius authentication server:
var radius = require('radius');
var dgram = require("dgram");
var secret = 'radius_secret';
var server = dgram.createSocket("udp4");
server.on("message", function (msg, rinfo) {
var code, username, password, packet;
packet = radius.decode({packet: msg, secret: secret});
if (packet.code != 'Access-Request') {
console.log('unknown packet type: ', packet.code);
return;
}
username = packet.attributes['User-Name'];
password = packet.attributes['User-Password'];
console.log('Access-Request for ' + username);
if (username == 'jlpicard' && password == 'beverly123') {
code = 'Access-Accept';
} else {
code = 'Access-Reject';
}
var response = radius.encode_response({
packet: packet,
code: code,
secret: secret
});
console.log('Sending ' + code + ' for user ' + username);
server.send(response, 0, response.length, rinfo.port, rinfo.address, function(err, bytes) {
if (err) {
console.log('Error sending response to ', rinfo);
}
});
});
server.on("listening", function () {
var address = server.address();
console.log("radius server listening " +
address.address + ":" + address.port);
});
server.bind(1812);
Client and server examples can be found in the examples directory.
## Important notes:
- node-radius in general does _not_ perform "higher-level" protocol validation, so for example node-radius will not complain if you encode an Access-Request packet but fail to include a NAS-IP-Address or NAS-Identifier.
- node-radius in general assumes most strings are UTF-8 encoded. This will work fine for ASCII and UTF-8 strings, but will not work for other encodings. At some point I might add an "encoding" option to override this default encoding, and/or a "raw" mode that just deals with Buffers (rather than Strings) when the encoding is not known.
- node-radius does not support non-standard VSAs (where type or length field for attributes are not one octet each).
- node-radius does not support special decoding/encoding for the following attribute types: ipv6addr, ifid, ipv6prefix, short. If node-radius encounters a type it doesn't support, node-radius will return a raw Buffer when decoding, and expect a Buffer when encoding.
- node-radius does not support any password encryption types other than that defined by RFC2865 for User-Password (e.g. does not support Tunnel-Password).
But, on the plus-side, unlike many other RADIUS libraries node-radius supports encrypting/decrypting passwords longer than 16 bytes!

48
code/node_modules/radius/accounting.js generated vendored Normal file
View File

@ -0,0 +1,48 @@
// Copyright (c) 2018, RetailNext, Inc.
// This material contains trade secrets and confidential information of
// RetailNext, Inc. Any use, reproduction, disclosure or dissemination
// is strictly prohibited without the explicit written permission
// of RetailNext, Inc.
// All rights reserved.
var fs = require("fs");
var radius = require("./lib/radius");
var dgram = require('dgram');
var dst_ip = "127.0.0.1";
var client = dgram.createSocket("udp4");
client.bind(49001);
var secret = "secret";
var attrs = {
'User-Name': 'jlpicard',
'User-Password': 'beverly',
'Service-Type': 'Login-User',
'NAS-IP-Address': '169.134.68.136'
};
var packet = radius.encode({
code: 'Access-Request',
identifier: 1,
attributes: attrs,
secret: secret
});
fs.writeFileSync("/tmp/short_password.packet", packet);
client.send(packet, 0, packet.length, 1812, dst_ip, function() {
attrs['User-Password'] = 'beverly-crusher-123';
packet = radius.encode({
code: 'Access-Request',
identifier: 2,
attributes: attrs,
secret: secret
});
fs.writeFileSync("/tmp/long_password.packet", packet);
client.send(packet, 0, packet.length, 1812, dst_ip, function() {
client.close();
});
});

29
code/node_modules/radius/decode.js generated vendored Normal file
View File

@ -0,0 +1,29 @@
// Copyright (c) 2018, RetailNext, Inc.
// This material contains trade secrets and confidential information of
// RetailNext, Inc. Any use, reproduction, disclosure or dissemination
// is strictly prohibited without the explicit written permission
// of RetailNext, Inc.
// All rights reserved.
var fs = require("fs");
var radius = require("./lib/radius");
var dgram = require('dgram');
var p = fs.readFileSync("/tmp/naughty_packet");
var packet = p;
// var decoded = radius.decode({packet: p, secret: ''});
var client = dgram.createSocket("udp4");
client.bind(49001);
client.send(packet, 0, packet.length, 1812, '127.0.0.1', function() {
console.log("done!");
});

View File

@ -0,0 +1,137 @@
# -*- text -*-
#
# Attributes and values defined in RFC 2865.
# http://www.ietf.org/rfc/rfc2865.txt
#
# $Id: dictionary.rfc2865 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE User-Name 1 string
ATTRIBUTE User-Password 2 string encrypt=1
ATTRIBUTE CHAP-Password 3 octets
ATTRIBUTE NAS-IP-Address 4 ipaddr
ATTRIBUTE NAS-Port 5 integer
ATTRIBUTE Service-Type 6 integer
ATTRIBUTE Framed-Protocol 7 integer
ATTRIBUTE Framed-IP-Address 8 ipaddr
ATTRIBUTE Framed-IP-Netmask 9 ipaddr
ATTRIBUTE Framed-Routing 10 integer
ATTRIBUTE Filter-Id 11 string
ATTRIBUTE Framed-MTU 12 integer
ATTRIBUTE Framed-Compression 13 integer
ATTRIBUTE Login-IP-Host 14 ipaddr
ATTRIBUTE Login-Service 15 integer
ATTRIBUTE Login-TCP-Port 16 integer
# Attribute 17 is undefined
ATTRIBUTE Reply-Message 18 string
ATTRIBUTE Callback-Number 19 string
ATTRIBUTE Callback-Id 20 string
# Attribute 21 is undefined
ATTRIBUTE Framed-Route 22 string
ATTRIBUTE Framed-IPX-Network 23 ipaddr
ATTRIBUTE State 24 octets
ATTRIBUTE Class 25 octets
ATTRIBUTE Vendor-Specific 26 octets
ATTRIBUTE Session-Timeout 27 integer
ATTRIBUTE Idle-Timeout 28 integer
ATTRIBUTE Termination-Action 29 integer
ATTRIBUTE Called-Station-Id 30 string
ATTRIBUTE Calling-Station-Id 31 string
ATTRIBUTE NAS-Identifier 32 string
ATTRIBUTE Proxy-State 33 octets
ATTRIBUTE Login-LAT-Service 34 string
ATTRIBUTE Login-LAT-Node 35 string
ATTRIBUTE Login-LAT-Group 36 octets
ATTRIBUTE Framed-AppleTalk-Link 37 integer
ATTRIBUTE Framed-AppleTalk-Network 38 integer
ATTRIBUTE Framed-AppleTalk-Zone 39 string
ATTRIBUTE CHAP-Challenge 60 octets
ATTRIBUTE NAS-Port-Type 61 integer
ATTRIBUTE Port-Limit 62 integer
ATTRIBUTE Login-LAT-Port 63 string
#
# Integer Translations
#
# Service types
VALUE Service-Type Login-User 1
VALUE Service-Type Framed-User 2
VALUE Service-Type Callback-Login-User 3
VALUE Service-Type Callback-Framed-User 4
VALUE Service-Type Outbound-User 5
VALUE Service-Type Administrative-User 6
VALUE Service-Type NAS-Prompt-User 7
VALUE Service-Type Authenticate-Only 8
VALUE Service-Type Callback-NAS-Prompt 9
VALUE Service-Type Call-Check 10
VALUE Service-Type Callback-Administrative 11
# Framed Protocols
VALUE Framed-Protocol PPP 1
VALUE Framed-Protocol SLIP 2
VALUE Framed-Protocol ARAP 3
VALUE Framed-Protocol Gandalf-SLML 4
VALUE Framed-Protocol Xylogics-IPX-SLIP 5
VALUE Framed-Protocol X.75-Synchronous 6
# Framed Routing Values
VALUE Framed-Routing None 0
VALUE Framed-Routing Broadcast 1
VALUE Framed-Routing Listen 2
VALUE Framed-Routing Broadcast-Listen 3
# Framed Compression Types
VALUE Framed-Compression None 0
VALUE Framed-Compression Van-Jacobson-TCP-IP 1
VALUE Framed-Compression IPX-Header-Compression 2
VALUE Framed-Compression Stac-LZS 3
# Login Services
VALUE Login-Service Telnet 0
VALUE Login-Service Rlogin 1
VALUE Login-Service TCP-Clear 2
VALUE Login-Service PortMaster 3
VALUE Login-Service LAT 4
VALUE Login-Service X25-PAD 5
VALUE Login-Service X25-T3POS 6
VALUE Login-Service TCP-Clear-Quiet 8
# Login-TCP-Port (see /etc/services for more examples)
VALUE Login-TCP-Port Telnet 23
VALUE Login-TCP-Port Rlogin 513
VALUE Login-TCP-Port Rsh 514
# Termination Options
VALUE Termination-Action Default 0
VALUE Termination-Action RADIUS-Request 1
# NAS Port Types
VALUE NAS-Port-Type Async 0
VALUE NAS-Port-Type Sync 1
VALUE NAS-Port-Type ISDN 2
VALUE NAS-Port-Type ISDN-V120 3
VALUE NAS-Port-Type ISDN-V110 4
VALUE NAS-Port-Type Virtual 5
VALUE NAS-Port-Type PIAFS 6
VALUE NAS-Port-Type HDLC-Clear-Channel 7
VALUE NAS-Port-Type X.25 8
VALUE NAS-Port-Type X.75 9
VALUE NAS-Port-Type G.3-Fax 10
VALUE NAS-Port-Type SDSL 11
VALUE NAS-Port-Type ADSL-CAP 12
VALUE NAS-Port-Type ADSL-DMT 13
VALUE NAS-Port-Type IDSL 14
VALUE NAS-Port-Type Ethernet 15
VALUE NAS-Port-Type xDSL 16
VALUE NAS-Port-Type Cable 17
VALUE NAS-Port-Type Wireless-Other 18
VALUE NAS-Port-Type Wireless-802.11 19

View File

@ -0,0 +1,56 @@
# -*- text -*-
#
# Attributes and values defined in RFC 2866.
# http://www.ietf.org/rfc/rfc2866.txt
#
# $Id: dictionary.rfc2866 39598 2011-10-26 05:33:30Z etxrab $
#
ATTRIBUTE Acct-Status-Type 40 integer
ATTRIBUTE Acct-Delay-Time 41 integer
ATTRIBUTE Acct-Input-Octets 42 integer
ATTRIBUTE Acct-Output-Octets 43 integer
ATTRIBUTE Acct-Session-Id 44 string
ATTRIBUTE Acct-Authentic 45 integer
ATTRIBUTE Acct-Session-Time 46 integer
ATTRIBUTE Acct-Input-Packets 47 integer
ATTRIBUTE Acct-Output-Packets 48 integer
ATTRIBUTE Acct-Terminate-Cause 49 integer
ATTRIBUTE Acct-Multi-Session-Id 50 string
ATTRIBUTE Acct-Link-Count 51 integer
# Accounting Status Types
VALUE Acct-Status-Type Start 1
VALUE Acct-Status-Type Stop 2
VALUE Acct-Status-Type Interim-Update 3
VALUE Acct-Status-Type Accounting-On 7
VALUE Acct-Status-Type Accounting-Off 8
VALUE Acct-Status-Type Failed 15
# Authentication Types
VALUE Acct-Authentic RADIUS 1
VALUE Acct-Authentic Local 2
VALUE Acct-Authentic Remote 3
VALUE Acct-Authentic Diameter 4
# Acct Terminate Causes
VALUE Acct-Terminate-Cause User-Request 1
VALUE Acct-Terminate-Cause Lost-Carrier 2
VALUE Acct-Terminate-Cause Lost-Service 3
VALUE Acct-Terminate-Cause Idle-Timeout 4
VALUE Acct-Terminate-Cause Session-Timeout 5
VALUE Acct-Terminate-Cause Admin-Reset 6
VALUE Acct-Terminate-Cause Admin-Reboot 7
VALUE Acct-Terminate-Cause Port-Error 8
VALUE Acct-Terminate-Cause NAS-Error 9
VALUE Acct-Terminate-Cause NAS-Request 10
VALUE Acct-Terminate-Cause NAS-Reboot 11
VALUE Acct-Terminate-Cause Port-Unneeded 12
VALUE Acct-Terminate-Cause Port-Preempted 13
VALUE Acct-Terminate-Cause Port-Suspended 14
VALUE Acct-Terminate-Cause Service-Unavailable 15
VALUE Acct-Terminate-Cause Callback 16
VALUE Acct-Terminate-Cause User-Error 17
VALUE Acct-Terminate-Cause Host-Request 18

View File

@ -0,0 +1,16 @@
# -*- text -*-
#
# Attributes and values defined in RFC 2867.
# http://www.ietf.org/rfc/rfc2867.txt
#
# $Id: dictionary.rfc2867 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE Acct-Tunnel-Connection 68 string
ATTRIBUTE Acct-Tunnel-Packets-Lost 86 integer
VALUE Acct-Status-Type Tunnel-Start 9
VALUE Acct-Status-Type Tunnel-Stop 10
VALUE Acct-Status-Type Tunnel-Reject 11
VALUE Acct-Status-Type Tunnel-Link-Start 12
VALUE Acct-Status-Type Tunnel-Link-Stop 13
VALUE Acct-Status-Type Tunnel-Link-Reject 14

View File

@ -0,0 +1,54 @@
# -*- text -*-
#
# Attributes and values defined in RFC 2868.
# http://www.ietf.org/rfc/rfc2868.txt
#
# $Id: dictionary.rfc2868 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE Tunnel-Type 64 integer has_tag
ATTRIBUTE Tunnel-Medium-Type 65 integer has_tag
ATTRIBUTE Tunnel-Client-Endpoint 66 string has_tag
ATTRIBUTE Tunnel-Server-Endpoint 67 string has_tag
ATTRIBUTE Tunnel-Password 69 string has_tag,encrypt=2
ATTRIBUTE Tunnel-Private-Group-Id 81 string has_tag
ATTRIBUTE Tunnel-Assignment-Id 82 string has_tag
ATTRIBUTE Tunnel-Preference 83 integer has_tag
ATTRIBUTE Tunnel-Client-Auth-Id 90 string has_tag
ATTRIBUTE Tunnel-Server-Auth-Id 91 string has_tag
# Tunnel Type
VALUE Tunnel-Type PPTP 1
VALUE Tunnel-Type L2F 2
VALUE Tunnel-Type L2TP 3
VALUE Tunnel-Type ATMP 4
VALUE Tunnel-Type VTP 5
VALUE Tunnel-Type AH 6
VALUE Tunnel-Type IP 7
VALUE Tunnel-Type MIN-IP 8
VALUE Tunnel-Type ESP 9
VALUE Tunnel-Type GRE 10
VALUE Tunnel-Type DVS 11
VALUE Tunnel-Type IP-in-IP 12
# Tunnel Medium Type
VALUE Tunnel-Medium-Type IP 1
VALUE Tunnel-Medium-Type IPv4 1
VALUE Tunnel-Medium-Type IPv6 2
VALUE Tunnel-Medium-Type NSAP 3
VALUE Tunnel-Medium-Type HDLC 4
VALUE Tunnel-Medium-Type BBN-1822 5
VALUE Tunnel-Medium-Type IEEE-802 6
VALUE Tunnel-Medium-Type E.163 7
VALUE Tunnel-Medium-Type E.164 8
VALUE Tunnel-Medium-Type F.69 9
VALUE Tunnel-Medium-Type X.121 10
VALUE Tunnel-Medium-Type IPX 11
VALUE Tunnel-Medium-Type Appletalk 12
VALUE Tunnel-Medium-Type DecNet-IV 13
VALUE Tunnel-Medium-Type Banyan-Vines 14
VALUE Tunnel-Medium-Type E.164-NSAP 15

View File

@ -0,0 +1,39 @@
# -*- text -*-
#
# Attributes and values defined in RFC 2869.
# http://www.ietf.org/rfc/rfc2869.txt
#
# $Id: dictionary.rfc2869 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE Acct-Input-Gigawords 52 integer
ATTRIBUTE Acct-Output-Gigawords 53 integer
ATTRIBUTE Event-Timestamp 55 date
ATTRIBUTE ARAP-Password 70 octets # 16 octets of data
ATTRIBUTE ARAP-Features 71 octets # 14 octets of data
ATTRIBUTE ARAP-Zone-Access 72 integer
ATTRIBUTE ARAP-Security 73 integer
ATTRIBUTE ARAP-Security-Data 74 string
ATTRIBUTE Password-Retry 75 integer
ATTRIBUTE Prompt 76 integer
ATTRIBUTE Connect-Info 77 string
ATTRIBUTE Configuration-Token 78 string
ATTRIBUTE EAP-Message 79 octets
ATTRIBUTE Message-Authenticator 80 octets
ATTRIBUTE ARAP-Challenge-Response 84 octets # 8 octets of data
ATTRIBUTE Acct-Interim-Interval 85 integer
# 86: RFC 2867
ATTRIBUTE NAS-Port-Id 87 string
ATTRIBUTE Framed-Pool 88 string
# ARAP Zone Access
VALUE ARAP-Zone-Access Default-Zone 1
VALUE ARAP-Zone-Access Zone-Filter-Inclusive 2
VALUE ARAP-Zone-Access Zone-Filter-Exclusive 4
# Prompt
VALUE Prompt No-Echo 0
VALUE Prompt Echo 1

View File

@ -0,0 +1,13 @@
# -*- text -*-
#
# Attributes and values defined in RFC 3162.
# http://www.ietf.org/rfc/rfc3162.txt
#
# $Id: dictionary.rfc3162 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE NAS-IPv6-Address 95 ipv6addr
ATTRIBUTE Framed-Interface-Id 96 ifid
ATTRIBUTE Framed-IPv6-Prefix 97 ipv6prefix
ATTRIBUTE Login-IPv6-Host 98 ipv6addr
ATTRIBUTE Framed-IPv6-Route 99 string
ATTRIBUTE Framed-IPv6-Pool 100 string

View File

@ -0,0 +1,30 @@
# -*- text -*-
#
# Attributes and values defined in RFC 3576.
# http://www.ietf.org/rfc/rfc3576.txt
#
# $Id: dictionary.rfc3576 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE Error-Cause 101 integer
# Service Types
VALUE Service-Type Authorize-Only 17
# Error causes
VALUE Error-Cause Residual-Context-Removed 201
VALUE Error-Cause Invalid-EAP-Packet 202
VALUE Error-Cause Unsupported-Attribute 401
VALUE Error-Cause Missing-Attribute 402
VALUE Error-Cause NAS-Identification-Mismatch 403
VALUE Error-Cause Invalid-Request 404
VALUE Error-Cause Unsupported-Service 405
VALUE Error-Cause Unsupported-Extension 406
VALUE Error-Cause Administratively-Prohibited 501
VALUE Error-Cause Proxy-Request-Not-Routable 502
VALUE Error-Cause Session-Context-Not-Found 503
VALUE Error-Cause Session-Context-Not-Removable 504
VALUE Error-Cause Proxy-Processing-Error 505
VALUE Error-Cause Resources-Unavailable 506
VALUE Error-Cause Request-Initiated 507

View File

@ -0,0 +1,16 @@
# -*- text -*-
#
# Attributes and values defined in RFC 3580.
# http://www.ietf.org/rfc/rfc3580.txt
#
# $Id: dictionary.rfc3580 28946 2009-07-06 12:39:58Z wmeier $
#
VALUE Acct-Terminate-Cause Supplicant-Restart 19
VALUE Acct-Terminate-Cause Reauthentication-Failure 20
VALUE Acct-Terminate-Cause Port-Reinit 21
VALUE Acct-Terminate-Cause Port-Disabled 22
VALUE NAS-Port-Type Token-Ring 20
VALUE NAS-Port-Type FDDI 21
VALUE Tunnel-Type VLAN 13

View File

@ -0,0 +1,9 @@
# -*- text -*-
#
# Attributes and values defined in RFC 4072
# http://www.ietf.org/rfc/4072.txt
#
# $Id: dictionary.rfc4072 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE EAP-Key-Name 102 string

View File

@ -0,0 +1,8 @@
# -*- text -*-
#
# Attributes and values defined in RFC 4372.
# http://www.ietf.org/rfc/4372.txt
#
# $Id: dictionary.rfc4372 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE Chargeable-User-Identity 89 string

View File

@ -0,0 +1,17 @@
# -*- text -*-
##############################################################################
#
# Attributes and values defined in RFC 4603.
# http://www.ietf.org/rfc/rfc4603.txt
#
# $Id: dictionary.rfc4603 39598 2011-10-26 05:33:30Z etxrab $
#
##############################################################################
VALUE NAS-Port-Type PPPoA 30
VALUE NAS-Port-Type PPPoEoA 31
VALUE NAS-Port-Type PPPoEoE 32
VALUE NAS-Port-Type PPPoEoVLAN 33
VALUE NAS-Port-Type PPPoEoQinQ 34

View File

@ -0,0 +1,28 @@
# -*- text -*-
#
# Attributes and values defined in RFC 4675.
# http://www.ietf.org/rfc/4675.txt
#
# $Id: dictionary.rfc4675 28946 2009-07-06 12:39:58Z wmeier $
#
#
# High byte = '1' (0x31) means the frames are tagged.
# High byte = '2' (0x32) means the frames are untagged.
#
# Next 12 bits MUST be zero.
#
# Lower 12 bits is the IEEE-802.1Q VLAN VID.
#
ATTRIBUTE Egress-VLANID 56 integer
ATTRIBUTE Ingress-Filters 57 integer
#
# First byte == '1' (0x31) means that the frames are tagged.
# First byte == '2' (0x32) means that the frames are untagged.
#
ATTRIBUTE Egress-VLAN-Name 58 string
ATTRIBUTE User-Priority-Table 59 octets # 8
VALUE Ingress-Filters Enabled 1
VALUE Ingress-Filters Disabled 2

View File

@ -0,0 +1,62 @@
# -*- text -*-
#
# Attributes and values defined in RFC 4679.
# http://www.ietf.org/rfc/4679.txt
#
# $Id: dictionary.rfc4679 28946 2009-07-06 12:39:58Z wmeier $
#
VENDOR ADSL-Forum 3561
BEGIN-VENDOR ADSL-Forum
#
# The first two attributes are prefixed with "ADSL-" because of
# conflicting names in dictionary.redback.
#
ATTRIBUTE ADSL-Agent-Circuit-Id 1 string
ATTRIBUTE ADSL-Agent-Remote-Id 2 string
ATTRIBUTE Actual-Data-Rate-Upstream 129 integer
ATTRIBUTE Actual-Data-Rate-Downstream 130 integer
ATTRIBUTE Minimum-Data-Rate-Upstream 131 integer
ATTRIBUTE Minimum-Data-Rate-Downstream 132 integer
ATTRIBUTE Attainable-Data-Rate-Upstream 133 integer
ATTRIBUTE Attainable-Data-Rate-Downstream 134 integer
ATTRIBUTE Maximum-Data-Rate-Upstream 135 integer
ATTRIBUTE Maximum-Data-Rate-Downstream 136 integer
ATTRIBUTE Minimum-Data-Rate-Upstream-Low-Power 137 integer
ATTRIBUTE Minimum-Data-Rate-Downstream-Low-Power 138 integer
ATTRIBUTE Maximum-Interleaving-Delay-Upstream 139 integer
ATTRIBUTE Actual-Interleaving-Delay-Upstream 140 integer
ATTRIBUTE Maximum-Interleaving-Delay-Downstream 141 integer
ATTRIBUTE Actual-Interleaving-Delay-Downstream 142 integer
#
# This next attribute has a weird encoding.
#
# Octet[0] - 0x01 AAL5
# Octet[0] - 0x02 Ethernet
# Octet[1] - 0x00 Not Available
# Octet[1] - 0x01 Untagged Ethernet
# Octet[1] - 0x02 Single-Tagged Ethernet
# Octet[2] - 0x00 Not available
# Octet[2] - 0x01 PPPoA LLC
# Octet[2] - 0x02 PPPoA Null
# Octet[2] - 0x03 IPoA LLC
# Octet[2] - 0x04 IPoA NULL
# Octet[2] - 0x05 Ethernet over AAL5 LLC with FCS
# Octet[2] - 0x06 Ethernet over AAL5 LLC without FCS
# Octet[2] - 0x07 Ethernet over AAL5 Null with FCS
# Octet[2] - 0x08 Ethernet over AAL5 Null without FCS
#
ATTRIBUTE Access-Loop-Encapsulation 144 octets # 3
#
# If this attribute exists, it means that IFW has been performed
# for the subscribers session.
#
ATTRIBUTE IWF-Session 252 octets # 0
END-VENDOR ADSL-Forum

View File

@ -0,0 +1,11 @@
# -*- text -*-
##############################################################################
#
# Attributes and values defined in RFC 4818.
# http://www.ietf.org/rfc/rfc4818.txt
#
# $Id: dictionary.rfc4818 28946 2009-07-06 12:39:58Z wmeier $
#
##############################################################################
ATTRIBUTE Delegated-IPv6-Prefix 123 ipv6prefix

View File

@ -0,0 +1,8 @@
# -*- text -*-
#
# Attributes and values defined in RFC 4849.
# http://www.ietf.org/rfc/rfc4849.txt
#
# $Id: dictionary.rfc4849 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE NAS-Filter-Rule 92 string

View File

@ -0,0 +1,27 @@
# -*- text -*-
#
# Attributes and values defined in RFC 5090.
# http://www.ietf.org/rfc/rfc5090.txt
#
# $Id: dictionary.rfc5090 28946 2009-07-06 12:39:58Z wmeier $
#
ATTRIBUTE Digest-Response 103 string
ATTRIBUTE Digest-Realm 104 string
ATTRIBUTE Digest-Nonce 105 string
ATTRIBUTE Digest-Response-Auth 106 string
ATTRIBUTE Digest-Nextnonce 107 string
ATTRIBUTE Digest-Method 108 string
ATTRIBUTE Digest-URI 109 string
ATTRIBUTE Digest-Qop 110 string
ATTRIBUTE Digest-Algorithm 111 string
ATTRIBUTE Digest-Entity-Body-Hash 112 string
ATTRIBUTE Digest-CNonce 113 string
ATTRIBUTE Digest-Nonce-Count 114 string
ATTRIBUTE Digest-Username 115 string
ATTRIBUTE Digest-Opaque 116 string
ATTRIBUTE Digest-Auth-Param 117 string
ATTRIBUTE Digest-AKA-Auts 118 string
ATTRIBUTE Digest-Domain 119 string
ATTRIBUTE Digest-Stale 120 string
ATTRIBUTE Digest-HA1 121 string
ATTRIBUTE SIP-AOR 122 string

View File

@ -0,0 +1,9 @@
# -*- text -*-
#
# Attributes and values defined in RFC 5176.
# http://www.ietf.org/rfc/rfc5176.txt
#
# $Id: dictionary.rfc5176 28946 2009-07-06 12:39:58Z wmeier $
#
VALUE Error-Cause Invalid-Attribute-Value 407
VALUE Error-Cause Multiple-Session-Selection-Unsupported 508

View File

@ -0,0 +1,41 @@
# -*- text -*-
#
# Attributes and values defined in RFC 5580.
# http://www.ietf.org/rfc/rfc5580.txt
#
# $Id: dictionary.rfc5580 39598 2011-10-26 05:33:30Z etxrab $
#
# One ASCII character of Namespace ID
# 0 = TADIG (GSM)
# 1 = Realm
# 2 = E212
#
#
# Followed by the actual string
ATTRIBUTE Operator-Name 126 string
#
# Large blobs of stuff
#
ATTRIBUTE Location-Information 127 octets
ATTRIBUTE Location-Data 128 octets
ATTRIBUTE Basic-Location-Policy-Rules 129 octets
ATTRIBUTE Extended-Location-Policy-Rules 130 octets
#
# Really a bit-packed field
#
ATTRIBUTE Location-Capable 131 integer
VALUE Location-Capable Civix-Location 1
VALUE Location-Capable Geo-Location 2
VALUE Location-Capable Users-Location 4
VALUE Location-Capable NAS-Location 8
ATTRIBUTE Requested-Location-Info 132 integer
VALUE Requested-Location-Info Civix-Location 1
VALUE Requested-Location-Info Geo-Location 2
VALUE Requested-Location-Info Users-Location 4
VALUE Requested-Location-Info NAS-Location 8
VALUE Requested-Location-Info Future-Requests 16
VALUE Requested-Location-Info None 32

View File

@ -0,0 +1,30 @@
# -*- text -*-
#
# Attributes and values defined in RFC 5607.
# http://www.ietf.org/rfc/rfc5607.txt
#
# $Id: dictionary.rfc5607 39598 2011-10-26 05:33:30Z etxrab $
#
VALUE Service-Type Framed-Management 18
ATTRIBUTE Framed-Management 133 integer
VALUE Framed-Management SNMP 1
VALUE Framed-Management Web-Based 2
VALUE Framed-Management Netconf 3
VALUE Framed-Management FTP 4
VALUE Framed-Management TFTP 5
VALUE Framed-Management SFTP 6
VALUE Framed-Management RCP 7
VALUE Framed-Management SCP 8
ATTRIBUTE Management-Transport-Protection 134 integer
VALUE Management-Transport-Protection No-Protection 1
VALUE Management-Transport-Protection Integrity-Protection 2
VALUE Management-Transport-Protection Integrity-Confidentiality-Protection 3
ATTRIBUTE Management-Policy-Id 135 string
ATTRIBUTE Management-Privilege-Level 136 integer

View File

@ -0,0 +1,22 @@
# -*- text -*-
#
# Attributes and values defined in RFC 5904.
# http://www.ietf.org/rfc/rfc5904.txt
#
# $Id: dictionary.rfc5904 39598 2011-10-26 05:33:30Z etxrab $
#
# The next two attributes are continued, like EAP-Message/
ATTRIBUTE PKM-SS-Cert 137 octets
ATTRIBUTE PKM-CA-Cert 138 octets
# 28 bytes of data, 7 integers
ATTRIBUTE PKM-Config-Settings 139 octets
ATTRIBUTE PKM-Cryptosuite-List 140 octets
ATTRIBUTE PKM-SAID 141 short
# 6 bytes of data: SAID, 1 byte of type, 3 of cryptosuite
ATTRIBUTE PKM-SA-Descriptor 142 octets
# 133 bytes of data: integer lifetime, 1 byte sequence, 128 bytes of key
ATTRIBUTE PKM-Auth-Key 143 octets

81
code/node_modules/radius/examples/auth_client.js generated vendored Normal file
View File

@ -0,0 +1,81 @@
// Example radius client sending auth packets.
var radius = require('../lib/radius');
var dgram = require('dgram');
var util = require('util');
var secret = 'radius_secret';
var packet_accepted = {
code: "Access-Request",
secret: secret,
identifier: 0,
attributes: [
['NAS-IP-Address', '10.5.5.5'],
['User-Name', 'jlpicard'],
['User-Password', 'beverly123']
]
};
var packet_rejected = {
code: "Access-Request",
secret: secret,
identifier: 1,
attributes: [
['NAS-IP-Address', '10.5.5.5'],
['User-Name', 'egarak'],
['User-Password', 'tailoredfit']
]
};
var packet_wrong_secret = {
code: "Access-Request",
secret: "wrong_secret",
identifier: 2,
attributes: [
['NAS-IP-Address', '10.5.5.5'],
['User-Name', 'riker'],
['User-Password', 'Riker-Omega-3']
]
};
var client = dgram.createSocket("udp4");
client.bind(49001);
var response_count = 0;
client.on('message', function(msg, rinfo) {
var response = radius.decode({packet: msg, secret: secret});
var request = sent_packets[response.identifier];
// although it's a slight hassle to keep track of packets, it's a good idea to verify
// responses to make sure you are talking to a server with the same shared secret
var valid_response = radius.verify_response({
response: msg,
request: request.raw_packet,
secret: request.secret
});
if (valid_response) {
console.log('Got valid response ' + response.code + ' for packet id ' + response.identifier);
// take some action based on response.code
} else {
console.log('WARNING: Got invalid response ' + response.code + ' for packet id ' + response.identifier);
// don't take action since server cannot be trusted (but maybe alert user that shared secret may be incorrect)
}
if (++response_count == 3) {
client.close();
}
});
var sent_packets = {};
[packet_accepted, packet_rejected, packet_wrong_secret].forEach(function(packet) {
var encoded = radius.encode(packet);
sent_packets[packet.identifier] = {
raw_packet: encoded,
secret: packet.secret
};
client.send(encoded, 0, encoded.length, 1812, "localhost");
});

54
code/node_modules/radius/examples/auth_server.js generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Example radius server doing authentication
var radius = require('../lib/radius');
var dgram = require("dgram");
var secret = 'radius_secret';
var server = dgram.createSocket("udp4");
server.on("message", function (msg, rinfo) {
var code, username, password, packet;
try {
packet = radius.decode({packet: msg, secret: secret});
} catch (e) {
console.log("Failed to decode radius packet, silently dropping:", e);
return;
}
if (packet.code != 'Access-Request') {
console.log('unknown packet type: ', packet.code);
return;
}
username = packet.attributes['User-Name'];
password = packet.attributes['User-Password'];
console.log('Access-Request for ' + username);
if (username == 'jlpicard' && password == 'beverly123') {
code = 'Access-Accept';
} else {
code = 'Access-Reject';
}
var response = radius.encode_response({
packet: packet,
code: code,
secret: secret
});
console.log('Sending ' + code + ' for user ' + username);
server.send(response, 0, response.length, rinfo.port, rinfo.address, function(err, bytes) {
if (err) {
console.log('Error sending response to ', rinfo);
}
});
});
server.on("listening", function () {
var address = server.address();
console.log("radius server listening " +
address.address + ":" + address.port);
});
server.bind(1812);

886
code/node_modules/radius/lib/radius.js generated vendored Normal file
View File

@ -0,0 +1,886 @@
var fs = require("fs");
var util = require("util");
var crypto = require("crypto");
var path = require("path");
var Radius = {};
var attributes_map = {}, vendor_name_to_id = {};
var dictionary_locations = [path.normalize(__dirname + "/../dictionaries")];
const NOT_LOADED = 1;
const LOADING = 2;
const LOADED = 3;
var dictionaries_state = NOT_LOADED;
const NO_VENDOR = -1;
const ATTR_ID = 0;
const ATTR_NAME = 1;
const ATTR_TYPE = 2;
const ATTR_ENUM = 3;
const ATTR_REVERSE_ENUM = 4;
const ATTR_MODIFIERS = 5;
const AUTH_START = 4;
const AUTH_END = 20;
const AUTH_LENGTH = 16;
const MESSAGE_AUTHENTICATOR_LENGTH = 16;
Radius.InvalidSecretError = function(msg, decoded, constr) {
Error.captureStackTrace(this, constr || this);
this.message = msg || 'Error';
this.decoded = decoded;
};
util.inherits(Radius.InvalidSecretError, Error);
Radius.InvalidSecretError.prototype.name = 'Invalid Secret Error';
Radius.add_dictionary = function(file) {
dictionary_locations.push(path.resolve(file));
};
var load_dictionaries_cbs = [];
Radius.load_dictionaries = function() {
var self = this;
if (dictionaries_state == LOADED) {
return;
}
dictionary_locations.forEach(function(file) {
if (!fs.existsSync(file)) {
throw new Error("Invalid dictionary location: " + file);
}
if (fs.statSync(file).isDirectory()) {
var files = fs.readdirSync(file);
for (var j = 0; j < files.length; j++) {
self.load_dictionary(file + "/" + files[j]);
}
} else {
self.load_dictionary(file);
}
});
dictionaries_state = LOADED;
};
Radius.load_dictionary = function(file, seen_files) {
file = path.normalize(file);
var self = this;
if (seen_files === undefined) {
seen_files = {};
}
if (seen_files[file]) {
return;
}
seen_files[file] = true;
var includes = self._load_dictionary(fs.readFileSync(file, "ascii"));
includes.forEach(function (i) {
self.load_dictionary(path.join(path.dirname(file), i), seen_files);
});
};
Radius._load_dictionary = function(content) {
var lines = content.split("\n");
var vendor = NO_VENDOR, includes = [], attr_vendor;
for (var i = 0; i < lines.length; i++) {
var line = lines[i];
line = line.replace(/#.*/, "").replace(/\s+/g, " ");
var match = line.match(/^\s*VENDOR\s+(\S+)\s+(\d+)/);
if (match) {
vendor_name_to_id[match[1]] = match[2];
continue;
}
if ((match = line.match(/^\s*BEGIN-VENDOR\s+(\S+)/))) {
vendor = vendor_name_to_id[match[1]];
continue;
}
if (line.match(/^\s*END-VENDOR/)) {
vendor = NO_VENDOR;
continue;
}
var init_entry = function(vendor, attr_id) {
if (!attributes_map[vendor]) {
attributes_map[vendor] = {};
}
if (!attributes_map[vendor][attr_id]) {
attributes_map[vendor][attr_id] = [null, null, null, {}, {}, {}];
}
};
match = line.match(/^\s*(?:VENDORATTR\s+(\d+)|ATTRIBUTE)\s+(\S+)\s+(\d+)\s+(\S+)\s*(.+)?/);
if (match) {
attr_vendor = vendor;
if (match[1] !== undefined) {
attr_vendor = match[1];
}
var modifiers = {};
if (match[5] !== undefined) {
match[5].replace(/\s*/g, "").split(",").forEach(function(m) {
modifiers[m] = true;
});
}
init_entry(attr_vendor, match[3]);
attributes_map[attr_vendor][match[3]][ATTR_ID] = match[3];
attributes_map[attr_vendor][match[3]][ATTR_NAME] = match[2];
attributes_map[attr_vendor][match[3]][ATTR_TYPE] = match[4].toLowerCase();
attributes_map[attr_vendor][match[3]][ATTR_MODIFIERS] = modifiers;
var by_name = attributes_map[attr_vendor][match[2]];
if (by_name !== undefined) {
var by_index = attributes_map[attr_vendor][match[3]];
[ATTR_ENUM, ATTR_REVERSE_ENUM].forEach(function(field) {
for (var name in by_name[field]) {
by_index[field][name] = by_name[field][name];
}
});
}
attributes_map[attr_vendor][match[2]] = attributes_map[attr_vendor][match[3]];
continue;
}
match = line.match(/^\s*(?:VENDOR)?VALUE\s+(\d+)?\s*(\S+)\s+(\S+)\s+(\d+)/);
if (match) {
attr_vendor = vendor;
if (match[1] !== undefined) {
attr_vendor = match[1];
}
init_entry(attr_vendor, match[2]);
attributes_map[attr_vendor][match[2]][ATTR_ENUM][match[4]] = match[3];
attributes_map[attr_vendor][match[2]][ATTR_REVERSE_ENUM][match[3]] = match[4];
continue;
}
if ((match = line.match(/^\s*\$INCLUDE\s+(.*)/))) {
includes.push(match[1]);
}
}
return includes;
};
Radius.unload_dictionaries = function() {
attributes_map = {};
vendor_name_to_id = {};
dictionaries_state = NOT_LOADED;
};
Radius.attr_name_to_id = function(attr_name, vendor_id) {
return this._attr_to(attr_name, vendor_id, ATTR_ID);
};
Radius.attr_id_to_name = function(attr_name, vendor_id) {
return this._attr_to(attr_name, vendor_id, ATTR_NAME);
};
Radius.vendor_name_to_id = function(vendor_name) {
return vendor_name_to_id[vendor_name];
};
Radius._attr_to = function(attr, vendor_id, target) {
if (vendor_id === undefined) {
vendor_id = NO_VENDOR;
}
if (!attributes_map[vendor_id]) {
return;
}
var attr_info = attributes_map[vendor_id][attr];
if (!attr_info) {
return;
}
return attr_info[target];
};
var code_map = {
1: "Access-Request",
2: "Access-Accept",
3: "Access-Reject",
4: "Accounting-Request",
5: "Accounting-Response",
6: "Interim-Accounting",
7: "Password-Request",
8: "Password-Ack",
9: "Password-Reject",
10: "Accounting-Message",
11: "Access-Challenge",
12: "Status-Server",
13: "Status-Client",
21: "Resource-Free-Request",
22: "Resource-Free-Response",
23: "Resource-Query-Request",
24: "Resource-Query-Response",
25: "Alternate-Resource-Reclaim-Request",
26: "NAS-Reboot-Request",
27: "NAS-Reboot-Response",
29: "Next-Passcode",
30: "New-Pin",
31: "Terminate-Session",
32: "Password-Expired",
33: "Event-Request",
34: "Event-Response",
40: "Disconnect-Request",
41: "Disconnect-ACK",
42: "Disconnect-NAK",
43: "CoA-Request",
44: "CoA-ACK",
45: "CoA-NAK",
50: "IP-Address-Allocate",
51: "IP-Address-Release"
};
var uses_random_authenticator = {
"Access-Request": true,
"Status-Server": true
};
var is_request_code = {
"Status-Server": true
};
var reverse_code_map = {};
for (var code in code_map) {
reverse_code_map[code_map[code]] = code;
if (code_map[code].match(/Request/)) {
is_request_code[code_map[code]] = true;
}
}
Radius.error = function(error_msg) {
var err = error_msg;
if (typeof(error_msg) === 'string') {
err = new Error(error_msg);
}
throw err;
};
// this is a convenience method, "decode({..., no_secret: true})" will also do the job
Radius.decode_without_secret = function(args) {
// copy args' fields without modifiying the orginal
var nargs = {no_secret: true};
for (var p in args) {
nargs[p] = args[p];
}
return this.decode(nargs, this._decode);
};
Radius.decode = function(args) {
this.load_dictionaries();
var packet = args.packet;
if (!packet || packet.length < 4) {
this.error("decode: packet too short");
return;
}
var ret = {};
ret.code = code_map[packet.readUInt8(0)];
if (!ret.code) {
this.error("decode: invalid packet code '" + packet.readUInt8(0) + "'");
return;
}
ret.identifier = packet.readUInt8(1);
ret.length = packet.readUInt16BE(2);
if (packet.length < ret.length) {
this.error("decode: incomplete packet");
return;
}
this.authenticator = ret.authenticator = packet.slice(AUTH_START, AUTH_END);
this.no_secret = args.no_secret;
this.secret = args.secret;
var attrs = packet.slice(AUTH_END, ret.length);
ret.attributes = {};
ret.raw_attributes = [];
try {
this.decode_attributes(attrs, ret.attributes, NO_VENDOR, ret.raw_attributes);
} catch(err) {
this.error(err);
return;
}
if (!uses_random_authenticator[ret.code] && is_request_code[ret.code] && !args.no_secret) {
var orig_authenticator = new Buffer(AUTH_LENGTH);
packet.copy(orig_authenticator, 0, AUTH_START, AUTH_END);
packet.fill(0, AUTH_START, AUTH_END);
var checksum = this.calculate_packet_checksum(packet, args.secret);
orig_authenticator.copy(packet, AUTH_START);
if (checksum.toString() != this.authenticator.toString()) {
this.error(new Radius.InvalidSecretError("decode: authenticator mismatch (possible shared secret mismatch)", ret));
return;
}
}
if (is_request_code[ret.code] && ret.attributes["Message-Authenticator"] && !args.no_secret) {
this._verify_request_message_authenticator(args, ret);
}
return ret;
};
Radius.zero_out_message_authenticator = function(attributes) {
var ma_id = this.attr_name_to_id("Message-Authenticator");
var new_attrs = attributes.slice(0);
for (var i = 0; i < new_attrs.length; i++) {
var attr = new_attrs[i];
if (attr[0] == ma_id) {
new_attrs[i] = [ma_id, new Buffer(MESSAGE_AUTHENTICATOR_LENGTH)];
new_attrs[i][1].fill(0x00);
break;
}
}
return new_attrs;
};
Radius._verify_request_message_authenticator = function(args, request) {
var reencoded = this.encode({
code: request.code,
attributes: this.zero_out_message_authenticator(request.raw_attributes),
identifier: request.identifier,
secret: args.secret
});
request.authenticator.copy(reencoded, AUTH_START);
var orig_ma = request.attributes["Message-Authenticator"];
var expected_ma = this.calculate_message_authenticator(reencoded, args.secret);
if (orig_ma.toString() != expected_ma.toString()) {
this.error(new Radius.InvalidSecretError("decode: Message-Authenticator mismatch (possible shared secret mismatch)", request));
}
};
Radius.verify_response = function(args) {
this.load_dictionaries();
if (!args || !Buffer.isBuffer(args.request) || !Buffer.isBuffer(args.response)) {
this.error("verify_response: must provide raw request and response packets");
return;
}
if (args.secret == null) {
this.error("verify_response: must specify shared secret");
return;
}
// first verify authenticator
var got_checksum = new Buffer(AUTH_LENGTH);
args.response.copy(got_checksum, 0, AUTH_START, AUTH_END);
args.request.copy(args.response, AUTH_START, AUTH_START, AUTH_END);
var expected_checksum = this.calculate_packet_checksum(args.response, args.secret);
got_checksum.copy(args.response, AUTH_START);
if (expected_checksum.toString() != args.response.slice(AUTH_START, AUTH_END).toString()) {
return false;
}
return this._verify_response_message_authenticator(args);
};
Radius._verify_response_message_authenticator = function(args) {
var parsed_request = this.decode({
packet: args.request,
secret: args.secret
});
if (parsed_request.attributes["Message-Authenticator"]) {
var parsed_response = this.decode({
packet: args.response,
secret: args.secret
});
var got_ma = parsed_response.attributes["Message-Authenticator"];
if (!got_ma) {
return false;
}
var expected_response = this.encode({
secret: args.secret,
code: parsed_response.code,
identifier: parsed_response.identifier,
attributes: this.zero_out_message_authenticator(parsed_response.raw_attributes)
});
parsed_request.authenticator.copy(expected_response, AUTH_START);
var expected_ma = this.calculate_message_authenticator(expected_response, args.secret);
if (expected_ma.toString() != got_ma.toString()) {
return false;
}
}
return true;
};
Radius.decode_attributes = function(data, attr_hash, vendor, raw_attrs) {
var type, length, value, tag;
while (data.length > 0) {
type = data.readUInt8(0);
length = data.readUInt8(1);
value = data.slice(2, length);
tag = undefined;
if (length < 2) {
throw new Error("invalid attribute length: " + length);
}
if (raw_attrs) {
raw_attrs.push([type, value]);
}
data = data.slice(length);
var attr_info = attributes_map[vendor] && attributes_map[vendor][type];
if (!attr_info) {
continue;
}
if (attr_info[ATTR_MODIFIERS]["has_tag"]) {
var first_byte = value.readUInt8(0);
if (first_byte <= 0x1F) {
tag = first_byte;
value = value.slice(1);
}
}
if (attr_info[ATTR_MODIFIERS]["encrypt=1"]) {
value = this.decrypt_field(value);
} else {
switch (attr_info[ATTR_TYPE]) {
case "string":
case "text":
// assumes utf8 encoding for strings
value = value.toString("utf8");
break;
case "ipaddr":
var octets = [];
for (var i = 0; i < value.length; i++) {
octets.push(value[i]);
}
value = octets.join(".");
break;
case "date":
value = new Date(value.readUInt32BE(0) * 1000);
break;
case "time":
case "integer":
if (attr_info[ATTR_MODIFIERS]["has_tag"]) {
var buf = new Buffer([0, 0, 0, 0]);
value.copy(buf, 1);
value = buf;
}
value = value.readUInt32BE(0);
value = attr_info[ATTR_ENUM][value] || value;
break;
}
if (attr_info[ATTR_NAME] == "Vendor-Specific") {
if (value[0] !== 0x00) {
throw new Error("Invalid vendor id");
}
var vendor_attrs = attr_hash["Vendor-Specific"];
if (!vendor_attrs) {
vendor_attrs = attr_hash["Vendor-Specific"] = {};
}
this.decode_attributes(value.slice(4), vendor_attrs, value.readUInt32BE(0));
continue;
}
}
if (tag !== undefined) {
value = [tag, value];
}
if (attr_hash[attr_info[ATTR_NAME]] !== undefined) {
if (!(attr_hash[attr_info[ATTR_NAME]] instanceof Array)) {
attr_hash[attr_info[ATTR_NAME]] = [attr_hash[attr_info[ATTR_NAME]]];
}
attr_hash[attr_info[ATTR_NAME]].push(value);
} else {
attr_hash[attr_info[ATTR_NAME]] = value;
}
}
};
Radius.decrypt_field = function(field) {
if (this.no_secret) {
return null;
}
if (field.length < 16) {
throw new Error("Invalid password: too short");
}
if (field.length > 128) {
throw new Error("Invalid password: too long");
}
if (field.length % 16 != 0) {
throw new Error("Invalid password: not padded");
}
var decrypted = this._crypt_field(field, true);
if (decrypted === null) return null;
return decrypted.toString("utf8");
};
Radius.encrypt_field = function(field) {
var len = Buffer.byteLength(field, 'utf8');
var buf = new Buffer(len + 15 - ((15 + len) % 16));
buf.write(field, 0, len);
// null-out the padding
for (var i = len; i < buf.length; i++) {
buf[i] = 0x00;
}
return this._crypt_field(buf, false);
};
Radius._crypt_field = function(field, is_decrypt) {
var ret = new Buffer(0);
var second_part_to_be_hashed = this.authenticator;
if (this.secret === undefined) {
throw new Error("Must provide RADIUS shared secret");
}
for (var i = 0; i < field.length; i = i + 16) {
var hasher = crypto.createHash("md5");
hasher.update(this.secret);
hasher.update(second_part_to_be_hashed);
var hash = new Buffer(hasher.digest("binary"), "binary");
var xor_result = new Buffer(16);
for (var j = 0; j < 16; j++) {
xor_result[j] = field[i + j] ^ hash[j];
if (is_decrypt && xor_result[j] == 0x00) {
xor_result = xor_result.slice(0, j);
break;
}
}
ret = Buffer.concat([ret, xor_result]);
second_part_to_be_hashed = is_decrypt ? field.slice(i, i + 16) : xor_result;
}
return ret;
};
Radius.encode_response = function(args) {
this.load_dictionaries();
var packet = args.packet;
if (!packet) {
this.error("encode_response: must provide packet");
return;
}
if (!args.attributes) {
args.attributes = [];
}
var proxy_state_id = this.attr_name_to_id("Proxy-State");
for (var i = 0; i < packet.raw_attributes.length; i++) {
var attr = packet.raw_attributes[i];
if (attr[0] == proxy_state_id) {
args.attributes.push(attr);
}
}
var response = this.encode({
code: args.code,
identifier: packet.identifier,
authenticator: packet.authenticator,
attributes: args.attributes,
secret: args.secret,
add_message_authenticator: packet.attributes["Message-Authenticator"] != null
});
return response;
};
Radius.encode = function(args) {
this.load_dictionaries();
if (!args || args.code === undefined) {
this.error("encode: must specify code");
return;
}
if (args.secret === undefined) {
this.error("encode: must provide RADIUS shared secret");
return;
}
var packet = new Buffer(4096);
var offset = 0;
var code = reverse_code_map[args.code];
if (code === undefined) {
this.error("encode: invalid packet code '" + args.code + "'");
return;
}
packet.writeUInt8(+code, offset++);
var identifier = args.identifier;
if (identifier === undefined) {
identifier = Math.floor(Math.random() * 256);
}
if (identifier > 255) {
this.error("encode: identifier too large");
return;
}
packet.writeUInt8(identifier, offset++);
// save room for length
offset += 2;
var authenticator = args.authenticator;
if (!authenticator) {
if (uses_random_authenticator[args.code]) {
authenticator = crypto.randomBytes(AUTH_LENGTH);
} else {
authenticator = new Buffer(AUTH_LENGTH);
authenticator.fill(0x00);
}
}
return this._encode_with_authenticator(args, packet, offset, authenticator);
};
Radius._encode_with_authenticator = function(args, packet, offset, authenticator) {
authenticator.copy(packet, offset);
offset += AUTH_LENGTH;
this.secret = args.secret;
this.no_secret = false;
this.authenticator = authenticator;
args.attributes = this.ensure_array_attributes(args.attributes);
var add_message_authenticator = args.add_message_authenticator;
if (add_message_authenticator == null) {
var eap_id = this.attr_name_to_id("EAP-Message");
var ma_id = this.attr_name_to_id("Message-Authenticator");
for (var i = 0; i < args.attributes.length; i++) {
var attr_id = args.attributes[i][0];
if (attr_id == eap_id || attr_id == "EAP-Message") {
add_message_authenticator = true;
} else if (attr_id == ma_id || attr_id == "Message-Authenticator") {
add_message_authenticator = false;
break;
}
}
if (add_message_authenticator == null && args.code == "Status-Server") {
add_message_authenticator = true;
}
}
if (add_message_authenticator) {
var empty_authenticator = new Buffer(MESSAGE_AUTHENTICATOR_LENGTH);
empty_authenticator.fill(0x00);
args.attributes.push(["Message-Authenticator", empty_authenticator]);
}
try {
offset += this.encode_attributes(packet.slice(offset), args.attributes, NO_VENDOR);
} catch (err) {
this.error(err);
return;
}
// now write the length in
packet.writeUInt16BE(offset, 2);
packet = packet.slice(0, offset);
var message_authenticator;
if (add_message_authenticator && !is_request_code[args.code]) {
message_authenticator = this.calculate_message_authenticator(packet, args.secret);
message_authenticator.copy(packet, offset - MESSAGE_AUTHENTICATOR_LENGTH);
}
if (!uses_random_authenticator[args.code]) {
this.calculate_packet_checksum(packet, args.secret).copy(packet, AUTH_START);
}
if (add_message_authenticator && is_request_code[args.code]) {
message_authenticator = this.calculate_message_authenticator(packet, args.secret);
message_authenticator.copy(packet, offset - MESSAGE_AUTHENTICATOR_LENGTH);
}
return packet;
};
Radius.calculate_message_authenticator = function(packet, secret) {
var hmac = crypto.createHmac('md5', secret);
hmac.update(packet);
return new Buffer(hmac.digest('binary'), 'binary');
};
Radius.calculate_packet_checksum = function(packet, secret) {
var hasher = crypto.createHash("md5");
hasher.update(packet);
hasher.update(secret);
return new Buffer(hasher.digest("binary"), "binary");
};
Radius.ensure_array_attributes = function(attributes) {
if (!attributes) {
return [];
}
if (typeof(attributes) == 'object' && !Array.isArray(attributes)) {
var array_attributes = [];
for (var name in attributes) {
var val = attributes[name];
if (typeof(val) == 'object') {
throw new Error("Cannot have nested attributes when using hash syntax. Use array syntax instead");
}
array_attributes.push([name, val]);
}
return array_attributes;
}
return attributes;
};
Radius.encode_attributes = function(packet, attributes, vendor) {
var offset = 0;
for (var i = 0; i < attributes.length; i++) {
var attr = attributes[i];
var attr_info = attributes_map[vendor] && attributes_map[vendor][attr[0]];
if (!attr_info && !(attr[1] instanceof Buffer)) {
throw new Error("encode: invalid attributes - must give Buffer for " +
"unknown attribute '" + attr[0] + "'");
}
var out_value, in_value = attr[1];
if (in_value instanceof Buffer) {
out_value = in_value;
} else {
var has_tag = attr_info[ATTR_MODIFIERS]["has_tag"] && attr.length == 3;
if (has_tag) {
in_value = attr[2];
}
if (attr_info[ATTR_MODIFIERS]["encrypt=1"]) {
out_value = this.encrypt_field(in_value);
} else {
switch (attr_info[ATTR_TYPE]) {
case "string":
case "text":
if (in_value.length == 0) {
continue;
}
out_value = new Buffer(in_value + "", "utf8");
break;
case "ipaddr":
out_value = new Buffer(in_value.split("."));
if (out_value.length != 4) {
throw new Error("encode: invalid IP: " + in_value);
}
break;
case "date":
in_value = Math.floor(in_value.getTime() / 1000);
case "time":
case "integer":
out_value = new Buffer(4);
in_value = attr_info[ATTR_REVERSE_ENUM][in_value] || in_value;
if (isNaN(in_value)) {
throw new Error("envode: invalid attribute value: " + in_value);
}
out_value.writeUInt32BE(+in_value, 0);
if (has_tag) {
out_value = out_value.slice(1);
}
break;
default:
if (attr_info[ATTR_NAME] != "Vendor-Specific") {
throw new Error("encode: must provide Buffer for attribute '" + attr_info[ATTR_NAME] + "'");
}
}
// handle VSAs specially
if (attr_info[ATTR_NAME] == "Vendor-Specific") {
var vendor_id = isNaN(attr[1]) ? vendor_name_to_id[attr[1]] : attr[1];
if (vendor_id === undefined) {
throw new Error("encode: unknown vendor '" + attr[1] + "'");
}
// write the attribute id
packet.writeUInt8(+attr_info[ATTR_ID], offset++);
var length = this.encode_attributes(packet.slice(offset + 5), attr[2], vendor_id);
// write in the length
packet.writeUInt8(2 + 4 + length, offset++);
// write in the vendor id
packet.writeUInt32BE(+vendor_id, offset);
offset += 4;
offset += length;
continue;
}
}
}
// write the attribute id
packet.writeUInt8(attr_info ? +attr_info[ATTR_ID] : +attr[0], offset++);
// write in the attribute length
packet.writeUInt8(2 + out_value.length + (has_tag ? 1 : 0), offset++);
if (has_tag) {
packet.writeUInt8(attr[1], offset++);
}
// copy in the attribute value
out_value.copy(packet, offset);
offset += out_value.length;
}
return offset;
};
module.exports = Radius;

19
code/node_modules/radius/package.json generated vendored Normal file
View File

@ -0,0 +1,19 @@
{
"name" : "radius",
"version" : "1.1.4",
"description" : "RADIUS packet encoding/decoding",
"author": "Nearbuy Systems <npm@nearbuysystems.com>",
"main": "./lib/radius",
"repository": {
"type": "git",
"url": "git://github.com/nearbuy/node-radius.git"
},
"engines": { "node": ">=0.8.0" },
"devDependencies": {
"nodeunit": "~0.8.6"
},
"scripts": {
"test": "nodeunit test"
},
"keywords": ["radius"]
}

53
code/node_modules/radius/short_password.js generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright (c) 2018, RetailNext, Inc.
// This material contains trade secrets and confidential information of
// RetailNext, Inc. Any use, reproduction, disclosure or dissemination
// is strictly prohibited without the explicit written permission
// of RetailNext, Inc.
// All rights reserved.
var fs = require("fs");
var radius = require("./lib/radius");
var dgram = require('dgram');
var dst_ip = "54.208.19.153";
radius.add_dictionary("/home/psanford/projects/nearbuy/storenet/node/radius/vendor_dictionary");
var client = dgram.createSocket("udp4");
client.bind(49001);
var secret = "XpmyBBATzveRp";
var attrs = [
['Vendor-Specific', 14823, [['Aruba-Location-Id', '13:37:13:37:13:37']]],
];
var packet = radius.encode({
code: 'Accounting-Request',
identifier: 1,
attributes: attrs,
secret: secret
});
fs.writeFileSync("/tmp/pkt.packet", packet);
client.on('message', function(msg, rinfo) {
var response = radius.decode({packet: msg, secret: secret});
console.log('got', response);
client.close();
});
client.send(packet, 0, packet.length, 1813, dst_ip);
// attrs['User-Password'] = 'beverly-crusher-123';
// packet = radius.encode({
// code: 'Access-Request',
// identifier: 2,
// attributes: attrs,
// secret: secret
// });
// fs.writeFileSync("/tmp/long_password.packet", packet);
// client.send(packet, 0, packet.length, 1812, dst_ip, function() {
// client.close();
// });
// });

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,16 @@
VENDOR Airespace 14179
BEGIN-VENDOR Airespace
ATTRIBUTE Airespace-Wlan-Id 1 integer
ATTRIBUTE Airespace-QOS-Level 2 integer
ATTRIBUTE Airespace-DSCP 3 integer
ATTRIBUTE Airespace-8021p-Tag 4 integer
ATTRIBUTE Airespace-Interface-Name 5 string
ATTRIBUTE Airespace-ACL-Name 6 string
VALUE Airespace-QOS-Level Bronze 3
VALUE Airespace-QOS-Level Silver 0
VALUE Airespace-QOS-Level Gold 1
VALUE Airespace-QOS-Level Platinum 2
END-VENDOR Airespace

View File

@ -0,0 +1,11 @@
# Aruba attributes
VENDORATTR 14823 Aruba-User-Role 1 string
VENDORATTR 14823 Aruba-User-Vlan 2 integer
VENDORATTR 14823 Aruba-Priv-Admin-User 3 integer
VENDORATTR 14823 Aruba-Admin-Role 4 string
VENDORATTR 14823 Aruba-Essid-Name 5 String
VENDORATTR 14823 Aruba-Location-Id 6 string
VENDORATTR 14823 Aruba-Port-Id 7 string
VENDORATTR 14823 Aruba-Template-User 8 string
VENDORATTR 14823 Aruba-Named-User-Vlan 9 string
VENDORATTR 14823 Aruba-AP-Group 10 string

View File

@ -0,0 +1,8 @@
VENDOR 123Foo 995486
BEGIN-VENDOR 123Foo
ATTRIBUTE 1Integer 1 integer
END-VENDOR 123Foo
VENDORATTR 995486 1String 2 string
VENDORATTR 995486 12345 3 string

View File

@ -0,0 +1,3 @@
ATTRIBUTE Attribute-Test1 244 string
$INCLUDE dictionary.test2

View File

@ -0,0 +1,4 @@
ATTRIBUTE Attribute-Test2 245 string
# don't do anything crazy like loop forever
$INCLUDE dictionary.test1

View File

@ -0,0 +1,3 @@
# Fake tunnel type to be loaded before ATTRIBUTE Tunnel-Type is defined
VALUE Tunnel-Type TESTTUNNEL 31

970
code/node_modules/radius/test/radius.test.js generated vendored Executable file
View File

@ -0,0 +1,970 @@
var testCase = require('nodeunit').testCase;
var radius = require('../lib/radius');
var fs = require('fs');
var crypto = require('crypto');
var secret;
var test_args = {};
module.exports = testCase({
setUp: function(callback) {
secret = "nearbuy";
callback();
},
tearDown: function(callback) {
radius.unload_dictionaries();
callback();
},
test_decode_mac_auth: function(test) {
var raw_packet = fs.readFileSync(__dirname + '/captures/aruba_mac_auth.packet');
radius.load_dictionary(__dirname + '/dictionaries/dictionary.aruba');
var decoded = radius.decode({ packet: raw_packet, secret: secret });
test.equal( decoded.code, 'Access-Request' );
test.equal( decoded.identifier, 58 );
test.equal( decoded.length, 208 );
var expected_attrs = {
'NAS-IP-Address': '10.0.0.90',
'NAS-Port': 0,
'NAS-Port-Type': 'Wireless-802.11',
'User-Name': '7c:c5:37:ff:f8:af',
'User-Password': '7c:c5:37:ff:f8:af',
'Calling-Station-Id': '7CC537FFF8AF',
'Called-Station-Id': '000B86F02068',
'Service-Type': 'Login-User',
'Vendor-Specific': {
'Aruba-Essid-Name': 'muir-aruba-guest',
'Aruba-Location-Id': '00:1a:1e:c6:b0:ca',
'Aruba-AP-Group': 'cloud-cp'
},
'Message-Authenticator': new Buffer('f8a12329c7ed5a6e2568515243efb918', 'hex')
};
test.deepEqual( decoded.attributes, expected_attrs );
test.done();
},
test_decode_mac_auth_without_secret: function(test) {
var raw_packet = fs.readFileSync(__dirname + '/captures/aruba_mac_auth.packet');
radius.load_dictionary(__dirname + '/dictionaries/dictionary.aruba');
var decoded = radius.decode_without_secret({ packet: raw_packet });
test.equal( decoded.code, 'Access-Request' );
test.equal( decoded.identifier, 58 );
test.equal( decoded.length, 208 );
var expected_attrs = {
'NAS-IP-Address': '10.0.0.90',
'NAS-Port': 0,
'NAS-Port-Type': 'Wireless-802.11',
'User-Name': '7c:c5:37:ff:f8:af',
'User-Password': null, // this is an encrypted field, and so cannot be read without the password
'Calling-Station-Id': '7CC537FFF8AF',
'Called-Station-Id': '000B86F02068',
'Service-Type': 'Login-User',
'Vendor-Specific': {
'Aruba-Essid-Name': 'muir-aruba-guest',
'Aruba-Location-Id': '00:1a:1e:c6:b0:ca',
'Aruba-AP-Group': 'cloud-cp'
},
'Message-Authenticator': new Buffer('f8a12329c7ed5a6e2568515243efb918', 'hex')
};
test.deepEqual( decoded.attributes, expected_attrs );
decoded = radius.decode({
secret: secret,
packet: radius.encode({
secret: secret,
code: "Access-Request",
attributes: {
'User-Name': 'Caenogaean-asphyxia',
'User-Password': 'barratry-Wertherism'
}
})
});
test.equal( decoded.attributes['User-Password'], 'barratry-Wertherism' );
test.done();
},
// make sure everthing is fine with no dictionaries
test_decode_no_dicts: function(test) {
var raw_packet = fs.readFileSync(__dirname + '/captures/aruba_mac_auth.packet');
radius.unload_dictionaries();
var orig_load = radius.load_dictionary;
radius.load_dictionary = function() { };
var decoded = radius.decode({ packet: raw_packet, secret: secret });
test.equal( decoded.code, 'Access-Request' );
test.equal( decoded.identifier, 58 );
test.equal( decoded.length, 208 );
// no pretty attributes
test.deepEqual( decoded.attributes, {} );
var expected_raw_attrs = [
[4, new Buffer([10, 0, 0, 90])],
[5, new Buffer([0, 0, 0, 0])],
[61, new Buffer([0, 0, 0, 19])],
[1, new Buffer('7c:c5:37:ff:f8:af')],
[2, new Buffer('eb2ef7e83ec1a05e04fb5c6d91e088569a990fa2b1b2dc6a0f048596081164cd', 'hex')],
[31, new Buffer('7CC537FFF8AF')],
[30, new Buffer('000B86F02068')],
[6, new Buffer([0, 0, 0, 1])],
[26, new Buffer('000039e705126d7569722d61727562612d6775657374', 'hex')],
[26, new Buffer('000039e7061330303a31613a31653a63363a62303a6361', 'hex')],
[26, new Buffer('000039e70a0a636c6f75642d6370', 'hex')],
[80, new Buffer('f8a12329c7ed5a6e2568515243efb918', 'hex')]
];
test.deepEqual( decoded.raw_attributes, expected_raw_attrs );
radius.load_dictionary = orig_load;
test.done();
},
// can make a "naked" packet
test_encode_access_request: function(test) {
radius.load_dictionary(__dirname + '/dictionaries/dictionary.aruba');
var attributes = [
['User-Name', 'ornithopter-aliptic'],
['User-Password', 'nucleohistone-overwilily'],
['Service-Type', 'Login-User'],
['NAS-IP-Address', '169.134.68.136'],
['Vendor-Specific', 14823, [
['Aruba-User-Role', 'cracked-tylote'],
[2, 825]
]],
['Vendor-Specific', 14823, [['Aruba-Essid-Name', 'phene-dentinalgia']]]
];
var packet = radius.encode({
code: 'Access-Request',
identifier: 123,
attributes: attributes,
secret: secret
});
var decoded = radius.decode({ packet: packet, secret: secret });
test.equal( decoded.code, 'Access-Request' );
test.equal( decoded.identifier, 123 );
var expected_attrs = {
'User-Name': 'ornithopter-aliptic',
'User-Password': 'nucleohistone-overwilily',
'Service-Type': 'Login-User',
'NAS-IP-Address': '169.134.68.136',
'Vendor-Specific': {
'Aruba-User-Role': 'cracked-tylote',
'Aruba-User-Vlan': 825,
'Aruba-Essid-Name': 'phene-dentinalgia'
}
};
test.deepEqual( decoded.attributes, expected_attrs );
test.done();
},
test_decode_hash_attributes: function(test) {
var attrs = {
'User-Name': 'ornithopter-aliptic',
'User-Password': 'nucleohistone-overwilily',
'Service-Type': 'Login-User',
'NAS-IP-Address': '169.134.68.136'
};
var packet = radius.encode({
code: 'Access-Request',
identifier: 123,
attributes: attrs,
secret: secret
});
var decoded = radius.decode({ packet: packet, secret: secret });
test.equal( decoded.code, 'Access-Request' );
test.equal( decoded.identifier, 123 );
test.deepEqual( decoded.attributes, attrs );
test.done();
},
test_throws_on_nested_hash_attributes: function(test) {
var attrs = {
'User-Name': 'ornithopter-aliptic',
'User-Password': 'nucleohistone-overwilily',
'Service-Type': 'Login-User',
'NAS-IP-Address': '169.134.68.136',
'Vendor-Specific': {
'Aruba-User-Role': 'cracked-tylote',
'Aruba-User-Vlan': 825,
'Aruba-Essid-Name': 'phene-dentinalgia'
}
};
test.throws(function() {
var packet = radius.encode({
code: 'Access-Request',
identifier: 123,
attributes: attrs,
secret: secret
});
});
test.done();
},
// test that our encoded packet matches bit-for-bit with a "real"
// RADIUS packet
test_encode_bit_for_bit: function(test) {
var raw_packet = fs.readFileSync(__dirname + '/captures/aruba_mac_auth.packet');
radius.load_dictionary(__dirname + '/dictionaries/dictionary.aruba');
var encoded = radius.encode({
code: 'Access-Request',
identifier: 58,
authenticator: new Buffer('4a45fae086d9e114286b37b5f371ec6c', 'hex'),
attributes: [
['NAS-IP-Address', '10.0.0.90'],
['NAS-Port', 0],
['NAS-Port-Type', 'Wireless-802.11'],
['User-Name', '7c:c5:37:ff:f8:af'],
['User-Password', '7c:c5:37:ff:f8:af'],
['Calling-Station-Id', '7CC537FFF8AF'],
['Called-Station-Id', '000B86F02068'],
['Service-Type', 'Login-User'],
['Vendor-Specific', 14823, [['Aruba-Essid-Name', 'muir-aruba-guest']]],
['Vendor-Specific', 14823, [['Aruba-Location-Id', '00:1a:1e:c6:b0:ca']]],
['Vendor-Specific', 14823, [['Aruba-AP-Group', 'cloud-cp']]]
],
secret: secret,
add_message_authenticator: true
});
test.equal( encoded.toString('hex'), raw_packet.toString('hex') );
test.done();
},
// encode will choose a random identifier for you if you don't provide one
test_encode_random_identifer: function(test) {
var decoded = radius.decode({
packet: radius.encode({
code: 'Access-Request',
secret: secret
}),
secret: secret
});
test.ok( decoded.identifier >= 0 && decoded.identifier < 256 );
var starting_id = decoded.identifier;
// if you are unlucky this is an infinite loop
while (true) {
decoded = radius.decode({
packet: radius.encode({
code: 'Access-Request',
secret: secret
}),
secret: secret
});
if (decoded.identifier != starting_id)
break;
}
test.ok( true );
test.done();
},
// given a previously decoded packet, prepare a response packet
test_packet_response: function(test) {
var raw_packet = fs.readFileSync(__dirname + '/captures/cisco_mac_auth.packet');
var decoded = radius.decode({ packet: raw_packet, secret: secret });
var response = radius.encode_response({
packet: decoded,
code: 'Access-Reject',
secret: secret
});
var raw_response = fs.readFileSync(__dirname + '/captures/cisco_mac_auth_reject.packet');
test.equal( response.toString('hex'), raw_response.toString('hex') );
test.done();
},
// response needs to include proxy state
test_response_include_proxy_state: function(test) {
var request_with_proxy = radius.decode({
packet: radius.encode({
code: 'Access-Request',
secret: secret,
attributes: [
['User-Name', 'ascribe-despairer'],
['Proxy-State', new Buffer('womanhouse-Pseudotsuga')],
['User-Password', 'ridiculous'],
['Proxy-State', new Buffer('regretfully-unstability')]
]
}),
secret: secret
});
var decoded_response = radius.decode({
packet: radius.encode_response({
packet: request_with_proxy,
code: 'Access-Reject',
secret: secret
}),
secret: secret
});
var expected_raw_attributes = [
[radius.attr_name_to_id('Proxy-State'), new Buffer('womanhouse-Pseudotsuga')],
[radius.attr_name_to_id('Proxy-State'), new Buffer('regretfully-unstability')]
];
test.deepEqual( decoded_response.raw_attributes, expected_raw_attributes );
test.done();
},
// dont accidentally strip null bytes when encoding
test_password_encode: function(test) {
var decoded = radius.decode({
packet: radius.encode({
code: 'Access-Request',
authenticator: new Buffer('426edca213c1bf6e005e90a64105ca3a', 'hex'),
attributes: [['User-Password', 'ridiculous']],
secret: secret
}),
secret: secret
});
test.equal( decoded.attributes['User-Password'], 'ridiculous' );
test.done();
},
accounting_group: {
setUp: function(cb) {
radius.load_dictionary(__dirname + '/dictionaries/dictionary.airespace');
test_args = {};
test_args.raw_acct_request = fs.readFileSync(__dirname + '/captures/cisco_accounting.packet');
test_args.expected_acct_attrs = {
'User-Name': 'user_7C:C5:37:FF:F8:AF_134',
'NAS-Port': 1,
'NAS-IP-Address': '10.0.3.4',
'Framed-IP-Address': '10.2.0.252',
'NAS-Identifier': 'Cisco 4400 (Anchor)',
'Vendor-Specific': {
'Airespace-Wlan-Id': 2
},
'Acct-Session-Id': '4fecc41e/7c:c5:37:ff:f8:af/9',
'Acct-Authentic': 'RADIUS',
'Tunnel-Type': [0x00, 'VLAN'],
'Tunnel-Medium-Type': [0x00, 'IEEE-802'],
'Tunnel-Private-Group-Id': 5,
'Acct-Status-Type': 'Start',
'Calling-Station-Id': '7c:c5:37:ff:f8:af',
'Called-Station-Id': '00:22:55:90:39:60'
};
cb();
},
test_accounting: function(test) {
var raw_acct_request = test_args.raw_acct_request;
var decoded = radius.decode({ packet: raw_acct_request, secret: secret });
var expected_attrs = test_args.expected_acct_attrs;
test.deepEqual( decoded.attributes, expected_attrs );
// test we can encode the same packet
var encoded = radius.encode({
code: 'Accounting-Request',
identifier: decoded.identifier,
secret: secret,
attributes: [
['User-Name', 'user_7C:C5:37:FF:F8:AF_134'],
['NAS-Port', 1],
['NAS-IP-Address', '10.0.3.4'],
['Framed-IP-Address', '10.2.0.252'],
['NAS-Identifier', 'Cisco 4400 (Anchor)'],
['Vendor-Specific', 'Airespace', [['Airespace-Wlan-Id', 2]]],
['Acct-Session-Id', '4fecc41e/7c:c5:37:ff:f8:af/9'],
['Acct-Authentic', 'RADIUS'],
['Tunnel-Type', 0x00, 'VLAN'],
['Tunnel-Medium-Type', 0x00, 'IEEE-802'],
['Tunnel-Private-Group-Id', '5'],
['Acct-Status-Type', 'Start'],
['Calling-Station-Id', '7c:c5:37:ff:f8:af'],
['Called-Station-Id', '00:22:55:90:39:60']
]
});
test.equal( encoded.toString('hex'), raw_acct_request.toString('hex') );
var raw_acct_response = fs.readFileSync(__dirname +
'/captures/cisco_accounting_response.packet');
encoded = radius.encode_response({
packet: decoded,
secret: secret,
code: 'Accounting-Response'
});
test.equal( encoded.toString('hex'), raw_acct_response.toString('hex') );
test.done();
},
test_invalid_accounting_packet_authenticator: function(test) {
var raw_acct_request = test_args.raw_acct_request;
var expected_attrs = test_args.expected_acct_attrs;
// detect invalid accounting packets
test.throws( function() {
radius.decode({ packet: raw_acct_request, secret: 'not-secret' });
} );
try {
radius.decode({ packet: raw_acct_request, secret: 'not-secret' });
} catch (err) {
test.deepEqual( err.decoded.attributes, expected_attrs );
}
test.done();
}
},
test_no_empty_strings: function(test) {
var decoded = radius.decode({
secret: secret,
packet: radius.encode({
code: 'Access-Request',
attributes: [['User-Name', '']],
secret: secret
})
});
// don't send empty strings (see RFC2865)
test.deepEqual( decoded.attributes, {} );
test.done();
},
test_repeated_attribute: function(test) {
var decoded = radius.decode({
secret: secret,
packet: radius.encode({
secret: secret,
code: 'Access-Reject',
attributes: [
['Reply-Message', 'message one'],
['Reply-Message', 'message two']
]
})
});
var expected_attrs = {
'Reply-Message': ['message one', 'message two']
};
test.deepEqual( decoded.attributes, expected_attrs );
test.done();
},
test_dictionary_include: function(test) {
radius.unload_dictionaries();
radius.add_dictionary(__dirname + '/dictionaries/dictionary.test1');
var decoded = radius.decode({
secret: secret,
packet: radius.encode({
secret: secret,
code: 'Access-Request',
attributes: [['Attribute-Test1', 'foo'], ['Attribute-Test2', 'bar']]
})
});
var expected_attrs = {
'Attribute-Test1': 'foo',
'Attribute-Test2': 'bar'
};
test.deepEqual( decoded.attributes, expected_attrs );
test.done();
},
// make sure we can load the dicts in any order
test_dictionary_out_of_order: function(test) {
var dicts = fs.readdirSync(__dirname + '/../dictionaries');
// make sure we can load any dictionary first
for (var i = 0; i < dicts.length; i++) {
radius.unload_dictionaries();
radius.load_dictionary(__dirname + '/../dictionaries/' + dicts[i]);
}
// and spot check things actually work loaded out of order
radius.unload_dictionaries();
radius.load_dictionary(__dirname + '/../dictionaries/dictionary.rfc2867');
radius.load_dictionary(__dirname + '/../dictionaries/dictionary.rfc2866');
var decoded = radius.decode({
secret: secret,
packet: radius.encode({
code: 'Accounting-Request',
secret: secret,
attributes: [
['Acct-Status-Type', 'Tunnel-Reject']
]
})
});
test.equal( decoded.attributes['Acct-Status-Type'], 'Tunnel-Reject' );
radius.unload_dictionaries();
radius.load_dictionary(__dirname + '/dictionaries/dictionary.test_tunnel_type');
radius.load_dictionaries();
decoded = radius.decode({
secret: secret,
packet: radius.encode({
code: 'Accounting-Request',
secret: secret,
attributes: [
['Tunnel-Type', 0x00, 'TESTTUNNEL']
]
})
});
var expected_attrs = {'Tunnel-Type': [0x00, 'TESTTUNNEL']};
test.deepEqual( decoded.attributes, expected_attrs );
test.done();
},
test_zero_identifer: function(test) {
var decoded = radius.decode({
packet: radius.encode({
secret: secret,
code: 'Access-Request',
identifier: 0
}),
secret: secret
});
test.equal( decoded.identifier, 0 );
test.done();
},
test_date_type: function(test) {
var raw_packet = fs.readFileSync(__dirname + '/captures/motorola_accounting.packet');
var decoded = radius.decode({
packet: raw_packet,
secret: secret
});
var epoch = 1349879753;
test.equal( decoded.attributes['Event-Timestamp'].getTime(), epoch * 1000 );
var encoded = radius.encode({
code: 'Accounting-Request',
identifier: decoded.identifier,
attributes: [
['User-Name', '00-1F-3B-8C-3A-15'],
['Acct-Status-Type', 'Start'],
['Acct-Session-Id', '1970D5A4-001F3B8C3A15-0000000001'],
['Calling-Station-Id', '00-1F-3B-8C-3A-15'],
['Called-Station-Id', 'B4-C7-99-77-59-D0:muir-moto-guest-site1'],
['NAS-Port', 1],
['NAS-Port-Type', 'Wireless-802.11'],
['NAS-IP-Address', '10.2.0.3'],
['NAS-Identifier', 'ap6532-70D5A4'],
['NAS-Port-Id', 'radio2'],
['Event-Timestamp', new Date(epoch * 1000)],
['Tunnel-Type', 0x00, 'VLAN' ],
['Tunnel-Medium-Type', 0x00, 'IEEE-802'],
['Tunnel-Private-Group-Id', '30'],
['Acct-Authentic', 'RADIUS']
],
secret: secret
});
test.equal( encoded.toString('hex'), raw_packet.toString('hex') );
test.done();
},
test_date_type_non_mult_1000_ms: function(test) {
var encoded;
test.doesNotThrow(function() {
encoded = radius.encode({
code: 'Accounting-Request',
identifier: 123,
attributes: [
['Event-Timestamp', new Date(1403025894009)]
],
secret: secret
});
});
// truncates ms
var decoded = radius.decode({ packet: encoded, secret: secret });
test.equal( decoded.attributes['Event-Timestamp'].getTime(), 1403025894000 );
test.done();
},
test_disconnect_request: function(test) {
var encoded = radius.encode({
code: 'Disconnect-Request',
identifier: 54,
secret: secret,
attributes: [
['User-Name', 'mariticide-inquietation'],
['NAS-Identifier', 'Aglauros-charioted']
]
});
// check we did the non-user-password authenticator
var got_authenticator = new Buffer(16);
encoded.copy(got_authenticator, 0, 4);
encoded.fill(0, 4, 20);
var expected_authenticator = new Buffer(16);
var hasher = crypto.createHash("md5");
hasher.update(encoded);
hasher.update(secret);
expected_authenticator.write(hasher.digest("binary"), 0, 16, "binary");
test.equal( got_authenticator.toString('hex'), expected_authenticator.toString('hex') );
// and make sure we check the authenticator when decoding
test.throws(function() {
radius.decode({
packet: encoded,
secret: secret
});
});
expected_authenticator.copy(encoded, 4, 0);
test.doesNotThrow(function() {
radius.decode({
packet: encoded,
secret: secret
});
});
test.done();
},
test_verify_response: function(test) {
var request = radius.encode({
secret: secret,
code: 'Accounting-Request',
attributes: {
'User-Name': '00-1F-3B-8C-3A-15',
'Acct-Status-Type': 'Start'
}
});
var response = radius.encode_response({
secret: secret,
code: 'Accounting-Response',
packet: radius.decode({ packet: request, secret: secret })
});
test.ok( radius.verify_response({
request: request,
response: response,
secret: secret
}) );
test.ok( !radius.verify_response({
request: request,
response: response,
secret: "Calliopsis-misbeholden"
}) );
// response encoded with wrong secret
response = radius.encode_response({
secret: "moyenne-paraboliform",
code: 'Accounting-Response',
packet: radius.decode({ packet: request, secret: secret })
});
test.ok( !radius.verify_response({
request: request,
response: response,
secret: secret
}) );
test.done();
},
test_server_request: function(test) {
var encoded1 = radius.encode({
code: 'Status-Server',
identifier: 54,
secret: secret,
attributes: [
['NAS-Identifier', 'symphilism-dicentrine']
]
});
var encoded2 = radius.encode({
code: 'Status-Server',
identifier: 54,
secret: secret,
attributes: [
['NAS-Identifier', 'symphilism-dicentrine']
]
});
// check we are doing a random authenticator
var got_authenticator1 = new Buffer(16);
encoded1.copy(got_authenticator1, 0, 4);
var got_authenticator2 = new Buffer(16);
encoded2.copy(got_authenticator2, 0, 4);
test.notEqual( got_authenticator1.toString(), got_authenticator2.toString() );
var response = radius.encode_response({
code: 'Access-Accept',
secret: secret,
packet: radius.decode({packet: encoded1, secret: secret})
});
test.ok( radius.verify_response({
request: encoded1,
response: response,
secret: secret
}) );
test.done();
},
test_vendor_names_with_numbers: function(test) {
radius.load_dictionary(__dirname + '/dictionaries/dictionary.number_vendor_name');
var encoded = radius.encode({
code: "Access-Request",
secret: secret,
attributes: [
['Vendor-Specific', '123Foo', [
['1Integer', 478],
['1String', 'Zollernia-fibrovasal'],
['12345', 'myrmecophagoid-harn']
]]
]
});
var decoded = radius.decode({
packet: encoded,
secret: secret
});
test.equal( radius.vendor_name_to_id('123Foo'), 995486 );
test.deepEqual( decoded.attributes, {
'Vendor-Specific': {
'1Integer': 478,
'1String': 'Zollernia-fibrovasal',
'12345': 'myrmecophagoid-harn'
}
} );
test.done();
},
message_authenticator_group: {
setUp: function(cb) {
secret = "testing123";
test_args = {
raw_request: fs.readFileSync(__dirname + '/captures/eap_request.packet')
};
test_args.parsed_request = radius.decode({
packet: test_args.raw_request,
secret: secret
});
cb();
},
// make sure we calculate the same Message-Authenticator
test_calculate: function(test) {
var attrs_without_ma = test_args.parsed_request.raw_attributes.filter(function(a) {
return a[0] != radius.attr_name_to_id('Message-Authenticator');
});
var encoded = radius.encode({
code: test_args.parsed_request.code,
identifier: test_args.parsed_request.identifier,
authenticator: test_args.parsed_request.authenticator,
attributes: attrs_without_ma,
secret: secret
});
test.equal( encoded.toString('hex'), test_args.raw_request.toString('hex') );
test.done();
},
// encode_response should calculate the appropriate Message-Authenticator
test_encode_response: function(test) {
var response = radius.encode_response({
code: "Access-Accept",
secret: secret,
packet: test_args.parsed_request
});
var parsed_response = radius.decode({
packet: response,
secret: secret
});
// calculate expected Message-Authenticator
var empty = new Buffer(16);
empty.fill(0);
var expected_response = radius.encode({
code: "Access-Accept",
identifier: test_args.parsed_request.identifier,
authenticator: test_args.parsed_request.authenticator,
attributes: [["Message-Authenticator", empty]],
secret: secret
});
// expected_response's authenticator is correct, but Message-Authenticator is wrong
// (it's all 0s). make sure verify_response checks both
test.ok( !radius.verify_response({
request: test_args.raw_request,
response: expected_response,
secret: secret
}) );
// put back the request's authenticator
test_args.parsed_request.authenticator.copy(expected_response, 4);
var expected_ma = radius.calculate_message_authenticator(expected_response, secret);
test.equal(
parsed_response.attributes["Message-Authenticator"].toString("hex"),
expected_ma.toString("hex")
);
test.ok( radius.verify_response({
request: test_args.raw_request,
response: response,
secret: secret
}) );
test.done();
},
// response is missing Message-Authenticator, not okay
test_response_missing_ma: function(test) {
var bad_response = radius.encode({
code: "Access-Accept",
identifier: test_args.parsed_request.identifier,
authenticator: test_args.parsed_request.authenticator,
attributes: [],
secret: secret
});
test.ok( !radius.verify_response({
request: test_args.raw_request,
response: bad_response,
secret: secret
}) );
test.done();
},
// make sure we verify Message-Authenticator when decoding requests
test_decode_verify: function(test) {
test.throws(function() {
radius.decode({
packet: test_args.raw_request,
secret: 'wrong secret'
});
});
test.done();
}
},
test_utf8_strings: function(test) {
var encoded = radius.encode({
secret: "密码",
code: "Access-Request",
attributes: {
"User-Name": "金庸先生",
"User-Password": "降龙十八掌"
}
});
var decoded = radius.decode({
packet: encoded,
secret: "密码"
});
test.deepEqual( {
"User-Name": "金庸先生",
"User-Password": "降龙十八掌"
}, decoded.attributes );
test.done();
},
test_invalid_packet_attribute_length: function(test) {
var invalid_packet = fs.readFileSync(__dirname + '/captures/invalid_register.packet');
var raw_packet = fs.readFileSync(__dirname + '/captures/aruba_mac_auth.packet');
// should fail decode packet attributes
test.throws(function() {
radius.decode_without_secret({ packet: invalid_packet });
} );
// should decode packet attributes
test.doesNotThrow(function() {
radius.decode_without_secret({ packet: raw_packet });
});
test.done();
},
test_tag_fields: function(test) {
var decoded = radius.decode({
secret: secret,
packet: radius.encode({
code: 'Accounting-Request',
secret: secret,
attributes: [
['Tunnel-Type', 0x01, 'VLAN'],
['User-Name', 'honeymooner-hitched'],
]
})
});
test.deepEqual( {
'Tunnel-Type': [ 1, 'VLAN'],
'User-Name': 'honeymooner-hitched'
}, decoded.attributes );
test.done();
}
});

24
code/package-lock.json generated Normal file
View File

@ -0,0 +1,24 @@
{
"name": "netradius",
"version": "0.0.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "netradius",
"version": "0.0.1",
"license": "ISC",
"dependencies": {
"radius": "^1.1.4"
}
},
"node_modules/radius": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/radius/-/radius-1.1.4.tgz",
"integrity": "sha512-UWuzdF6xf3NpsXFZZmUEkxtEalDXj8hdmMXgbGzn7vOk6zXNsiIY2I6SJ1euHt7PTQuMoz2qDEJB+AfJDJgQYw==",
"engines": {
"node": ">=0.8.0"
}
}
}
}

20
code/package.json Normal file
View File

@ -0,0 +1,20 @@
{
"name": "netradius",
"version": "0.0.1",
"description": "Simple network RADIUS server",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"network",
"wifi",
"authentication",
"radius"
],
"author": "Alex Rennie-Lis",
"license": "ISC",
"dependencies": {
"radius": "^1.1.4"
}
}

10
config/config.json Normal file
View File

@ -0,0 +1,10 @@
{
"ports": {
"radius_authentication": 1812,
"radius_accounting": 1813
},
"client_secret": "password",
"storage": "json:./data.json",
"default_vlan_enabled": true,
"default_vlan_id": 90
}

9
data.json Normal file
View File

@ -0,0 +1,9 @@
{
"users": [
{
"username": "2e309a1d6db8",
"password": "2e309a1d6db8",
"vlan": 10
}
]
}