From: Arran Cudbard-Bell Date: Sun, 5 Jun 2022 01:51:51 +0000 (-0400) Subject: Use valuebox code to parse config items X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2f5f30197e0a0fc3550752e6cd97709deb821c40;p=thirdparty%2Ffreeradius-server.git Use valuebox code to parse config items Removes a significant amount of code and adds support for a large number of additional configuration types --- diff --git a/src/lib/server/cf_parse.c b/src/lib/server/cf_parse.c index 2354e90c9e5..26b63d3d5c3 100644 --- a/src/lib/server/cf_parse.c +++ b/src/lib/server/cf_parse.c @@ -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: diff --git a/src/lib/server/tmpl_eval.c b/src/lib/server/tmpl_eval.c index f9bd99227b5..61dfcca83a8 100644 --- a/src/lib/server/tmpl_eval.c +++ b/src/lib/server/tmpl_eval.c @@ -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 diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 1c2e567f8b0..bf26a7de88d 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -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; diff --git a/src/lib/util/value.h b/src/lib/util/value.h index d9737dd3b92..85cb8bfff9f 100644 --- a/src/lib/util/value.h +++ b/src/lib/util/value.h @@ -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; }