Giter VIP home page Giter VIP logo

node-tacacs-plus's Introduction

tacacs-plus [TACACS+]

NPM

This is a simple TACACS+ library to help with basic encoding and decoding of TACACS+ authentication and authorization packets.

More information on TACACS+ can be found here, https://tools.ietf.org/html/draft-ietf-opsawg-tacacs-05.

Basic Usage

var tacacs = require('tacacs-plus');

// receive or send raw TCP packet (port 49) to a TACACS+ server or client

var decoded = tacacs.decodePacket({ packet: raw_data, secret: 'your_key' });

The decoded object, depending on the sequence of packets, should be something along the lines of this.

{
    "header": {
        "majorVersion": 12,
        "minorVersion": 0,
        "versionByte": 193,
        "type": 1,
        "sequenceNumber": 1,
        "flags": 1,
        "is_encrypted": false,
        "is_singleConnect": false,
        "sessionId": 1,
        "length": 34
    },
    "rawData": {
        "type": "Buffer",
        "data": [ ... ]
    },
    "data": {
        "action": 1,
        "privLvl": 0,
        "authenType": 0,
        "authenService": 0,
        "userLen": 9,
        "portLen": 4,
        "remAddrLen": 13,
        "dataLen": 0,
        "user": "your_user_name",
        "port": "tty10",
        "remAddr": "your_location"
    }
}

In certain instances, the data element may not be populated if there is an issue with the type of messages or the sequence number. You can manually decode a message body using the decode functions in the library.


Creating a Simple Auth Start

If you are creating a client, to create a simple auth start to send to a server, simply do something along the lines of the following code snippit.

var tacacs = require('tacacs-plus');

// create the auth start body
var authStart = tacacs.createAuthStart({
    action: tacacs.TAC_PLUS_AUTHEN_LOGIN,
    privLvl: tacacs.TAC_PLUS_PRIV_LVL_MAX,
    authenAype: tacacs.TAC_PLUS_AUTHEN_TYPE_ASCII,
    authenAervice: tacacs.TAC_PLUS_AUTHEN_SVC_LOGIN,
    user: 'your_user_name',
    port: 'tty10',
    remAddr: 'your_location',
    data: null
});

// create the tacacs+ header
var header = tacacs.createHeader({
    majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
    minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
    type: tacacs.TAC_PLUS_AUTHEN,
    sequenceNumber: 0x1,
    flags: tacacs.TAC_PLUS_UNENCRYPTED_FLAG,
    sessionId: 0x1,
    length: authStart.length
});

// combine the header and body
var authStartPacket = Buffer.concat([header, authStart]);

// open a connection and send the raw packet via TCP to the server (this example is not using encryption)
  • All decode processes take Buffers that are then converted to objects.
  • All create processes take objects and return Buffers of data.

Encryption

You can use the encodeByteData and decodeByteData functions to encrypt and decrypt data packets.

Using encryption requires a shared secret key as well as cryptographically secure random Session ID values.

var crypto = require('crypto');
var tacacs = require('tacacs-plus');

// Generate a random 32-bit session
var sessionIdBytes = crypto.randomBytes(4);
var sessionId = Math.abs(sessionIdBytes.readInt32BE(0));

// create the auth start body
var authStart = tacacs.createAuthStart({
    action: tacacs.TAC_PLUS_AUTHEN_LOGIN,
    privLvl: tacacs.TAC_PLUS_PRIV_LVL_MAX,
    authenType: tacacs.TAC_PLUS_AUTHEN_TYPE_ASCII,
    authenService: tacacs.TAC_PLUS_AUTHEN_SVC_LOGIN,
    user: 'your_user_name',
    port: 'tty10',
    remAddr: 'your_location',
    data: null
});

var version = tacacs.createVersion(tacacs.TAC_PLUS_MAJOR_VER, tacacs.TAC_PLUS_MINOR_VER_DEFAULT);
var sequenceNumber = 1;
var encryptedAuthStart = tacacs.encodeByteData(sessionId, 'your_key', version, sequenceNumber, authStart);

// create the tacacs+ header
var headerOptions = {
    majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
    minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
    type: tacacs.TAC_PLUS_AUTHEN,
    sequenceNumber: sequenceNumber,
    flags: 0x0, // setting this to zero assumes encryption is being used
    sessionId: sessionId,
    length: authStart.length
}
var header = tacacs.createHeader(headerOptions);

var packetToSend = Buffer.concat([header, encryptedAuthStart]);

// open a connection and send the packet via TCP to the server

Sample Communications

Here is a very simple client that sends a auth start packet to a server, then the server responds to the client... this is a very simple "getting started" sample, that requires a lot more development to implement a full workflow, but it illustrates how to start.

For a more complete client example, see examples/client.js.

var crypto = require('crypto');
var tacacs = require('tacacs-plus');

// SAMPLE SERVER

