]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Apply validation to enum value names
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 24 Jan 2022 21:25:41 +0000 (15:25 -0600)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Mon, 24 Jan 2022 21:25:41 +0000 (15:25 -0600)
share/dictionary/radius/dictionary.ascend
share/dictionary/radius/dictionary.usr
src/lib/util/dict.h
src/lib/util/dict_tokenize.c
src/lib/util/dict_util.c

index f69423ab46af77b47826cac84d538169521a75e8..0564b0ff7a23d08001c95b856e5a6ca0ba4c357d 100644 (file)
@@ -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
index 027f36b1e9390acf857dfc16b1d9df8a80756e8a..6de3e6ae20b9d164dc13cb2228f609dbeecd6d6f 100644 (file)
@@ -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
index 21a3e3b3a1b70430f8ef735b5c5477e1007586f0..563b0216ba4c1f0283fae52484b09ecb5c9f896a 100644 (file)
@@ -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
index f4228729a7f3d4ea38f067533a9140bc43201dba..a41bb9f92d8db3f0944dc2e5bfa90ec6d6562ec6 100644 (file)
@@ -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, "<INVALID>"));
                return -1;
-
-       default:
-               break;
        }
 
        if (fr_value_box_from_str(NULL, &value, da->type, NULL,
index b90c8278b017341e81c07fc2d0c7fb596507351b..b982bbbbe1ef4b2be545f72c75f7ac6f10103523 100644 (file)
@@ -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;