radigo

package module
v0.0.0-...-491c899 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2024 License: MIT Imports: 24 Imported by: 1

README

RADiGo

RADIUS library for Go language.

Provides both Client and Server functionality, both asynchronous and thread safe.

Support for both UDP and TCP as transports.

Support for Vendor Specific Attributes.

Support for client based secret and dictionaries.

Sample usage code

package main

import (
	"log"
	"strings"

	. "github.com/cgrates/radigo"
)

var sampleDict = `
## Sample dictionary file containing few lines of Cisco vendor

# Vendors
VENDOR    Cisco    9

BEGIN-VENDOR    Cisco
ATTRIBUTE       Cisco-AVPair    1   string
ATTRIBUTE       Cisco-NAS-Port  2	string
END-VENDOR      Cisco
`

func handleAuth(req *Packet) (rpl *Packet, err error) {
	rpl = req.Reply()
	rpl.Code = AccessAccept
	return
}

func handleAcct(req *Packet) (rpl *Packet, err error) {
	rpl = req.Reply()
	rpl.Code = AccountingResponse
	return
}

func main() {
	// Centralized secrets and dictionaries
	dict := RFC2865Dictionary()

	if err := dict.ParseFromReader(strings.NewReader(sampleDict)); err != nil {
		log.Fatalln(err)
	}

	secrets := NewSecrets(map[string]string{"127.0.0.1": "CGRateS.org"})
	dicts := NewDictionaries(map[string]*Dictionary{"127.0.0.1": dict})

	stopCh := make(chan struct{})
	// Start RADIUS AUTH Server
	go NewServer("tcp", "localhost:1812",
		secrets, dicts,
		map[PacketCode]func(*Packet) (*Packet, error){AccessRequest: handleAuth}, nil).ListenAndServe(stopCh)

	// Start RADIUS ACCT Server
	go NewServer("tcp", "localhost:1813",
		secrets, dicts,
		map[PacketCode]func(*Packet) (*Packet, error){AccountingRequest: handleAcct}, nil).ListenAndServe(stopCh)

	// Connect Auth client:
	authClnt, err := NewClient("tcp", "localhost:1812", "CGRateS.org", dict, 0, nil)
	if err != nil {
		log.Fatalf("Could not connect to RAD-AUTH server, error: %s", err.Error())
	}

	req := authClnt.NewRequest(AccessRequest, 1)
	if err := req.AddAVPWithName("User-Name", "flopsy", ""); err != nil {
		log.Fatalln(err)
	}
	if err := req.AddAVPWithName("Cisco-NAS-Port", "CGR1", "Cisco"); err != nil {
		log.Fatalln(err)
	}
	if reply, err := authClnt.SendRequest(req); err != nil {
		log.Println(err)
	} else {
		log.Printf("Received reply: %+v", reply.Code)
	}
	close(stopCh)
}

Build Status

Documentation

Index

Constants

View Source
const (
	AttributeKeyword   = "ATTRIBUTE"
	ValueKeyword       = "VALUE"
	VendorKeyword      = "VENDOR"
	BeginVendorKeyword = "BEGIN-VENDOR"
	EndVendorKeyword   = "END-VENDOR"
	IncludeFileKeyword = "$INCLUDE"
	// rfc2865 value Formats
	TextValue    = "text"
	StringValue  = "string"
	AddressValue = "address"
	IntegerValue = "integer"
	TimeValue    = "time"
	IPAddrValue  = "ipaddr"
	UnknownValue = "unknown"
	// other value formats
	OctetsValue = "octets"
)
View Source
const (
	MetaDefault  = "*default" // default client
	MaxPacketLen = 4095
)
View Source
const (
	UNLIMITED = -1
)

Variables

View Source
var (
	ErrNotImplemented = errors.New("not implemented")
)
View Source
var ErrUnsupportedAttributeType = errors.New("unsupported attribute type")
View Source
var RFC2865Dict = `` /* 2264-byte string literal not displayed */

Functions

func AuthenticateCHAP

func AuthenticateCHAP(password, authenticator, chapChallenge []byte) bool

AuthenticateCHAP receive the password as plaintext and verify against the chap challenge

func ChallengeHash

func ChallengeHash(peerChallenge, authenticatorChallenge []byte, username string) []byte