var server = net.createServer(function (c) {
    console.log('Server: Connection opened.');

    c.on('error', function (err) {
        console.log(err);
    });

    c.on('end', function () {
        console.log('Server: Connection closed.');
    });

    c.on('data', function (data) {
        var replyOptions = {};
        var replyHeader = {
            majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
            minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
            type: tacacs.TAC_PLUS_AUTHEN,
            sequenceNumber: 1,
            flags: 0x0,
            sessionId: 0x0,
            length: 0x0
        };

        console.log('Server: Received ', (data ? data.length : 0), ' bytes.');
        console.log('Server: ' + data.toString('hex'));

        var decodedPacket = tacacs.decodePacket({ packet: data, key: 'your_key' });

        console.log('Server: Decoded TACACS+ request.');
        console.log(JSON.stringify(decodedPacket));

        decodedPacket.header = decodedPacket.header || {};

        var replyHeader = decodedPacket.header;
        replyHeader.sequenceNumber++;
        replyHeader.sessionId = decodedPacket.header.sessionId;

        // build the auth reply (this is all the server should ever send for auth)
        // in this example we will send a get password command (TAC_PLUS_AUTHEN_STATUS_GETPASS)
        var replyOptions = {
            status: tacacs.TAC_PLUS_AUTHEN_STATUS_GETPASS,
            flags: 0x0,
            message: 'Please enter your password: ',
            data: null
        };
        var replyBytes = tacacs.createAuthReply(replyOptions);

        replyHeader.length = replyBytes.length;
        var headerBytes = tacacs.createHeader(replyHeader);
        var encryptedResponse = tacacs.encodeByteData(replyHeader.sessionId, 'your_key', tacacs.createVersion(replyHeader.majorVersion, replyHeader.minorVersion), replyHeader.sequenceNumber, replyBytes);

        replyBytes = Buffer.concat([headerBytes, encryptedResponse]);
        c.write(replyBytes);
    });
});

server.on('error', function (err) {
    console.log('Server: ' + err);
});

server.listen({ port: 49 }, function () {
    console.log('Server: listening...');
});


// SIMPLE CLIENT

var client = net.connect(49, '127.0.0.1', function () {
    console.log('Client connected!');

    // now that we've connected, send the first auth packet

    var sessionIdBytes = crypto.randomBytes(4);
    var sessionId = Math.abs(sessionIdBytes.readInt32BE(0));

    // create the auth start body
    var authStart = tacacs.createAuthStart({
        action: tacacs.TAC_PLUS_AUTHEN_LOGIN,
        privLvl: tacacs.TAC_PLUS_PRIV_LVL_MAX,
        authenType: tacacs.TAC_PLUS_AUTHEN_TYPE_ASCII,
        authenService: tacacs.TAC_PLUS_AUTHEN_SVC_LOGIN,
        user: 'your_user_name',
        port: 'tty10',
        remAddr: 'your_location',
        data: null
    });

    var version = tacacs.createVersion(tacacs.TAC_PLUS_MAJOR_VER, tacacs.TAC_PLUS_MINOR_VER_DEFAULT);
    var sequenceNumber = 1;
    var encryptedAuthStart = tacacs.encodeByteData(sessionId, 'your_key', version, sequenceNumber, authStart);

    // create the tacacs+ header
    var headerOptions = {
        majorVersion: tacacs.TAC_PLUS_MAJOR_VER,
        minorVersion: tacacs.TAC_PLUS_MINOR_VER_DEFAULT,
        type: tacacs.TAC_PLUS_AUTHEN,
        sequenceNumber: sequenceNumber,
        flags: 0x0, // setting this to zero assumes encryption is being used
        sessionId: sessionId,
        length: authStart.length
    }
    var header = tacacs.createHeader(headerOptions);

    var packetToSend = Buffer.concat([header, encryptedAuthStart]);

    // send the auth start packet to the server
    console.log('Client: Sending: ' + packetToSend.length + ' bytes.');
    console.log('Client: ' + packetToSend.toString('hex'));
    client.write(packetToSend);
});

client.on('error', function (err) { console.log(err); });
client.on('data', function (data) {
    if (data) {
        console.log('Client: Received Data: ' + data.toString('hex'));
        // decode response
        var resp = tacacs.decodePacket({ packet: data, key: 'your_key' });
        console.log('Client: Decoded Response: ' + JSON.stringify(resp, null, 2));
    }
    else {
        console.log('Client: No data!');
    }
});

Authorization

simple authorization request and responses can also be created by using the createAuthorizationRequest and createAuthorizationResponse and their associated decode processes.

const tacacs = require('tacacs-plus');


var authorReq = tacacs.createAuthorizationRequest({
    authenMethod: tacacs.TAC_PLUS_AUTHEN_METH_NOT_SET,
    privLvl: tacacs.TAC_PLUS_PRIV_LVL_USER,
    authenType: tacacs.TAC_PLUS_AUTHEN_TYPE_ASCII,
    authenService: tacacs.TAC_PLUS_AUTHEN_TYPE_NOT_SET,
    user: 'user',
    port: 'port',
    remAddr: 'rem addr',
    args: ['test=123', 'test1=456']
});

console.log('Author Request: ' + authorReq.toString('hex'));

var decodedReq = tacacs.decodeAuthorizationRequest(authorReq);

console.log('Author Request: ' + JSON.stringify(decodedReq));

console.log('---------');

var authorResp = tacacs.createAuthorizationResponse({
    status: tacacs.TAC_PLUS_AUTHOR_STATUS_ERROR,
    args: ['test=123', 'test1=456'],
    serverMessage: 'Test Message',
    data: 'Test Data'
});

console.log('Author Response: ' + authorResp.toString('hex'));

var decodedResp = tacacs.decodeAuthorizationResponse(authorResp);

console.log('Author Response: ' + JSON.stringify(decodedResp));

Testing Server

A good and easy to spin up testing server is tac_plus running in a docker container. If you have docker setup, simply run the following to start the tac_plus container. More information is available here, https://hub.docker.com/r/dchidell/docker-tacacs.

sudo docker run -it --rm -p 49:49 dchidell/docker-tacacs

Then you can point your client to the docker server IP on port 49 and use the shared key 'ciscotacacskey' and the user 'iosuser' with the password 'cisco'.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.