From: Alan T. DeKok Date: Thu, 23 Feb 2023 14:41:53 +0000 (-0500) Subject: support encoding and decoding of dictionary.rfc8907 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=74e9417095a1cdbf9202b981e7f5b6fac3c048dc;p=thirdparty%2Ffreeradius-server.git support encoding and decoding of dictionary.rfc8907 --- diff --git a/src/protocols/tacacs/decode.c b/src/protocols/tacacs/decode.c index 8e8e203439b..b4c6a1c9260 100644 --- a/src/protocols/tacacs/decode.c +++ b/src/protocols/tacacs/decode.c @@ -186,6 +186,7 @@ static int tacacs_decode_args(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr uint8_t const *p = attrs; fr_pair_t *vp; fr_pair_t *vendor = NULL; + fr_dict_attr_t const *root; /* * No one? Just get out! @@ -207,12 +208,15 @@ static int tacacs_decode_args(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr } } + root = fr_dict_root(dict_tacacs); + /* * Then, do the dirty job of creating attributes. */ for (i = 0; i < arg_cnt; i++) { uint8_t const *value, *name_end, *arg_end; fr_dict_attr_t const *da; + fr_pair_list_t *dst; uint8_t buffer[256]; fr_assert((p + argv[i]) <= end); @@ -245,9 +249,20 @@ static int tacacs_decode_args(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr if (!name_end) goto next; /* - * Dupe the whole thing so that we have: - * - * Argument-List += "name=value" + * Prefer to decode from the attribute root, first. + */ + da = fr_dict_attr_by_name(NULL, root, (char *) buffer); + if (da) { + vp = fr_pair_afrom_da(ctx, da); + if (!vp) goto oom; + + dst = out; + goto decode; + } + + /* + * If the attribute isn't in the main dictionary, + * maybe it's in the vendor dictionary? */ if (vendor) { da = fr_dict_attr_by_name(NULL, parent, (char *) buffer); @@ -256,6 +271,9 @@ static int tacacs_decode_args(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr vp = fr_pair_afrom_da(vendor, da); if (!vp) goto oom; + dst = &vendor->vp_group; + + decode: /* * If it's OCTETS or STRING type, then just copy the value verbatim, as the * contents are (should be?) binary-safe. But if it's zero length, then don't need to @@ -304,7 +322,7 @@ static int tacacs_decode_args(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_dict_attr */ } - fr_pair_append(&vendor->vp_group, vp); + fr_pair_append(dst, vp); } else { raw: diff --git a/src/protocols/tacacs/encode.c b/src/protocols/tacacs/encode.c index 6e1cbe4e354..36eeff65523 100644 --- a/src/protocols/tacacs/encode.c +++ b/src/protocols/tacacs/encode.c @@ -153,11 +153,20 @@ static uint8_t tacacs_encode_body_arg_cnt(fr_pair_list_t *vps, fr_dict_attr_t co fr_assert(fr_dict_by_da(vp->da) == dict_tacacs); + /* + * RFC 8907 attributes. + */ + if (vp->da->parent->flags.is_root) { + arg_cnt++; + continue; + } + /* * Recurse into children. */ if (vp->da->type == FR_TYPE_VENDOR) { arg_cnt += tacacs_encode_body_arg_cnt(&vp->vp_group, NULL); + continue; } if (vp->da->parent->type != FR_TYPE_VENDOR) continue; @@ -212,13 +221,14 @@ static ssize_t tacacs_encode_body_arg_n(fr_dbuff_t *dbuff, uint8_t arg_cnt, uint i += child_argc; continue; - } else if (!vp->da->parent || (vp->da->parent->type != FR_TYPE_VENDOR)) { + } else if (!vp->da->parent || (!vp->da->parent->flags.is_root && (vp->da->parent->type != FR_TYPE_VENDOR))) { continue; } else { ssize_t slen; fr_sbuff_t sbuff; fr_dbuff_t arg_dbuff = FR_DBUFF_MAX(&work_dbuff, 255); + fr_value_box_t box; char buffer[256]; /* @@ -228,8 +238,30 @@ static ssize_t tacacs_encode_body_arg_n(fr_dbuff_t *dbuff, uint8_t arg_cnt, uint FR_DBUFF_IN_BYTES_RETURN(&arg_dbuff, (uint8_t) '='); sbuff = FR_SBUFF_OUT(buffer, sizeof(buffer)); - slen = fr_pair_print_value_quoted(&sbuff, vp, 0); - if (slen <= 0) return -1; + + switch (vp->vp_type) { + /* + * For now, we always print time deltas and dates as integers. + * + * Because everyone else's date formats are insane. + */ + case FR_TYPE_DATE: + case FR_TYPE_TIME_DELTA: + fr_value_box_init(&box, FR_TYPE_UINT64, vp->data.enumv, vp->vp_tainted); + if (fr_value_box_cast(NULL, &box, FR_TYPE_UINT64, NULL, &vp->data) < 0) { + buffer[0] = '\0'; + slen = 0; + break; + } + + slen = fr_sbuff_in_sprintf(&sbuff, "%lu", box.vb_uint64); + if (slen <= 0) return -1; + break; + + default: + slen = fr_pair_print_value_quoted(&sbuff, vp, T_BARE_WORD); + if (slen <= 0) return -1; + } FR_DBUFF_IN_MEMCPY_RETURN(&arg_dbuff, buffer, (size_t) slen); @@ -240,6 +272,8 @@ static ssize_t tacacs_encode_body_arg_n(fr_dbuff_t *dbuff, uint8_t arg_cnt, uint fr_dbuff_set(&work_dbuff, &arg_dbuff); } + fr_assert(len <= UINT8_MAX); + FR_PROTO_TRACE("len(arg[%d]) = %d", i, len); arg_len[i++] = len; } @@ -941,7 +975,8 @@ ssize_t fr_tacacs_encode(fr_dbuff_t *dbuff, uint8_t const *original_packet, char } #ifndef NDEBUG - if (fr_debug_lvl >= L_DBG_LVL_4) { +// if (fr_debug_lvl >= L_DBG_LVL_4) { + if (1) { uint8_t flags = packet->hdr.flags; packet->hdr.flags |= FR_TAC_PLUS_UNENCRYPTED_FLAG; diff --git a/src/tests/unit/protocols/tacacs/base.txt b/src/tests/unit/protocols/tacacs/base.txt index 84b7292ae24..bd7ac0f8d8f 100644 --- a/src/tests/unit/protocols/tacacs/base.txt +++ b/src/tests/unit/protocols/tacacs/base.txt @@ -35,7 +35,7 @@ match c1 01 02 00 b7 0f c8 0e 00 00 00 06 39 51 39 56 ef f4 # Authorization - Request: (Client -> Server) # decode-proto c0 02 01 00 e1 66 78 e6 00 00 00 35 4b c5 ea 62 13 cc ca a6 6a 03 3c 8e 3f c0 5a aa 46 da 12 cd ee 48 62 69 67 9a b8 b4 db 70 98 30 b7 fc f6 93 09 d4 3f 2c a9 58 9e 3c 6a 0e d5 50 20 e6 a5 39 46 -match Packet.Version-Major = Plus, Packet.Version-Minor = 0, Packet.Packet-Type = Authorization, Packet.Sequence-Number = 1, Packet.Flags = None, Packet.Session-Id = 3781589222, Packet.Length = 53, Packet-Body-Type = Request, Authentication-Method = TACACSPLUS, Privilege-Level = Minimum, Authentication-Type = PAP, Authentication-Service = PPP, User-Name = "bob", Client-Port = "tapioca/0", Remote-Address = "localhost", Argument-List = "service=ppp", Argument-List = "protocol=ip" +match Packet.Version-Major = Plus, Packet.Version-Minor = 0, Packet.Packet-Type = Authorization, Packet.Sequence-Number = 1, Packet.Flags = None, Packet.Session-Id = 3781589222, Packet.Length = 53, Packet-Body-Type = Request, Authentication-Method = TACACSPLUS, Privilege-Level = Minimum, Authentication-Type = PAP, Authentication-Service = PPP, User-Name = "bob", Client-Port = "tapioca/0", Remote-Address = "localhost", service = "ppp", protocol = "ip" encode-proto - match c0 02 01 00 e1 66 78 e6 00 00 00 35 4b c5 ea 62 13 cc ca a6 6a 03 3c 8e 3f c0 5a aa 46 da 12 cd ee 48 62 69 67 9a b8 b4 db 70 98 30 b7 fc f6 93 09 d4 3f 2c a9 58 9e 3c 6a 0e d5 50 20 e6 a5 39 46 @@ -44,7 +44,7 @@ match c0 02 01 00 e1 66 78 e6 00 00 00 35 4b c5 ea 62 13 cc ca a6 6a 03 3c 8e 3f # Authorization - Response: (Client <- Server) # decode-proto c0 02 02 00 e1 66 78 e6 00 00 00 13 02 59 f9 90 38 81 e1 bb 9d a6 13 93 fc 86 7e 4a 14 1c 24 -match Packet.Version-Major = Plus, Packet.Version-Minor = 0, Packet.Packet-Type = Authorization, Packet.Sequence-Number = 2, Packet.Flags = None, Packet.Session-Id = 3781589222, Packet.Length = 19, Packet-Body-Type = Response, Authorization-Status = Pass-Add, Server-Message = "", Data = 0x, Argument-List = "addr=1.2.3.4" +match Packet.Version-Major = Plus, Packet.Version-Minor = 0, Packet.Packet-Type = Authorization, Packet.Sequence-Number = 2, Packet.Flags = None, Packet.Session-Id = 3781589222, Packet.Length = 19, Packet-Body-Type = Response, Authorization-Status = Pass-Add, Server-Message = "", Data = 0x, addr = 1.2.3.4 encode-proto - match c0 02 02 00 e1 66 78 e6 00 00 00 13 02 59 f9 90 38 81 e1 bb 9d a6 13 93 fc 86 7e 4a 14 1c 24 @@ -63,7 +63,7 @@ match c0 02 02 04 e1 66 78 e6 00 00 00 13 02 59 f9 90 38 81 e1 bb 9d a6 13 93 fc # Accounting - Request: (Client -> Server) # decode-proto c0 03 01 00 07 9b 35 d9 00 00 00 5b 7c 8a 99 d6 88 f9 32 3c ec 34 6d 23 89 71 72 dd 89 46 75 df 9c 00 a5 96 28 05 fc 57 88 02 0c 11 a3 60 9a 05 8b 71 6d 27 ca 83 b0 ab 2f 00 27 c8 da 58 d3 1a f1 3f 07 17 8d f6 35 c5 7b e2 07 be 29 86 d4 93 16 99 04 01 ef 03 6c 1c 2b ad 3a fb 5b 11 06 61 dc d9 09 1d 6a 08 1e -match Packet.Version-Major = Plus, Packet.Version-Minor = 0, Packet.Packet-Type = Accounting, Packet.Sequence-Number = 1, Packet.Flags = None, Packet.Session-Id = 127612377, Packet.Length = 91, Packet-Body-Type = Request, Accounting-Flags = Start, Authentication-Method = TACACSPLUS, Privilege-Level = Minimum, Authentication-Type = PAP, Authentication-Service = PPP, User-Name = "bob", Client-Port = "tapioca/0", Remote-Address = "localhost", Argument-List = "start_time=1596565644", Argument-List = "task_id=17558", Argument-List = "service=ppp", Argument-List = "protocol=ip" +match Packet.Version-Major = Plus, Packet.Version-Minor = 0, Packet.Packet-Type = Accounting, Packet.Sequence-Number = 1, Packet.Flags = None, Packet.Session-Id = 127612377, Packet.Length = 91, Packet-Body-Type = Request, Accounting-Flags = Start, Authentication-Method = TACACSPLUS, Privilege-Level = Minimum, Authentication-Type = PAP, Authentication-Service = PPP, User-Name = "bob", Client-Port = "tapioca/0", Remote-Address = "localhost", start_time = "Aug 4 2020 18:27:24 UTC", task_id = "17558", service = "ppp", protocol = "ip" encode-proto - match c0 03 01 00 07 9b 35 d9 00 00 00 5a 7c 8a 99 d6 88 f9 32 3c ed 21 75 25 89 18 7f d0 9f 53 64 c6 9a 0c a7 d8 37 59 ff 5b 8a 0f 08 16 bf 67 9d 02 9e 62 6b 0c e1 9e b4 a3 77 0c 23 c4 d5 5b d0 19 f2 3d 07 57 98 e4 2d f1 4d ef 5e b2 2f 84 d4 9e 5d 8f 13 05 f0 09 6a 44 66 ad 3a fb 59 0c 1d 7a d0 d5 0a 4c 3e 11 diff --git a/src/tests/unit/protocols/tacacs/typed.txt b/src/tests/unit/protocols/tacacs/typed.txt.ignore similarity index 100% rename from src/tests/unit/protocols/tacacs/typed.txt rename to src/tests/unit/protocols/tacacs/typed.txt.ignore