ChallengeHash - rfc2759, 8.2

func ChallengeResponse

func ChallengeResponse(challenge, passwordHash []byte) []byte

ChallengeResponse - rfc2759, 8.5

func DESCrypt

func DESCrypt(key, clear []byte) []byte

DESCrypt - rfc2759, 8.6

func DecodeUserPassword

func DecodeUserPassword(p *Packet, a *AVP) error

func EncodeCHAPPassword

func EncodeCHAPPassword(password, authenticator []byte) []byte

EncodeCHAPPassword is used in test to encode CHAP-Password raw value

func EncodeUserPassword

func EncodeUserPassword(plaintext, secret, requestAuthenticator []byte) []byte

EncodeUserPassword encodes the plaintext, where plaintext's length needs to be greater than 16 and a multiple of 16.

func GenerateAuthenticatorResponse

func GenerateAuthenticatorResponse(authenticatorChallenge, peerChallenge, ntResponse []byte, username, password string) (string, error)

GenerateAuthenticatorResponse - rfc2759, 8.7

func GenerateClientMSCHAPResponse

func GenerateClientMSCHAPResponse(authenticator [16]byte, userName, password string) ([]byte, error)

func GenerateNTResponse

func GenerateNTResponse(authenticatorChallenge, peerChallenge []byte, username, password string) ([]byte, error)

GenerateNTResponse - rfc2759, 8.1

func HashPassword

func HashPassword(password []byte) []byte

HashPassword with MD4 - rfc2759, 8.3

func ToUTF16

func ToUTF16(in string) ([]byte, error)

ToUTF16 takes an ASCII string and turns it into a UCS-2 / UTF-16 representation

Types

type AVP

type AVP struct {
	Number      uint8       // attribute number
	Name        string      // attribute name
	Type        string      // type of the value helping us to convert to concrete
	RawValue    []byte      // original value as byte
	Value       interface{} // holds the concrete value defined in dictionary, extracted back with type (eg: avp.Value.(string) or avp.Value.(*VSA))
	StringValue string      // stores the string value for convenience and pretty print
}

func (*AVP) Encode

func (a *AVP) Encode(b []byte) (n int, err error)

func (*AVP) GetStringValue

func (a *AVP) GetStringValue() (strVal string)

StringValue returns the string value from either AVP of VSA

func (*AVP) SetRawValue

func (a *AVP) SetRawValue(dict *Dictionary, cdr Coder) (err error)

SetRawValue will set the raw value (wire ready) from concrete one stored in interface

func (*AVP) SetValue

func (a *AVP) SetValue(dict *Dictionary, cdr Coder) (err error)

SetValue populates Value with concrete data based on raw one abandons in case of Value already set

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is a thread-safe RADIUS client

func NewClient

func NewClient(net, address string, secret string, dict *Dictionary,
	connAttempts int, avpCoders map[string]codecs.AVPCoder, l logger) (*Client, error)

NewClient creates a new client and connects it to the address

func (*Client) NewRequest

func (c *Client) NewRequest(code PacketCode, id uint8) (req *Packet)

NewRequest produces new client request with an random Authenticator

func (*Client) SendRequest

func (c *Client) SendRequest(req *Packet) (rpl *Packet, err error)

SendRequest dispatches a request and returns it's reply or error

type Coder

type Coder map[string]codecs.AVPCoder

Coder puts together the available codecs Key represents the attribute type as defined in dictionary

func NewCoder

func NewCoder() Coder

func (Coder) Decode

func (cdr Coder) Decode(attrType string, b []byte) (v interface{}, s string, err error)

Decode converts raw value received over network into concrete value stored in AVP and it's string representation

func (Coder) Encode

func (cdr Coder) Encode(attrType string, v interface{}) (b []byte, err error)

Encode converts concrete value into raw value to be sent over the network

func (Coder) EncodeString

func (cdr Coder) EncodeString(attrType, strVal string) (b []byte, err error)

type Dictionaries

type Dictionaries struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Dictionaries gathers together dictionaries to be safely accessed centralized in more than one server instance

func NewDictionaries

func NewDictionaries(dicts map[string]*Dictionary) *Dictionaries

NewDictionaries instantiates Dictionary structure

func (*Dictionaries) GetInstance

