From: Arran Cudbard-Bell Date: Mon, 24 Jan 2022 21:25:41 +0000 (-0600) Subject: Apply validation to enum value names X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=278df9fa2a80f47dd277a265fee4fdecd7ae0e57;p=thirdparty%2Ffreeradius-server.git Apply validation to enum value names --- diff --git a/share/dictionary/radius/dictionary.ascend b/share/dictionary/radius/dictionary.ascend index f69423ab46..0564b0ff7a 100644 --- a/share/dictionary/radius/dictionary.ascend +++ b/share/dictionary/radius/dictionary.ascend @@ -815,9 +815,9 @@ VALUE IP-TOS-Precedence IP-TOS-Precedence-Pri-Three 96 VALUE IP-TOS-Precedence IP-TOS-Precedence-Pri-Two 64 VALUE IPX-Header-Compression IPX-Header-Compression-No 0 VALUE IPX-Header-Compression IPX-Header-Compression-Yes 1 -VALUE NAS-Port-Format 1_2_2 3 -VALUE NAS-Port-Format 2_4_5_5 2 -VALUE NAS-Port-Format 2_4_6_4 1 +VALUE NAS-Port-Format x_1_2_2 3 +VALUE NAS-Port-Format x_4_5_5 2 +VALUE NAS-Port-Format x_4_6_4 1 VALUE NAS-Port-Format Unknown 0 VALUE Numbering-Plan-ID ISDN-Numbering-Plan 1 VALUE Numbering-Plan-ID Private-Numbering-Plan 9 diff --git a/share/dictionary/radius/dictionary.usr b/share/dictionary/radius/dictionary.usr index 027f36b1e9..6de3e6ae20 100644 --- a/share/dictionary/radius/dictionary.usr +++ b/share/dictionary/radius/dictionary.usr @@ -544,10 +544,10 @@ VALUE Event-Id DS0s-In-Service 77 VALUE Event-Id DS0s-Out-of-Service 78 VALUE Event-Id T1/T1PRI/E1PRI-Call-Event 79 VALUE Event-Id Psu-Incompatible 80 -VALUE Event-Id T1,T1-E1/PRI-Call-Arrive-Event 81 -VALUE Event-Id T1,T1-E1/PRI-Call-Connect-Event 82 -VALUE Event-Id T1,T1-E1/PRI-Call-Termina-Event 83 -VALUE Event-Id T1,T1-E1/PRI-Call-Failed-Event 84 +VALUE Event-Id T1/T1-E1/PRI-Call-Arrive-Event 81 +VALUE Event-Id T1/T1-E1/PRI-Call-Connect-Event 82 +VALUE Event-Id T1/T1-E1/PRI-Call-Termina-Event 83 +VALUE Event-Id T1/T1-E1/PRI-Call-Failed-Event 84 VALUE Event-Id DNS-Contact-Lost 85 VALUE Event-Id NTP-Contact-Lost 86 VALUE Event-Id NTP-Contact-Restored 87 @@ -1390,8 +1390,8 @@ VALUE Request-Type Tacacs-Message 253 VALUE Request-Type Reserved 255 VALUE Speed-Of-Connection Auto 0 -VALUE Speed-Of-Connection 56 1 -VALUE Speed-Of-Connection 64 2 +VALUE Speed-Of-Connection 56K 1 +VALUE Speed-Of-Connection 64K 2 VALUE Speed-Of-Connection Voice 3 VALUE Expansion-Algorithm Constant 1 diff --git a/src/lib/util/dict.h b/src/lib/util/dict.h index 21a3e3b3a1..563b0216ba 100644 --- a/src/lib/util/dict.h +++ b/src/lib/util/dict.h @@ -323,6 +323,11 @@ typedef struct fr_dict_gctx_s fr_dict_gctx_t; */ extern bool const fr_dict_attr_allowed_chars[UINT8_MAX + 1]; +/** Characters that are allowed in dictionary enumeration value names + * + */ +extern bool const fr_dict_enum_allowed_chars[UINT8_MAX + 1]; + /** @name Dictionary structure extensions * * @{ @@ -561,6 +566,11 @@ char const *fr_dict_enum_name_by_value(fr_dict_attr_t const *da, fr_value_box_t fr_dict_enum_value_t *fr_dict_enum_by_name(fr_dict_attr_t const *da, char const *name, ssize_t len); ssize_t fr_dict_enum_by_name_substr(fr_dict_enum_value_t **out, fr_dict_attr_t const *da, fr_sbuff_t *in); + +fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_t *in, fr_sbuff_term_t const *tt); + +static inline fr_slen_t fr_dict_enum_name_afrom_substr(TALLOC_CTX *ctx, char **out, fr_sbuff_t *in, fr_sbuff_term_t const *tt) +SBUFF_OUT_TALLOC_FUNC_NO_LEN_DEF(fr_dict_enum_name_from_substr, in, tt) /** @} */ /** @name Dictionary and protocol loading diff --git a/src/lib/util/dict_tokenize.c b/src/lib/util/dict_tokenize.c index f4228729a7..a41bb9f92d 100644 --- a/src/lib/util/dict_tokenize.c +++ b/src/lib/util/dict_tokenize.c @@ -1254,6 +1254,7 @@ static int dict_read_process_value(dict_tokenize_ctx_t *ctx, char **argv, int ar { fr_dict_attr_t *da; fr_value_box_t value; + fr_slen_t enum_len; fr_dict_attr_t const *parent = ctx->stack[ctx->stack_depth].da; if (argc != 3) { @@ -1281,6 +1282,15 @@ static int dict_read_process_value(dict_tokenize_ctx_t *ctx, char **argv, int ar } da = ctx->value_attr; + /* + * Verify the enum name matches the expected from. + */ + enum_len = (fr_slen_t)strlen(argv[1]); + if (fr_dict_enum_name_from_substr(NULL, &FR_SBUFF_IN(argv[1], enum_len), NULL) != enum_len) { + fr_strerror_printf_push("Invalid VALUE name '%s' for attribute '%s'", argv[1], da->name); + return -1; + } + /* * Remember which attribute is associated with this * value. This allows us to define enum @@ -1303,18 +1313,12 @@ static int dict_read_process_value(dict_tokenize_ctx_t *ctx, char **argv, int ar } /* - * Only a few data types can have VALUEs defined. + * Only a leaf types can have values defined. */ - switch (da->type) { - case FR_TYPE_STRUCTURAL: - case FR_TYPE_NULL: - case FR_TYPE_MAX: + if (!fr_type_is_leaf(da->type)) { fr_strerror_printf_push("Cannot define VALUE for attribute '%s' of data type '%s'", da->name, fr_table_str_by_value(fr_value_box_type_table, da->type, "")); return -1; - - default: - break; } if (fr_value_box_from_str(NULL, &value, da->type, NULL, diff --git a/src/lib/util/dict_util.c b/src/lib/util/dict_util.c index b90c8278b0..b982bbbbe1 100644 --- a/src/lib/util/dict_util.c +++ b/src/lib/util/dict_util.c @@ -55,6 +55,27 @@ bool const fr_dict_attr_allowed_chars[UINT8_MAX + 1] = { ['z'] = true }; +/** Characters allowed in enumeration value names + * + */ +bool const fr_dict_enum_allowed_chars[UINT8_MAX + 1] = { + ['+'] = true, ['-'] = true, ['.'] = true, ['/'] = true, ['_'] = true, + ['0'] = true, ['1'] = true, ['2'] = true, ['3'] = true, ['4'] = true, + ['5'] = true, ['6'] = true, ['7'] = true, ['8'] = true, ['9'] = true, + ['A'] = true, ['B'] = true, ['C'] = true, ['D'] = true, ['E'] = true, + ['F'] = true, ['G'] = true, ['H'] = true, ['I'] = true, ['J'] = true, + ['K'] = true, ['L'] = true, ['M'] = true, ['N'] = true, ['O'] = true, + ['P'] = true, ['Q'] = true, ['R'] = true, ['S'] = true, ['T'] = true, + ['U'] = true, ['V'] = true, ['W'] = true, ['X'] = true, ['Y'] = true, + ['Z'] = true, + ['a'] = true, ['b'] = true, ['c'] = true, ['d'] = true, ['e'] = true, + ['f'] = true, ['g'] = true, ['h'] = true, ['i'] = true, ['j'] = true, + ['k'] = true, ['l'] = true, ['m'] = true, ['n'] = true, ['o'] = true, + ['p'] = true, ['q'] = true, ['r'] = true, ['s'] = true, ['t'] = true, + ['u'] = true, ['v'] = true, ['w'] = true, ['x'] = true, ['y'] = true, + ['z'] = true +}; + /* * Create the hash of the name. * @@ -2926,6 +2947,83 @@ ssize_t fr_dict_enum_by_name_substr(fr_dict_enum_value_t **out, fr_dict_attr_t c return 0; } +/** Extract an enumeration name from a string + * + * This function defines the canonical format for an enumeration name. + * + * An enumeration name is made up of one or more fr_dict_attr_allowed_chars + * with at least one character in the sequence not being a special character + * i.e. [-+/_] or a number. + * + * This disambiguates enumeration identifiers from mathematical expressions. + * + * If we allowed enumeration names consisting of sequences of numbers separated + * by special characters it would not be possible to determine if the special + * character were an operator in a subexpression. + * + * For example take: + * + * &My-Enum-Attr == 01234-5678 + * + * Without having access to the enumeration values of My-Enum-Attr (which we + * might not have during tokenisation), we cannot tell if this is: + * + * (&My-Enum-Attr == 01234-5678) + * + * OR + * + * ((&My-Enum-Attr == 01234) - 5678) + * + * If an alpha character occurs anywhere in the string i.e: + * + * (&My-Enum-Attr == 01234-A5678) + * + * we know 01234-A5678 can't be a mathematical sub-expression because the + * second potential operand can no longer be parsed as an integer constant. + * + * @param[out] out The name string we managed to extract. + * May be NULL in which case only the length of the name + * will be returned. + * @param[in] in The string containing the enum identifier. + * @param[in] tt If non-null verify that a terminal sequence occurs + * after the enumeration name. + * @return + * - <0 the offset at which the parse error occurred. + * - >1 the number of bytes parsed. + */ +fr_slen_t fr_dict_enum_name_from_substr(fr_sbuff_t *out, fr_sbuff_t *in, fr_sbuff_term_t const *tt) +{ + fr_sbuff_t our_in = FR_SBUFF(in); + bool seen_alpha = false; + + while (fr_sbuff_is_in_charset(&our_in, fr_dict_enum_allowed_chars)) { + if (fr_sbuff_is_alpha(&our_in)) seen_alpha = true; + fr_sbuff_next(&our_in); + } + + if (!seen_alpha) { + if (fr_sbuff_used(&our_in) == 0) { + fr_strerror_const("VALUE name empty"); + return -1; + } + + fr_strerror_const("VALUE name must contain at least one alpha character"); + return -1; + } + + /* + * Check that the sequence is correctly terminated + */ + if (tt && !fr_sbuff_is_terminal(&our_in, tt)) { + fr_strerror_const("VALUE name has trailing text"); + return fr_sbuff_error(&our_in); + } + + if (out) return fr_sbuff_out_bstrncpy_exact(out, in, fr_sbuff_used(&our_in)); + + return fr_sbuff_set(in, &our_in); +} + int dict_dlopen(fr_dict_t *dict, char const *name) { char *module_name;