From: Alan T. DeKok Date: Thu, 18 Dec 2025 07:44:27 +0000 (+0100) Subject: parse partially known and partially unknown attributes X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ea71ef56f07628e95d8854d3f0210855a05fd89a;p=thirdparty%2Ffreeradius-server.git parse partially known and partially unknown attributes and commit more pair tests --- diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 027e545beee..c544c0da308 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -5688,12 +5688,22 @@ parse: } else if (fr_sbuff_adv_past_str_literal(&our_in, "::")) { slen = fr_dict_attr_by_oid_substr(NULL, &dst->vb_attr, dst_enumv, &our_in, rules->terminals); - if (slen > 0) { - fr_assert(dst->vb_attr != NULL); + if (slen <= 0) goto unknown_attr; + + fr_assert(dst->vb_attr != NULL); + + if (!fr_sbuff_next_if_char(&our_in, '.')) { FR_SBUFF_SET_RETURN(in, &our_in); } + + /* + * Parse the unknown attribute starting from where we left off. + */ + dst_enumv = dst->vb_attr; + dst->vb_attr = NULL; } +unknown_attr: slen = fr_dict_attr_unknown_afrom_oid_substr(ctx, &dst->vb_attr, dst_enumv, &our_in, FR_TYPE_OCTETS); if (slen <= 0) { fr_strerror_printf("Failed to find the named attribute in %s", dst_enumv->name); diff --git a/src/tests/unit/dictionary.test b/src/tests/unit/dictionary.test new file mode 100644 index 00000000000..d4c893f7185 --- /dev/null +++ b/src/tests/unit/dictionary.test @@ -0,0 +1 @@ +DEFINE Test-Attr attribute internal diff --git a/src/tests/unit/pair.txt b/src/tests/unit/pair.txt new file mode 100644 index 00000000000..e12d1b2ae74 --- /dev/null +++ b/src/tests/unit/pair.txt @@ -0,0 +1,232 @@ +# -*- text -*- +# Copyright (C) 2025 Network RADIUS SARL (legal@networkradius.com) +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# +# Version $Id$ +# + +# +# @todo - add error offset to failure cases. +# + +# +# Test pair parsing for fr_pair_list_afrom_substr() +# +proto radius +proto-dictionary radius + +load-dictionary dictionary.test + +pair User-Name = "bob" +match User-Name = "bob" + +# +# @todo - This is arguably wrong. The rest of the server requires quoted strings, and disallows bare words. +# +pair User-Name = 0xabcdef +match User-Name = "0xabcdef" + +# +# @todo - This is arguably wrong. The rest of the server requires "::" prefixes for enumeration values. +# +pair Service-Type = Framed-User +match Service-Type = ::Framed-User + +# +# Multiple things +# +pair User-Name = "bob", User-Password = "hello" +match User-Name = "bob", User-Password = "hello" + +###################################################################### +# +# Raw attributes +# +###################################################################### + +# +# A raw attribute. +# +# This is perhaps wrong? We should probably be printing this as +# "raw.1 = 0xabcdef", because it's not the normal attribute. +# +pair raw.User-Name = 0xabcdef +match raw.User-Name = 0xabcdef + +# +# Change data types. +# +# @todo - This is arguably wrong. We should be able to change data types, so +# long as the attribute is "raw.1", and not "raw.User-Name". +# +pair raw.User-Name = (ipaddr) 192.168.0.1 +match Cannot create raw attribute User-Name which changes data type from string to ipaddr + +# +# This is OK, due to the underlying IP address parser. +# +pair Framed-IP-Address = 1 +match Framed-IP-Address = 0.0.0.1 + +###################################################################### +# +# Nested attributes +# +###################################################################### + +# +# Empty list +# +pair Extended-Attribute-1 = { } +match Extended-Attribute-1 = { } + +# +# Internal group can contain a protocol attribute. +# +pair Tmp-Group-0 = { User-Name = "bob" } +match Tmp-Group-0 = { User-Name = "bob" } + +# +# Internal TLV cannot contain a protocol attribute. +# +pair Tmp-TLV-0 = { User-Name = "bob" } +match Internal attribute 'Tmp-TLV-0' of data type 'tlv' cannot contain protocol attributes + +# +# Protocol TLV can contain an internal attribute +# +# This is definitely wrong. The switch to the internal dictionary +# confuses the pair printing routines. +# +pair Extended-Attribute-1 = { Tmp-Integer-0 = 1 } +match Extended-Attribute-1 = { Tmp-Integer-0 = 1 } + +# +# One level of TLVs +# +pair Digest-Attributes = { Realm = "foo" } +match Digest-Attributes = { Realm = "foo" } + +# +# Two levels of TLVs +# +pair Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } } +match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } } + +# +# Aliases +# +pair IP-Port-Limit-Info.Type = 1 +match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } } + +pair IP-Port-Limit-Info = { Type = 1 } +match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } } + +###################################################################### +# +# Flat lists with operators +# +###################################################################### + +# +# Flat input results in nested output. +# +pair Extended-Attribute-1.IP-Port-Limit-Info.Type = 1 +match Extended-Attribute-1 = { IP-Port-Limit-Info = { Type = 1 } } + +# +# Flat inputs nest at the lowest level +# +pair Vendor-Specific.Cisco.AVPair = "foo", Vendor-Specific.Cisco.AVPair = "bar" +match Vendor-Specific = { Cisco = { AVPair = "foo", AVPair = "bar" } } + +# +# "+=" gets mashed to "=" +# +pair Vendor-Specific.Cisco.AVPair = "foo", Vendor-Specific.Cisco.AVPair += "bar" +match Vendor-Specific = { Cisco = { AVPair = "foo", AVPair = "bar" } } + +# +# Comparison operators, equality. Only leaf attributes can be compared. +# +pair-compare Vendor-Specific.Cisco.AVPair == "foo", Vendor-Specific.Cisco.AVPair == "bar" +match Vendor-Specific = { Cisco = { AVPair == "foo", AVPair == "bar" } } + +# +# Except that lists can only be compared for equality +# + +# +# Comparison operators, leaf comparisons +# +pair-compare Vendor-Specific.Cisco.AVPair < "foo", Vendor-Specific.Cisco.AVPair > "bar" +match Vendor-Specific = { Cisco = { AVPair < "foo", AVPair > "bar" } } + +# +# Comparison operators apply only to leaf. +# +pair-compare Vendor-Specific < { Cisco == { AVPair == "foo" } } +match Structural attribute 'Vendor-Specific' must use '=' or '==' for comparisons + +# +# Invalid operator - comparison operators are not allowed when doing assignments. +# +pair Vendor-Specific.Cisco.AVPair == "foo", Vendor-Specific.Cisco.AVPair == "bar" +match Invalid character '=' after operator '=' + +# +# Flat inputs nest at a higher level, too +# +pair Vendor-Specific.Cisco.AVPair = "foo", Vendor-Specific.HP.Privilege-Level = 1 +match Vendor-Specific = { Cisco = { AVPair = "foo" }, HP = { Privilege-Level = 1 } } + +###################################################################### +# +# Error cases +# +###################################################################### + +pair .User-Name = "bob" +match The '.Attribute' syntax cannot be used at the root of a dictionary + +pair Tmp-Group-0 = { .User-Name = "bob" } +match The '.Attribute' syntax cannot be used with parent Tmp-Group-0 of data type 'group' + +pair Vendor-Specific = { Cisco = { .AVPair += "foo" } } +match The '.Attribute' syntax cannot be used along with the '+=' operator + +# +# Invalid data types +# +pair NAS-Port = 192.168.0.1 +match Unexpected text '.168.0.1 ...' after value + +pair NAS-Port = 192.168.0.1, NAS-Port = 2 +match Unexpected text '.168.0.1, NAS-Port = ...' after value + +pair NAS-Port = abcdef +match Failed parsing string as type 'uint32' + +pair Extended-Attribute-1 = 0xabcdef +match Group list for Extended-Attribute-1 MUST start with '{' + +pair Extended-Attribute-1.Framed-IP-Address.Tmp-String-0 := "hello" +match Unknown attribute "Framed-IP-Address.Tmp-String-0" for parent "Extended-Attribute-1" + +# +# Attributes as values +# +pair Test-Attr = ::Vendor-Specific.Cisco.AVPair +match Test-Attr = ::Vendor-Specific.Cisco.AVPair + +pair Test-Attr = ::26.9.1 +match Test-Attr = ::Vendor-Specific.Cisco.AVPair + +# +# Partially known and partially unknown +# +pair Test-Attr = ::Vendor-Specific.12345.1.1 +match Test-Attr = ::Vendor-Specific.12345.1.1 + +count +match 71