func (dts *Dictionaries) GetInstance(instanceID string) (dict *Dictionary)

GetInstance returns the Dictionary instance based on id or default one if not found

type Dictionary

type Dictionary struct {
	sync.RWMutex // locks the Dictionary so we can update it on run-time
	// contains filtered or unexported fields
}

Dictionary translates between types and human readable attributes provides per-client inFormation

func NewDictionaryFromFoldersWithRFC2865

func NewDictionaryFromFoldersWithRFC2865(dirPath []string) (*Dictionary, error)

NewDictionaryFromFoldersWithDefaults parses the folder and returns the Dictionary object Resulting dictionary contains RFC2865 elements

func NewEmptyDictionary

func NewEmptyDictionary() *Dictionary

NewEmptyDictionary initializes properly the maps in the Dictionary struct

func RFC2865Dictionary

func RFC2865Dictionary() (d *Dictionary)

Dictionary data required in RFC2865

func (*Dictionary) AttributeWithName

func (dict *Dictionary) AttributeWithName(attrName, VendorName string) *DictionaryAttribute

DictionaryAttribute queries Dictionary for Attribute with specific name

func (*Dictionary) AttributeWithNumber

func (dict *Dictionary) AttributeWithNumber(attrNr uint8, vendorCode uint32) *DictionaryAttribute

DictionaryAttribute queries Dictionary for Attribute having specific number

func (*Dictionary) ParseFromFolder

func (dict *Dictionary) ParseFromFolder(dirPath string) (err error)

parseFromFolder walks through the folder/subfolders and loads all dictionary.* files it finds

func (*Dictionary) ParseFromReader

func (dict *Dictionary) ParseFromReader(rdr io.Reader) (err error)

parseFromReader loops through the lines in the reader, adding info to the Dictionary overwrites previous data

func (*Dictionary) ValueWithName

func (dict *Dictionary) ValueWithName(attrName, valName, vendorName string) (dv *DictionaryValue)

func (*Dictionary) ValueWithNumber

func (dict *Dictionary) ValueWithNumber(attrName string, valNr uint8, vendorCode uint32) (dv *DictionaryValue)

func (*Dictionary) VendorWithCode

func (dict *Dictionary) VendorWithCode(vendorCode uint32) *DictionaryVendor

func (*Dictionary) VendorWithName

func (dict *Dictionary) VendorWithName(VendorName string) *DictionaryVendor

type DictionaryAttribute

type DictionaryAttribute struct {
	AttributeName   string
	AttributeNumber uint8
	AttributeType   string
}

dictionaryAttribute defines a dictionary mapping and type for an attribute.

type DictionaryValue

type DictionaryValue struct {
	AttributeName string
	ValueName     string
	ValueNumber   uint8
}

dictionaryValue defines an enumerated value for an attribute.

type DictionaryVendor

type DictionaryVendor struct {
	VendorName   string
	VendorNumber uint32
	Format       string
}

DictionaryVendor defines a dictionary mapping for a vendor.

type Packet

type Packet struct {
	sync.RWMutex

	Code          PacketCode
	Identifier    uint8
	Authenticator [16]byte
	AVPs          []*AVP
	// contains filtered or unexported fields
}

func NewPacket

func NewPacket(code PacketCode, id uint8, dict *Dictionary, coder Coder, secret string) *Packet

NewPacket creates a fresh packet, used mostly for testing

func (*Packet) AddAVPWithName

func (p *Packet) AddAVPWithName(attrName, strVal, vendorName string) (err error)

AddAVPWithName adds an AVP based on it's attribute name and string value

func (*Packet) AddAVPWithNumber

func (p *Packet) AddAVPWithNumber(attrNr uint8, val interface{}, vendorCode uint32) (err error)

AddAVPWithNumber adds an AVP based on it's attribute number and value

func (*Packet) AttributesWithName

func (p *Packet) AttributesWithName(attrName, vendorName string) (avps []*AVP)

Attributes queries AVPs matching the attrNr

func (*Packet) AttributesWithNumber

func (p *Packet) AttributesWithNumber(attrNr uint8, vendorCode uint32) (avps []*AVP)

AttributesWithNumber queries AVPs matching the attrNr if vendorCode is defined, AttributesWithNumber will query VSAs

