]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Use valuebox code to parse config items
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 5 Jun 2022 01:51:51 +0000 (21:51 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sun, 5 Jun 2022 01:51:51 +0000 (21:51 -0400)
Removes a significant amount of code and adds support for a large number of additional configuration types

src/lib/server/cf_parse.c
src/lib/server/tmpl_eval.c
src/lib/util/value.c
src/lib/util/value.h

index 2354e90c9e5689e5135640fb137224811d4af624..26b63d3d5c339339c689a2e1a9fb9ea394831f80 100644 (file)
@@ -46,96 +46,52 @@ static char const parse_spaces[] = "
 #define PAIR_SPACE(_cs) ((_cs->depth + 1) * 2)
 #define SECTION_SPACE(_cs) (_cs->depth * 2)
 
-/** Validation function for ipaddr conf_file types
- *
- */
-static inline int CC_HINT(nonnull) fr_item_validate_ipaddr(CONF_SECTION *cs, char const *name,
-                                                          fr_type_t type, char const *value,
-                                                          fr_ipaddr_t *ipaddr)
+static void cf_pair_debug(CONF_SECTION const *cs, CONF_PAIR const *cp, fr_type_t type, bool secret)
 {
-       char ipbuf[128];
-
-       if (strcmp(value, "*") == 0) {
-               cf_log_debug(cs, "%.*s%s = *", PAIR_SPACE(cs), parse_spaces, name);
-       } else if (strspn(value, ".0123456789abdefABCDEF:%[]/") == strlen(value)) {
-               cf_log_debug(cs, "%.*s%s = %s", PAIR_SPACE(cs), parse_spaces, name, value);
-       } else {
-               cf_log_debug(cs, "%.*s%s = %s IPv%s address [%s]", PAIR_SPACE(cs), parse_spaces, name, value,
-                           (ipaddr->af == AF_INET ? "4" : " 6"), fr_inet_ntoh(ipaddr, ipbuf, sizeof(ipbuf)));
-       }
-
-       switch (type) {
-       case FR_TYPE_IPV4_ADDR:
-       case FR_TYPE_IPV6_ADDR:
-       case FR_TYPE_COMBO_IP_ADDR:
-               switch (ipaddr->af) {
-               case AF_INET:
-               if (ipaddr->prefix != 32) {
-                       ERROR("Invalid IPv4 mask length \"/%i\".  Only \"/32\" permitted for non-prefix types",
-                             ipaddr->prefix);
-
-                       return -1;
-               }
-                       break;
+       char const      *value;
+       char            *tmp = NULL;
+       char const      *quote = "";
 
-               case AF_INET6:
-               if (ipaddr->prefix != 128) {
-                       ERROR("Invalid IPv6 mask length \"/%i\".  Only \"/128\" permitted for non-prefix types",
-                             ipaddr->prefix);
-
-                       return -1;
-               }
-                       break;
-
-               default:
-                       return -1;
-               }
-               return 0;
-
-       case FR_TYPE_IPV4_PREFIX:
-       case FR_TYPE_IPV6_PREFIX:
-       case FR_TYPE_COMBO_IP_PREFIX:
-               return 0;
-
-       default:
-               fr_assert(0);
-               return -1;
-       }
-}
-
-static void cf_pair_debug(CONF_SECTION const *cs, CONF_PAIR const *cp, bool secret)
-{
        if (secret && (fr_debug_lvl < L_DBG_LVL_3)) {
                cf_log_debug(cs, "%.*s%s = <<< secret >>>", PAIR_SPACE(cs), parse_spaces, cp->attr);
+               return;
+       }
 
-       } else if (cp->rhs_quote == T_BARE_WORD) {
-               cf_log_debug(cs, "%.*s%s = %s", PAIR_SPACE(cs), parse_spaces, cp->attr, cp->value);
-
+       /*
+        *      Print the strings with the correct quotation character and escaping.
+        */
+       if (fr_type_is_string(type)) {
+               value = tmp = fr_asprint(NULL, cp->value, talloc_array_length(cp->value) - 1, cp->rhs_quote);
        } else {
-               /*
-                *      Print the strings with the correct quotation character and escaping.
-                */
-               char *tmp = fr_asprint(NULL, cp->value, talloc_array_length(cp->value) - 1, cp->rhs_quote);
-               char quote;
+               value = cf_pair_value(cp);
+       }
 
-               switch (cp->rhs_quote) {
+       if (fr_type_is_quoted(type)) {
+               switch (cf_pair_value_quote(cp)) {
                default:
+                       break;
+
                case T_DOUBLE_QUOTED_STRING:
-                       quote = '"';
+                       quote = "\"";
                        break;
 
                case T_SINGLE_QUOTED_STRING:
-                       quote = '\'';
+                       quote = "'";
                        break;
 
                case T_BACK_QUOTED_STRING:
-                       quote = '`';
+                       quote = "`";
                        break;
-               }
 
-               cf_log_debug(cs, "%.*s%s = %c%s%c", PAIR_SPACE(cs), parse_spaces, cp->attr, quote, tmp, quote);
-               talloc_free(tmp);
+               case T_SOLIDUS_QUOTED_STRING:
+                       quote = "/";
+                       break;
+               }
        }
+
+       cf_log_debug(cs, "%.*s%s = %s%s%s", PAIR_SPACE(cs), parse_spaces, cp->attr, quote, value, quote);
+
+       talloc_free(tmp);;
 }
 
 /** Parses a #CONF_PAIR into a C data type
@@ -218,7 +174,7 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
                                        };
                fr_sbuff_t              sbuff = FR_SBUFF_IN(cp->value, strlen(cp->value));
 
-               if (!cp->printed) cf_pair_debug(cs, cp, secret);
+               if (!cp->printed) cf_pair_debug(cs, cp, FR_TYPE_STRING, secret);
 
                /*
                 *      Parse the cast operator for barewords
@@ -276,266 +232,44 @@ int cf_pair_parse_value(TALLOC_CTX *ctx, void *out, UNUSED void *base, CONF_ITEM
                goto finish;
        }
 
-       switch (type) {
-       case FR_TYPE_BOOL:
-               /*
-                *      Allow yes/no, true/false, and on/off
-                */
-               if ((strcasecmp(cp->value, "yes") == 0) ||
-                   (strcasecmp(cp->value, "true") == 0) ||
-                   (strcasecmp(cp->value, "on") == 0)) {
-                       *(bool *)out = true;
-               } else if ((strcasecmp(cp->value, "no") == 0) ||
-                          (strcasecmp(cp->value, "false") == 0) ||
-                          (strcasecmp(cp->value, "off") == 0)) {
-                       *(bool *)out = false;
-               } else {
-                       cf_log_err(cs, "Invalid value \"%s\" for boolean variable %s",
-                                  cp->value, cp->attr);
-                       goto error;
-               }
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %s", PAIR_SPACE(cs), parse_spaces, cp->attr, cp->value);
-               break;
-
-       case FR_TYPE_UINT32:
-       {
-               unsigned long v = strtoul(cp->value, 0, 0);
-
-               /*
-                *      Restrict integer values to 0-INT32_MAX, this means
-                *      it will always be safe to cast them to a signed type
-                *      for comparisons, and imposes the same range limit as
-                *      before we switched to using an unsigned type to
-                *      represent config item integers.
-                */
-               if (v > INT32_MAX) {
-                       cf_log_err(cs, "Invalid value \"%s\" for variable %s, must be between 0-%u", cp->value,
-                                  cp->attr, INT32_MAX);
-                       goto error;
-               }
-
-               *(uint32_t *)out = v;
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %u", PAIR_SPACE(cs), parse_spaces, cp->attr, *(uint32_t *)out);
-       }
-               break;
-
-       case FR_TYPE_UINT8:
-       {
-               unsigned long v = strtoul(cp->value, 0, 0);
-
-               if (v > UINT8_MAX) {
-                       cf_log_err(cs, "Invalid value \"%s\" for variable %s, must be between 0-%u", cp->value,
-                                  cp->attr, UINT8_MAX);
-                       goto error;
-               }
-               *(uint8_t *)out = (uint8_t) v;
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %u", PAIR_SPACE(cs), parse_spaces, cp->attr, *(uint8_t *)out);
-       }
-               break;
-
-       case FR_TYPE_UINT16:
+       /*
+        *      Parse as a boxed value out of sheer laziness...
+        *
+        *      Then we get all the internal types for free, and only need to add
+        *      one set of printing and parsing functions for new types...
+        */
        {
-               unsigned long v = strtoul(cp->value, 0, 0);
-
-               if (v > UINT16_MAX) {
-                       cf_log_err(cs, "Invalid value \"%s\" for variable %s, must be between 0-%u", cp->value,
-                                  cp->attr, UINT16_MAX);
-                       goto error;
-               }
-               *(uint16_t *)out = (uint16_t) v;
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %u", PAIR_SPACE(cs), parse_spaces, cp->attr, *(uint16_t *)out);
-       }
-               break;
+               fr_value_box_t  vb;
 
-       case FR_TYPE_UINT64:
-               *(uint64_t *)out = strtoull(cp->value, NULL, 0);
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %" PRIu64, PAIR_SPACE(cs), parse_spaces, cp->attr, *(uint64_t *)out);
-               break;
+               if (fr_value_box_from_str(ctx, &vb, type, NULL,
+                                         cf_pair_value(cp), strlen(cf_pair_value(cp)), NULL, false) < 0) {
+                       cf_log_perr(cs, "Invalid value \"%s\" for config item %s",
+                                   cp->value, cp->attr);
 
-       case FR_TYPE_SIZE:
-       {
-               if (fr_size_from_str((size_t *)out, cp->value) < 0) {
-                       cf_log_perr(cs, "Invalid value \"%s\" for variable %s", cp->value, cp->attr);
-                       goto error;
+                       return -1;
                }
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %zu", PAIR_SPACE(cs), parse_spaces, cp->attr, *(size_t *)out);
-               break;
-       }
 
-       case FR_TYPE_INT32:
-               *(int32_t *)out = strtol(cp->value, NULL, 10);
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %d", PAIR_SPACE(cs), parse_spaces, cp->attr, *(int32_t *)out);
-               break;
-
-       case FR_TYPE_STRING:
-       {
-               char **str = out;
-
-               if (!cp->printed) cf_pair_debug(cs, cp, secret);
+               if (!cp->printed) cf_pair_debug(cs, cp, type, secret);
 
                /*
-                *      If there's out AND it's an input file, check
-                *      that we can read it.  This check allows errors
-                *      to be caught as early as possible, during
-                *      server startup.
+                *      Strings can be file paths...
                 */
-               if (file_input && !cf_file_check(cs, cp->value, true)) goto error;
-               if (file_exists && !cf_file_check(cs, cp->value, false)) goto error;
-
-               /*
-                *      Free any existing buffers
-                */
-               talloc_free(*str);
-               *str = talloc_typed_strdup(cs, cp->value);
-       }
-               break;
-
-       case FR_TYPE_IPV4_ADDR:
-       case FR_TYPE_IPV4_PREFIX:
-       {
-               fr_ipaddr_t *ipaddr = out;
-
-               if (fr_inet_pton4(ipaddr, cp->value, -1, true, false, true) < 0) {
-                       cf_log_perr(cp, "Failed parsing config item");
-                       goto error;
-               }
-               /* Also prints the IP to the log */
-               if (fr_item_validate_ipaddr(cs, cp->attr, type, cp->value, ipaddr) < 0) goto error;
-       }
-               break;
-
-       case FR_TYPE_IPV6_ADDR:
-       case FR_TYPE_IPV6_PREFIX:
-       {
-               fr_ipaddr_t *ipaddr = out;
-
-               if (fr_inet_pton6(ipaddr, cp->value, -1, true, false, true) < 0) {
-                       cf_log_perr(cp, "Failed parsing config item");
-                       goto error;
-               }
-               /* Also prints the IP to the log */
-               if (fr_item_validate_ipaddr(cs, cp->attr, type, cp->value, ipaddr) < 0) goto error;
-       }
-               break;
-
-       case FR_TYPE_COMBO_IP_ADDR:
-       case FR_TYPE_COMBO_IP_PREFIX:
-       {
-               fr_ipaddr_t     *ipaddr = out;
-               int             af = AF_UNSPEC;
-               fr_type_t       our_type = FR_TYPE_NULL;
-               fr_sbuff_t      sbuff = FR_SBUFF_IN(cp->value, strlen(cp->value));
-               tmpl_rules_t    rules = {};
-
-               slen = tmpl_cast_from_substr(&rules, &sbuff);
-               if (slen < 0) {
-                       cf_log_perr(cp, "Failed parsing config item");
-                       goto error;
-               }
-               our_type = rules.cast;
-
-               if (slen > 0) {
-                       if (type == FR_TYPE_COMBO_IP_ADDR) {
-                               switch (our_type) {
-                               case FR_TYPE_IPV4_ADDR:
-                                       af = AF_INET;
-                                       break;
-
-                               case FR_TYPE_IPV6_ADDR:
-                                       af = AF_INET6;
-                                       break;
-
-                               default:
-                                       cf_log_perr(cp, "Invalid cast, expecting 'ipv4addr' or 'ipv6addr'");
-                                       goto error;
-                               }
-                       } else if (type == FR_TYPE_COMBO_IP_PREFIX) {
-                               switch (our_type) {
-                               case FR_TYPE_IPV4_PREFIX:
-                                       af = AF_INET;
-                                       break;
-
-                               case FR_TYPE_IPV6_PREFIX:
-                                       af = AF_INET6;
-                                       break;
-
-                               default:
-                                       cf_log_perr(cp, "Invalid cast, expecting 'ipv4prefix' or 'ipv6prefix'");
-                                       goto error;
-                               }
+               if (fr_type_is_string(type)) {
+                       /*
+                        *      If there's out AND it's an input file, check
+                        *      that we can read it.  This check allows errors
+                        *      to be caught as early as possible, during
+                        *      server startup.
+                        */
+                       if (file_input && !cf_file_check(cs, cp->value, true)) {
+                       value_error:
+                               fr_value_box_clear_value(&vb);
+                               goto error;
                        }
+                       if (file_exists && !cf_file_check(cs, cp->value, false)) goto value_error;
                }
 
-               if (fr_inet_pton(ipaddr, cp->value, -1, af, true, true) < 0) {
-                       cf_log_perr(cp, "Failed parsing config item");
-                       goto error;
-               }
-               /* Also prints the IP to the log */
-               if (fr_item_validate_ipaddr(cs, cp->attr, type, cp->value, ipaddr) < 0) {
-                       goto error;
-               }
-       }
-               break;
-
-       case FR_TYPE_TIME_DELTA:
-       {
-               fr_time_delta_t delta;
-
-               if (fr_time_delta_from_str(&delta, cp->value, strlen(cp->value), FR_TIME_RES_SEC) < 0) {
-                       cf_log_perr(cp, "Failed parsing config item");
-                       goto error;
-               }
-
-               if (!cp->printed) {
-                       char *p;
-                       fr_value_box_aprint(NULL, &p, fr_box_time_delta(delta), NULL);
-                       cf_log_debug(cs, "%.*s%s = %s", PAIR_SPACE(cs), parse_spaces, cp->attr, p);
-                       talloc_free(p);
-               }
-
-               memcpy(out, &delta, sizeof(delta));
-       }
-               break;
-
-       case FR_TYPE_FLOAT32:
-       {
-               float num;
-
-               if (sscanf(cp->value, "%f", &num) != 1) {
-                       cf_log_err(cp, "Failed parsing floating point number");
-                       goto error;
-               }
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %f", PAIR_SPACE(cs), parse_spaces, cp->attr,
-                                              (double) num);
-               memcpy(out, &num, sizeof(num));
-       }
-               break;
-
-       case FR_TYPE_FLOAT64:
-       {
-               double num;
-
-               if (sscanf(cp->value, "%lf", &num) != 1) {
-                       cf_log_err(cp, "Failed parsing floating point number");
-                       goto error;
-               }
-               if (!cp->printed) cf_log_debug(cs, "%.*s%s = %f", PAIR_SPACE(cs), parse_spaces, cp->attr, num);
-               memcpy(out, &num, sizeof(num));
-       }
-               break;
-
-       default:
-               /*
-                *      If we get here, it's a sanity check error.
-                *      It's not an error parsing the configuration
-                *      file.
-                */
-               fr_assert(type > FR_TYPE_NULL);
-               fr_assert(type < FR_TYPE_MAX);
-
-               cf_log_err(cp, "type '%s' (%i) is not supported in the configuration files",
-                          fr_type_to_str(type), type);
-               goto error;
+               if (fr_value_box_memcpy_out(out, &vb) < 0) goto value_error;
        }
 
 finish:
index f9bd99227b5e1a4e5d0a84f33e9347270a33804a..61dfcca83a8f8c0b87035342d91a64ebcb288f4f 100644 (file)
@@ -559,7 +559,7 @@ ssize_t _tmpl_to_type(void *out,
        RDEBUG4("Copying %zu bytes to %p from offset %zu",
                fr_value_box_field_sizes[dst_type], *((void **)out), fr_value_box_offsets[dst_type]);
 
-       fr_value_box_memcpy_out(out, from_cast, dst_type);
+       fr_value_box_memcpy_out(out, from_cast);
 
        return from_cast->vb_length;
 }
@@ -810,7 +810,7 @@ ssize_t _tmpl_to_atype(TALLOC_CTX *ctx, void *out,
        RDEBUG4("Copying %zu bytes to %p from offset %zu",
                fr_value_box_field_sizes[dst_type], *((void **)out), fr_value_box_offsets[dst_type]);
 
-       fr_value_box_memcpy_out(out, &from_cast, dst_type);
+       fr_value_box_memcpy_out(out, &from_cast);
 
        /*
         *      Frees any memory allocated for temporary buffers
index 1c2e567f8b06224408d9a94a0af0b20f8810515a..bf26a7de88d43f82e9021211e200bbebaca7ea70 100644 (file)
@@ -1974,7 +1974,7 @@ ssize_t fr_value_box_from_network(TALLOC_CTX *ctx,
        return fr_dbuff_set(dbuff, &work_dbuff);
 }
 
-/* Get a key from a value box
+/** Get a key from a value box
  *
  * @param[in,out] out - set to a small buffer on input.  If the callback has more data
  *               than is available here, the callback can update "out" to point elsewhere
@@ -2039,7 +2039,6 @@ int fr_value_box_to_key(uint8_t **out, size_t *outlen, fr_value_box_t const *val
        return 0;
 }
 
-
 /** Convert octets to a fixed size value box value
  *
  * All fixed size types are allowed.
@@ -3548,7 +3547,7 @@ int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t
 {
        switch (src->type) {
        default:
-               fr_value_box_memcpy_out(fr_value_box_raw(dst, src->type), src, src->type);
+               fr_value_box_memcpy_out(fr_value_box_raw(dst, src->type), src);
                fr_value_box_copy_meta(dst, src);
                break;
 
index d9737dd3b92f76329a579c541a11aaf13ab311a0..85cb8bfff9f3218e4e7b82250047096e1fca95cc 100644 (file)
@@ -511,13 +511,38 @@ uint8_t *fr_value_box_raw(fr_value_box_t const *vb, fr_type_t type)
        return UNCONST(uint8_t *, vb) + fr_value_box_offsets[type];
 }
 
-/** Copy raw values out of a value box
+/** Copy the value of a value box to a field in a C struct
  *
+ * This is useful when interacting with 3rd party libraries, and doing configuration parsing
+ * as it allows us to use standard parsing and casting functions and then emit the result
+ * as a C value.
+ *
+ * The field pointed to by out must be of the same type as we use to represent the value boxe's
+ * value in its datum union, or at least the same size.
+ *
+ * No checks are done to ensure this is the case, so if you get this wrong it'll lead to silent
+ * memory corruption.
+ *
+ * @param[out] out     Field in struct to write variable to.
+ * @param[in] vb       to copy value from.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
  */
 static inline CC_HINT(always_inline)
-void fr_value_box_memcpy_out(void *dst, fr_value_box_t const *vb, fr_type_t type)
+int fr_value_box_memcpy_out(void *out, fr_value_box_t const *vb)
 {
-       memcpy(dst, ((uint8_t const *)vb) + fr_value_box_offsets[type], fr_value_box_field_sizes[type]);
+       size_t len;
+
+       len = fr_value_box_field_sizes[vb->type];
+       if (len == 0) {
+               fr_strerror_printf("Type %s not supported for conversion to C type", fr_type_to_str(vb->type));
+               return -1;
+       }
+
+       memcpy(out, ((uint8_t const *)vb) + fr_value_box_offsets[vb->type], len);
+
+       return 0;
 }