diff --git a/src/pyrad3/dictionary.py b/src/pyrad3/dictionary.py index 88e910e..d9ede59 100644 --- a/src/pyrad3/dictionary.py +++ b/src/pyrad3/dictionary.py @@ -330,6 +330,8 @@ class Dictionary: filename = self.filestack[-1] tlength = self.cur_vendor.tlength codes = evs.copy() + if len(codes) > 0: + codes.append(self.cur_vendor.code) for code in attr_code.split("."): try: code_num = _parse_number(code) @@ -409,7 +411,7 @@ class Dictionary: raise ParseError(filename, "evs must be a tlv", line_num) self.evs[name.upper()] = list(attrcode) - if self.cur_vendor != self.rfc_vendor: + if self.cur_vendor != self.rfc_vendor and len(evs) == 0: codes = [26, self.cur_vendor.code] + codes LOG.info( "Register Attribute %s for Vendor %s", diff --git a/src/pyrad3/types.py b/src/pyrad3/types.py index 8d27ab6..d3a47ee 100644 --- a/src/pyrad3/types.py +++ b/src/pyrad3/types.py @@ -1,14 +1,13 @@ # Copyright 2020 Istvan Ruzman # SPDX-License-Identifier: MIT OR Apache-2.0 - - """Valid RADIUS codes (registered in IANA) Currently not all RADIUS codes are contained, because we don't support them (yet). """ +from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, field from enum import Enum, IntEnum, auto from typing import Dict, List, Tuple, Union @@ -84,6 +83,7 @@ class Attribute: # pylint: disable=too-many-instance-attributes has_tag: bool = False encrypt: Encrypt = Encrypt(0) is_sub_attr: bool = False + tlvs: Dict[str, Attribute] = field(default_factory=dict) # vendor = Dictionary diff --git a/src/pyrad3/utils.py b/src/pyrad3/utils.py index 5588a76..516d2a0 100644 --- a/src/pyrad3/utils.py +++ b/src/pyrad3/utils.py @@ -27,10 +27,10 @@ PACK_TABLE = { class PacketError(Exception): - """Exception for Invalid Packets""" + """Exception for Invalid/Maleformed RADIUS Packets""" -Header = namedtuple("Header", ["code", "radius_id", "length", "authenticator"]) +Header = namedtuple("Header", ["code", "identifier", "length", "authenticator"]) Attribute = namedtuple("Attribute", ["name", "pos", "type", "length", "tag", "value"]) PreParsedAttributes = List[Tuple[Tuple[int, ...], bytes, int]] @@ -70,6 +70,7 @@ def decode_attributes(rad_dict: Dictionary, raw_packet: bytes) -> List[Attribute for key, value, offset in pre_decode_attributes(rad_dict, packet): attr_def = rad_dict.attrindex.get(key) + print(rad_dict.attrindex.keys()) dec_value: Any = value # to silence mypy tag = 0 if attr_def is None: @@ -129,8 +130,7 @@ def pre_decode_attributes( # pylint: disable=too-many-branches elif attr_def.datatype == Datatype.CONCAT: key, value, modifier = decode_concat(key, value, offset) else: - modifier = 2 - # Redundant in the "normal" case + modifier = 2 # Redundant in the "normal" case tmp_attributes = [(key, value, offset + modifier)] except (KeyError, IndexError): # We do not know the TLV, but the packet seems to be well-formed so far @@ -143,6 +143,9 @@ def pre_decode_attributes( # pylint: disable=too-many-branches if adef.datatype == Datatype.TLV: # TODO: deal with tagged tlvs attributes.extend(decode_tlv(rad_dict, list(key), value, offset)) + elif adef.datatype == Datatype.EVS: + assert isinstance(key, tuple) + attributes.append(decode_evs(key, value)) else: raise ValueError except (ValueError, KeyError): @@ -222,16 +225,16 @@ def decode_vsa( def decode_extended(key: int, value: bytes) -> SpecialTlvDescription: """Decode an Attribute of type extended""" - key = (key, value[0]) + ext_key = (key, value[0]) value = value[1:] - return (key, value, 3) + return (ext_key, value, 3) def decode_longextended(key: int, value: bytes) -> SpecialTlvDescription: """Decode an Attribute of type long-extended""" - key = (key, value[0]) + ext_key = (key, value[0]) value = value[2:] - return (key, value, 4) + return (ext_key, value, 4) def decode_concat(key: int, value: bytes, offset: int) -> SpecialTlvDescription: @@ -239,13 +242,13 @@ def decode_concat(key: int, value: bytes, offset: int) -> SpecialTlvDescription: raise NotImplementedError -def decode_evs(key: int, value: bytes, offset: int) -> PreParsedAttributes: +def decode_evs(key: Tuple[int, int], value: bytes) -> SpecialTlvDescription: """Decode an Attribute of type EVS (Extended Vendor Specific)""" vendor_id = int.from_bytes(value[:4], "big") vendor_type = value[4] - key = tuple(list(key) + [vendor_id, vendor_type]) + evs_key = (key[0], key[1], vendor_id, vendor_type) value = value[5:] - return (key, value, 5) + return (evs_key, value, 5) def decode_tlv( @@ -268,6 +271,11 @@ def decode_tlv( return ret +def encode_attributes(rad_dict: Dictionary, attributes: List[Attribute]): + for attribute in attributes: + raise NotImplementedError + + def calculate_authenticator( secret: bytes, authenticator: bytes, raw_packet: bytes ) -> bytes: diff --git a/tests/dictionaries/dict b/tests/dictionaries/dict index fbcab0c..db3e106 100644 --- a/tests/dictionaries/dict +++ b/tests/dictionaries/dict @@ -81,44 +81,44 @@ ATTRIBUTE VENDOR10-TAGGED-ETHER 116 ether has_tag END-VENDOR TEST10 BEGIN-VENDOR TEST10 format=RFC-SPACE-TYPE-EVS -ATTRIBUTE VENDOR10-EVS-TYPE-STRING 19.21.1234.1 string -ATTRIBUTE VENDOR10-EVS-TYPE-OCTETS 19.21.1234.2 octets -ATTRIBUTE VENDOR10-EVS-TYPE-DATE 19.21.1234.3 date -ATTRIBUTE VENDOR10-EVS-TYPE-ABINARY 19.21.1234.4 abinary -ATTRIBUTE VENDOR10-EVS-TYPE-BYTE 19.21.1234.5 byte -ATTRIBUTE VENDOR10-EVS-TYPE-SHORT 19.21.1234.6 short -ATTRIBUTE VENDOR10-EVS-TYPE-INTEGER 19.21.1234.7 integer -ATTRIBUTE VENDOR10-EVS-TYPE-SIGNED 19.21.1234.8 signed -ATTRIBUTE VENDOR10-EVS-TYPE-INTEGER64 19.21.1234.9 integer64 -ATTRIBUTE VENDOR10-EVS-TYPE-IPADDR 19.21.1234.10 ipaddr -ATTRIBUTE VENDOR10-EVS-TYPE-IPV4PREFIX 19.21.1234.11 ipv4prefix -ATTRIBUTE VENDOR10-EVS-TYPE-IPV6ADDR 19.21.1234.12 ipv6addr -ATTRIBUTE VENDOR10-EVS-TYPE-IPV6PREFIX 19.21.1234.13 ipv6prefix -ATTRIBUTE VENDOR10-EVS-TYPE-COMBOIP 19.21.1234.14 comboip -ATTRIBUTE VENDOR10-EVS-TYPE-IFID 19.21.1234.15 ifid -ATTRIBUTE VENDOR10-EVS-TYPE-ETHER 19.21.1234.16 ether -ATTRIBUTE VENDOR10-EVS-TYPE-TLV 19.21.1234.18 tlv -END-VENDOR +ATTRIBUTE VENDOR10-EVS-TYPE-STRING 1 string +ATTRIBUTE VENDOR10-EVS-TYPE-OCTETS 2 octets +ATTRIBUTE VENDOR10-EVS-TYPE-DATE 3 date +ATTRIBUTE VENDOR10-EVS-TYPE-ABINARY 4 abinary +ATTRIBUTE VENDOR10-EVS-TYPE-BYTE 5 byte +ATTRIBUTE VENDOR10-EVS-TYPE-SHORT 6 short +ATTRIBUTE VENDOR10-EVS-TYPE-INTEGER 7 integer +ATTRIBUTE VENDOR10-EVS-TYPE-SIGNED 8 signed +ATTRIBUTE VENDOR10-EVS-TYPE-INTEGER64 9 integer64 +ATTRIBUTE VENDOR10-EVS-TYPE-IPADDR 10 ipaddr +ATTRIBUTE VENDOR10-EVS-TYPE-IPV4PREFIX 11 ipv4prefix +ATTRIBUTE VENDOR10-EVS-TYPE-IPV6ADDR 12 ipv6addr +ATTRIBUTE VENDOR10-EVS-TYPE-IPV6PREFIX 13 ipv6prefix +ATTRIBUTE VENDOR10-EVS-TYPE-COMBOIP 14 comboip +ATTRIBUTE VENDOR10-EVS-TYPE-IFID 15 ifid +ATTRIBUTE VENDOR10-EVS-TYPE-ETHER 16 ether +ATTRIBUTE VENDOR10-EVS-TYPE-TLV 18 tlv +END-VENDOR TEST10 -BEGIN-VENDOR TEST10 format=RFC-SPACE-TYPE-EVS -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-STRING 20.21.1234.1 string -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-OCTETS 20.21.1234.2 octets -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-DATE 20.21.1234.3 date -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-ABINARY 20.21.1234.4 abinary -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-BYTE 20.21.1234.5 byte -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-SHORT 20.21.1234.6 short -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-INTEGER 20.21.1234.7 integer -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-SIGNED 20.21.1234.8 signed -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-INTEGER64 20.21.1234.9 integer64 -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPADDR 20.21.1234.10 ipaddr -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPV4PREFIX 20.21.1234.11 ipv4prefix -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPV6ADDR 20.21.1234.12 ipv6addr -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPV6PREFIX 20.21.1234.13 ipv6prefix -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-COMBOIP 20.21.1234.14 comboip -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IFID 20.21.1234.15 ifid -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-ETHER 20.21.1234.16 ether -ATTRIBUTE VENDOR10-LONG-EVS-TYPE-TLV 20.21.1234.18 tlv -END-VENDOR +BEGIN-VENDOR TEST10 format=RFC-SPACE-TYPE-LONG-EVS +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-STRING 1 string +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-OCTETS 2 octets +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-DATE 3 date +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-ABINARY 4 abinary +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-BYTE 5 byte +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-SHORT 6 short +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-INTEGER 7 integer +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-SIGNED 8 signed +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-INTEGER64 9 integer64 +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPADDR 10 ipaddr +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPV4PREFIX 11 ipv4prefix +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPV6ADDR 12 ipv6addr +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IPV6PREFIX 13 ipv6prefix +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-COMBOIP 14 comboip +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-IFID 15 ifid +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-ETHER 16 ether +ATTRIBUTE VENDOR10-LONG-EVS-TYPE-TLV 18 tlv +END-VENDOR TEST10 VENDOR TEST11 1235 format=1,1 diff --git a/tests/test_dictionary.py b/tests/test_dictionary.py index edc6771..9838394 100644 --- a/tests/test_dictionary.py +++ b/tests/test_dictionary.py @@ -419,4 +419,17 @@ def test_extended_evs(): "END-VENDOR TEST-VENDOR" ) dd = Dictionary("", dictionary) - assert dd["VENDOR-ATTRIBUTE"].code == [10, 20, 10] + assert dd["VENDOR-ATTRIBUTE"].code == [10, 20, 1234, 10] + + +def test_long_extended_evs(): + dictionary = StringIO( + "ATTRIBUTE RFC-LONGEXTENDED 10 extended\n" + "ATTRIBUTE RFC-LONGEXTENDED-EVS 10.20 evs\n" + "VENDOR TEST-VENDOR 1234\n" + "BEGIN-VENDOR TEST-VENDOR format=RFC-LONGEXTENDED-EVS\n" + "ATTRIBUTE VENDOR-ATTRIBUTE 10 integer\n" + "END-VENDOR TEST-VENDOR" + ) + dd = Dictionary("", dictionary) + assert dd["VENDOR-ATTRIBUTE"].code == [10, 20, 1234, 10]