func (*Packet) Decode

func (p *Packet) Decode(buf []byte) error

func (*Packet) Encode

func (p *Packet) Encode(b []byte) (n int, err error)

Encode is used to encode the Packet into buffer b returning number of bytes written or error

func (*Packet) Has

func (p *Packet) Has(attrNr uint8) bool

func (*Packet) NegativeReply

func (p *Packet) NegativeReply(errMsg string) *Packet

NegativeReply generates a response packet indicating a failure or rejection based on the original request.

func (*Packet) RemoteAddr

func (pk *Packet) RemoteAddr() net.Addr

func (*Packet) Reply

func (p *Packet) Reply() *Packet

func (*Packet) SetAVPValues

func (p *Packet) SetAVPValues()

func (*Packet) SetCodeWithName

func (p *Packet) SetCodeWithName(codeName string) (err error)

SetCodeWithName sets the packet code based on predefined name

type PacketCode

type PacketCode uint8
const (
	AccessRequest        PacketCode = 1
	AccessAccept         PacketCode = 2
	AccessReject         PacketCode = 3
	AccountingRequest    PacketCode = 4
	AccountingResponse   PacketCode = 5
	AccessChallenge      PacketCode = 11
	StatusServer         PacketCode = 12 //(experimental)
	StatusClient         PacketCode = 13 //(experimental)
	DisconnectRequest    PacketCode = 40
	DisconnectACK        PacketCode = 41
	DisconnectNAK        PacketCode = 42
	CoARequest           PacketCode = 43
	CoAACK               PacketCode = 44
	CoANAK               PacketCode = 45
	Reserved             PacketCode = 255
	ReplyMessage                    = 18
	VendorSpecificNumber            = 26 // vendor specific AVP number
	VendorSpecificName              = "Vendor-Specific"
	NoVendor                        = 0
)

func (PacketCode) String

func (p PacketCode) String() string

type Secrets

type Secrets struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

Secrets centralizes RADIUS secrets so it can be safely accessed over different server instances

func NewSecrets

func NewSecrets(sts map[string]string) *Secrets

NewSecrets intantiates Secrets

func (*Secrets) GetSecret

func (sts *Secrets) GetSecret(instanceID string) (scrt string)

GetSecret returns secret for specific instanceID Returns default if no instanceID found

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server represents a single listener on a port

func NewServer

func NewServer(net, addr string, secrets *Secrets, dicts *Dictionaries,
	reqHandlers map[PacketCode]func(*Packet) (*Packet, error),
	avpCoders map[string]codecs.AVPCoder, l logger) *Server

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(stopChan <-chan struct{}) error

ListenAndServe binds to a port and serves requests

func (*Server) RegisterHandler

func (s *Server) RegisterHandler(code PacketCode, hndlr func(*Packet) (*Packet, error))

RegisterHandler registers a new handler after the server was instantiated useful for live server reloads

type VSA

type VSA struct {
	Vendor      uint32
	Number      uint8       // attribute number
	VendorName  string      // populated by dictionary
	Name        string      // attribute name
	Type        string      // type of the value helping us to convert to concrete
	Value       interface{} // holds the concrete value defined in dictionary, extracted back with type (eg: avp.Value.(string))
	RawValue    []byte      // value as received over network
	StringValue string      // stores the string value
}

Vendor specific Attribute/Val originally ported from github.com/bronze1man/radius/avp_vendor.go

func NewVSAFromAVP

func NewVSAFromAVP(avp *AVP) (*VSA, error)

func (*VSA) AVP

func (vsa *VSA) AVP() *AVP

AVP encodes VSA back into AVP

func (*VSA) SetRawValue

func (vsa *VSA) SetRawValue(dict *Dictionary, cdr Coder) (err error)

SetRawValue populates RawValue(wire data) based on concrete stored in vsa.Value

func (*VSA) SetValue

func (vsa *VSA) SetValue(dict *Dictionary, cdr Coder) (err error)

SetValue populates Value elements based on vsa.RawValue

type Validation

type Validation struct {
	MinLength int
	MaxLength int //if < 0 unlimited
	Decode    func(p *Packet, attr *AVP) error
}

func (Validation) Validate

func (v Validation) Validate(p *Packet, attr *AVP) error

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL