pyrad.packet – packet encoding and decoding
RADIUS packets (RFC 2865): encoding and decoding of packets and attributes, password encryption, request and response authenticators and the Message-Authenticator.
- class pyrad.packet.Packet(code=0, id=None, secret=b'', authenticator=None, **attributes)[source]
A RADIUS packet, with a dictionary interface to its attributes.
Since RADIUS allows repeated attributes, values are always lists, and the order of the attributes is kept when encoding and decoding. Assigning
pkt['Name'] = valuereplaces all values of the attribute,AddAttribute()appends to them.With attribute names as keys pyrad encodes and decodes the values according to the dictionary; string values that are not valid UTF-8 are returned as
bytes. With the attribute code (or a(vendor, code)tuple for vendor attributes) as key you work with the rawbytesvalues; attributes which are not in the dictionary are always keyed like this.Attributes with the has_tag flag (RFC 2868) take a tag after the name, for example
pkt['Tunnel-Type:1']. Reading without a tag returns the values of all tags, assigning (also with a tag) replaces the values of all tags;del pkt['Name:tag']is not supported. An untagged Tunnel-Password is sent with tag 0.The value of a TLV attribute is a dict which maps the sub-attribute names to their values. Sub-attributes can also be read, set, deleted and tested with
inby their own name.Normally you will not use this class directly, but one of the
AuthPacket,AcctPacketorCoAPacketclasses.- Variables:
code – packet type code, e.g.
AccessRequestid – packet identifier (0-255)
authenticator – Request or Response Authenticator, None until it is created
secret – RADIUS secret
dict – RADIUS dictionary
raw_packet – the data the packet was decoded from, or None
message_authenticator – whether the packet has (or gets when it is encoded) a Message-Authenticator
request_authenticator – in a reply, the Request Authenticator of the request, needed to decrypt salt encrypted attributes
- Parameters:
code (int) – packet type code
id (int) – packet identifier (0-255), defaults to the next value of the module-wide counter, see
pyrad.packet.CreateID()secret (bytes) – RADIUS secret
authenticator (bytes) – Request or Response Authenticator
dict (pyrad.dictionary.Dictionary) – RADIUS dictionary; without one only integer keys (attribute codes) work
packet (bytes) – received data to decode
message_authenticator (bool) – add a Message-Authenticator when the packet is encoded
attributes – attributes to add, with
-in the attribute names replaced by_
- Raises:
PacketError –
packetcannot be decodedTypeError –
secretorauthenticatoris not bytes
- AddAttribute(key, value)[source]
Add an attribute to the packet. The values are appended to existing values of the attribute. Raw values of attributes which are not in the dictionary are set with
pkt[code] = [value]instead.- Parameters:
key (str) – attribute name, with an optional tag for attributes with the has_tag flag (
'Tunnel-Type:1')value – value or list of values, of the Python type of the attribute’s data type (e.g. str, int or bytes)
- static CreateAuthenticator()[source]
Create a packet authenticator. All RADIUS packets contain a sixteen byte authenticator which is used to authenticate replies from the RADIUS server and in the password hiding algorithm. This function returns a suitable random string that can be used as an authenticator.
- Returns:
valid packet authenticator
- Return type:
bytes
- CreateID()[source]
Return a random packet Identifier (0-255).
All RADIUS requests have an Identifier which is used to match replies to requests and to detect duplicate requests. Packets created without an id get the next value of the module-level counter
pyrad.packet.CreateID()instead.- Returns:
packet Identifier
- Return type:
int
- CreateReply(**attributes)[source]
Create a reply to this packet.
The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes (RFC 2865 section 5.33). Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. The reply code defaults to 0, pass
code. Withpacket=<bytes>a received reply is decoded.- Parameters:
attributes – arguments of
Packetand attributes to add- Returns:
the reply
- Return type:
- DecodePacket(packet)[source]
Decode a packet as received from the network.
The code, id, authenticator and attributes of this packet are replaced by the decoded ones.
- Parameters:
packet (bytes) – raw packet
- Raises:
PacketError – the packet cannot be decoded
- ReplyPacket()[source]
Encode the packet as a reply, ready to be sent to a client.
The Response Authenticator is calculated from the Request Authenticator in
authenticator, and a Message-Authenticator is added ifmessage_authenticatoris set.- Returns:
raw packet
- Return type:
bytes
- SaltCrypt(value)[source]
Encrypt a value with the salt encryption of RFC 2868 section 3.5 (attributes with encrypt=2).
Values longer than 239 octets don’t fit into an attribute; the encoding of the packet fails for them.
- Parameters:
value (str or bytes) – plaintext value
- Returns:
encrypted value including salt
- Return type:
bytes
- Raises:
ValueError – if the value is longer than 255 octets
- SaltDecrypt(value)[source]
Decrypt a value encrypted with the salt encryption of RFC 2868 section 3.5 (attributes with encrypt=2).
For a reply,
request_authenticatormust be set to the Request Authenticator of the request (the clients do that).- Parameters:
value (bytes) – encrypted value including salt
- Returns:
decrypted value
- Return type:
bytes
- Raises:
PacketError – the value has an invalid length
- VerifyReply(reply, rawreply=None, enforce_ma=False)[source]
Verify that a reply belongs to this request.
Checks the packet ID and the Response Authenticator and, as countermeasure against the BlastRADIUS attack (CVE-2024-3596), the Message-Authenticator (always verified if the reply has one) and, for Access-Request and Status-Server, that the reply has no Proxy-State which was not sent.
- Parameters:
reply (pyrad.packet.Packet) – reply packet
rawreply (bytes) – reply as received from the network, defaults to the raw packet the reply was decoded from
enforce_ma (bool) – require a Message-Authenticator in replies to Access-Request and Status-Server packets
- Returns:
True if the reply is valid else False
- Return type:
bool
- add_message_authenticator()[source]
Add a Message-Authenticator (RFC 2869 section 5.14) as first attribute, as recommended as countermeasure against the BlastRADIUS attack (CVE-2024-3596). The value is calculated when the packet is encoded.
- get(key, failobj=None)[source]
Return the values of an attribute, or
failobjif the packet does not have it.- Parameters:
key – attribute name, code or
(vendor, code)tuplefailobj – value to return if the attribute is missing
- Returns:
list of values, or
failobj
- get_message_authenticator()[source]
Calculate the Message-Authenticator and return the
message_authenticatorflag.
- has_key(key)
Return whether the packet has the attribute
key.has_key(key)is a deprecated alias ofkey in pkt.
- keys()[source]
Return the attribute keys of the packet, in order.
Attributes in the dictionary are listed by name (a TLV by the TLV name, not by its sub-attributes), undefined attributes by their code or
(vendor, code)tuple.- Returns:
attribute names and codes
- Return type:
list
- verify_message_authenticator(secret=None, original_authenticator=None, original_code=None)[source]
Verify the Message-Authenticator of the packet.
To verify a received reply, the authenticator and code of the request must be passed as original_authenticator and original_code (
VerifyReply()does that).- Parameters:
secret (bytes) – RADIUS secret, defaults to
secretoriginal_authenticator (bytes) – Request Authenticator of the request, for a reply
original_code (int) – code of the request, for a reply
- Returns:
True if the Message-Authenticator is valid else False
- Return type:
bool
- Raises:
Exception – the packet has no Message-Authenticator; check
message_authenticatorfirst
- class pyrad.packet.AuthPacket(code=1, id=None, secret=b'', authenticator=None, auth_type='pap', **attributes)[source]
RADIUS authentication packet: Access-Request, Status-Server sent to the authentication port, and their replies.
- Parameters:
code (int) – packet type code
id (int) – packet identifier, see
Packetsecret (bytes) – RADIUS secret
authenticator (bytes) – Request Authenticator, created when needed
auth_type (str) –
'pap', or'eap-md5'to authenticate with EAP-MD5 when the packet is sent withpyrad.client.Client.SendPacket()orpyrad.client_async.ClientAsync.SendPacket()attributes –
dict,packet,message_authenticatorand attributes to add, seePacket
- CreateReply(**attributes)[source]
Create a reply to this packet, an Access-Accept by default.
The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes (RFC 2865 section 5.33). Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. Passing
coderaisesTypeError, setreply.codeinstead (e.g. toAccessReject).A reply to an Access-Request or Status-Server gets a Message-Authenticator unless
message_authenticator=Falseis passed. Withpacket=<bytes>a received reply is decoded.- Parameters:
attributes – arguments of
AuthPacketand attributes to add- Returns:
the reply
- Return type:
- PwCrypt(password)[source]
Encrypt a password for the User-Password attribute.
RADIUS hides passwords with an algorithm based on the MD5 hash of the Request Authenticator and the secret (RFC 2865 section 5.2):
pkt['User-Password'] = pkt.PwCrypt(password). If no authenticator is set, one is created. Changing the authenticator afterwards makes the encrypted password invalid.- Parameters:
password (str or bytes) – plaintext password
- Returns:
obfuscated version of the password
- Return type:
bytes
- Raises:
ValueError – if the password is longer than 128 octets
- PwDecrypt(password)[source]
Decrypt a User-Password (RFC 2865 section 5.2).
RADIUS hides passwords with an algorithm based on the MD5 hash of the Request Authenticator and the secret; this reverses it.
The password is decoded as UTF-8, the encoding RFC 2865 uses for text; invalid UTF-8 sequences (for example if the secret is wrong) are ignored.
- Parameters:
password (bytes or str) – encrypted password
- Returns:
plaintext password
- Return type:
str
- Raises:
PacketError – the length is not a multiple of 16 octets
- RequestPacket()[source]
Encode the packet as a request, ready to be sent to a server.
A Request Authenticator and id are created if not set yet.
- Returns:
raw packet
- Return type:
bytes
- VerifyAuthRequest()[source]
Check whether the authenticator is an accounting-style request authenticator: the MD5 hash of the packet header, 16 zero octets, the attributes and the secret (as for Accounting-Request, RFC 2866 section 3).
This is not a verification of Access-Request packets: their Request Authenticator is a random number (RFC 2865 section 3), so this check fails for them. Use verify_message_authenticator to authenticate an Access-Request or Status-Server packet with a Message-Authenticator.
Only for packets decoded from received data (
packet=), it fails for packets built locally.- Returns:
True if verification passed else False
- Return type:
bool
- VerifyChapPasswd(userpwd)[source]
Verify the CHAP-Password of an Access-Request (RFC 2865 section 2.2). The CHAP-Challenge attribute is used as challenge if present, else the request authenticator.
- Parameters:
userpwd (str or bytes) – plaintext password; surrounding whitespace is stripped from a str, not from bytes
- Returns:
True if the password matches else False, also if there is no CHAP-Password attribute
- Return type:
bool
- class pyrad.packet.AcctPacket(code=4, id=None, secret=b'', authenticator=None, **attributes)[source]
RADIUS accounting packet: Accounting-Request, Status-Server sent to the accounting port, and their replies.
- Parameters:
code (int) – packet type code
id (int) – packet identifier, see
Packetsecret (bytes) – RADIUS secret
authenticator (bytes) – Request Authenticator; for an Accounting-Request it is calculated when the packet is encoded
attributes –
dict,packet,message_authenticatorand attributes to add, seePacket
- CreateReply(**attributes)[source]
Create a reply to this packet, an Accounting-Response.
The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes. Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. Passing
coderaisesTypeError, setreply.codeinstead.A reply to a Status-Server gets a Message-Authenticator unless
message_authenticator=Falseis passed. Withpacket=<bytes>a received reply is decoded.- Parameters:
attributes – arguments of
AcctPacketand attributes to add- Returns:
the reply
- Return type:
- RequestPacket()[source]
Encode the packet as a request, ready to be sent to a server.
The Request Authenticator of an Accounting-Request is calculated over the packet (RFC 2866 section 3) and stored in
authenticator.A Status-Server packet (RFC 5997 section 3) gets a random Request Authenticator, like an Access-Request, and a Message-Authenticator unless message_authenticator is set to False.
- Returns:
raw packet
- Return type:
bytes
- VerifyAcctRequest()[source]
Verify the Request Authenticator of a received request.
The Request Authenticator of a Status-Server packet is random (RFC 5997 section 3), for these packets the Message-Authenticator is verified instead, and a packet without one fails verification.
Only for packets decoded from received data (
packet=), it fails for packets built locally.- Returns:
True if verification passed else False
- Return type:
bool
- class pyrad.packet.CoAPacket(code=43, id=None, secret=b'', authenticator=None, **attributes)[source]
RADIUS Dynamic Authorization packet (RFC 5176): CoA-Request, Disconnect-Request and their replies.
- Parameters:
code (int) – packet type code,
CoARequestorDisconnectRequestfor a requestid (int) – packet identifier, see
Packetsecret (bytes) – RADIUS secret
authenticator (bytes) – Request Authenticator; for a request it is calculated when the packet is encoded
attributes –
dict,packet,message_authenticatorand attributes to add, seePacket
- CreateReply(**attributes)[source]
Create a reply to this packet: a Disconnect-ACK for a Disconnect-Request, otherwise a CoA-ACK.
Set
reply.codefor a NAK (CoANAKorDisconnectNAK). PassingcoderaisesTypeError.The reply gets the id, Request Authenticator, secret and dictionary of this packet, and its Proxy-State attributes. Don’t copy Proxy-State yourself: a duplicated Proxy-State makes the client reject the reply. With
packet=<bytes>a received reply is decoded.- Parameters:
attributes – arguments of
CoAPacketand attributes to add- Returns:
the reply
- Return type:
- class pyrad.packet.PacketError[source]
Raised for a packet or attribute value that cannot be decoded, e.g. an invalid length.
Constants
The pyrad.packet module defines several common constants
that are useful when dealing with RADIUS packets.
The following packet codes are defined:
Constant name |
Value |
|---|---|
AccessRequest |
1 |
AccessAccept |
2 |
AccessReject |
3 |
AccountingRequest |
4 |
AccountingResponse |
5 |
AccessChallenge |
11 |
StatusServer |
12 |
StatusClient |
13 |
DisconnectRequest |
40 |
DisconnectACK |
41 |
DisconnectNAK |
42 |
CoARequest |
43 |
CoAACK |
44 |
CoANAK |
45 |