[wireless-regdb] [PATCH] reglib: Validate all structure and array lengths

Luis R. Rodriguez mcgrof at do-not-panic.com
Tue Jul 16 21:31:36 EDT 2013


On Sun, Jun 30, 2013 at 4:49 PM, Ben Hutchings <ben at decadent.org.uk> wrote:
> Add checks that:
> - Signature length does not exceed the file length (this was already
>   checked, but did not account for signature lengths greater than 2 GB)
> - Database length is long enough for all structures we expect in it
> - Array length calculations will not overflow
>
> To keep these checks simple, change the types of array length and index
> variables to unsigned int (must be at least 32-bit, matching the file
> format) and the types of byte-length variables to size_t.
>
> Alexandre Rebert <alexandre at cmu.edu> reported and provided a test case
> for the signature length issue; the others I found by inspection.
>
> Signed-off-by: Ben Hutchings <ben at decadent.org.uk>

Thanks! Despite the fact you didn't resend for a wider review and I
would have preferred this split up into a few patches this has been
sitting on wireless-regdb for a while, so after my review I just
applied and pushed. Thanks again!

  Luis

> ---
>  reglib.c | 58 +++++++++++++++++++++++++++++++++++++++++-----------------
>  reglib.h |  5 +++--
>  2 files changed, 44 insertions(+), 19 deletions(-)
>
> diff --git a/reglib.c b/reglib.c
> index c4d00f8..224821b 100644
> --- a/reglib.c
> +++ b/reglib.c
> @@ -10,6 +10,7 @@
>  #include <stdbool.h>
>  #include <unistd.h>
>  #include <string.h>
> +#include <limits.h>
>
>  #include <arpa/inet.h> /* ntohl */
>
> @@ -37,10 +38,16 @@
>  #include "keys-gcrypt.c"
>  #endif
>
> -void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr)
> +void *
> +reglib_get_file_ptr(uint8_t *db, size_t dblen, size_t structlen, uint32_t ptr)
>  {
>         uint32_t p = ntohl(ptr);
>
> +       if (structlen > dblen) {
> +               fprintf(stderr, "Invalid database file, too short!\n");
> +               exit(3);
> +       }
> +
>         if (p > dblen - structlen) {
>                 fprintf(stderr, "Invalid database file, bad pointer!\n");
>                 exit(3);
> @@ -49,6 +56,17 @@ void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr)
>         return (void *)(db + p);
>  }
>
> +static size_t
> +reglib_array_len(size_t baselen, unsigned int elemcount, size_t elemlen)
> +{
> +       if (elemcount > (SIZE_MAX - baselen) / elemlen) {
> +               fprintf(stderr, "Invalid database file, count too large!\n");
> +               exit(3);
> +       }
> +
> +       return baselen + elemcount * elemlen;
> +}
> +
>  /*
>   * reglib_verify_db_signature():
>   *
> @@ -59,7 +77,7 @@ void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr)
>   */
>
>  #ifdef USE_OPENSSL
> -int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
> +int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
>  {
>         RSA *rsa;
>         uint8_t hash[SHA_DIGEST_LENGTH];
> @@ -118,7 +136,7 @@ out:
>  #endif /* USE_OPENSSL */
>
>  #ifdef USE_GCRYPT
> -int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
> +int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
>  {
>         gcry_mpi_t mpi_e, mpi_n;
>         gcry_sexp_t rsa, signature, data;
> @@ -180,7 +198,7 @@ out:
>  #endif /* USE_GCRYPT */
>
>  #if !defined(USE_OPENSSL) && !defined(USE_GCRYPT)
> -int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
> +int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
>  {
>         return 1;
>  }
> @@ -220,7 +238,7 @@ const struct reglib_regdb_ctx *reglib_malloc_regdb_ctx(const char *regdb_file)
>                 return NULL;
>         }
>
> -       ctx->header = reglib_get_file_ptr(ctx->db, ctx->dblen,
> +       ctx->header = reglib_get_file_ptr(ctx->db, ctx->real_dblen,
>                                           sizeof(struct regdb_file_header),
>                                           0);
>         header = ctx->header;
> @@ -232,12 +250,13 @@ const struct reglib_regdb_ctx *reglib_malloc_regdb_ctx(const char *regdb_file)
>                 goto err_out;
>
>         ctx->siglen = ntohl(header->signature_length);
> -       /* The actual dblen does not take into account the signature */
> -       ctx->dblen = ctx->real_dblen - ctx->siglen;
>
> -       if (ctx->dblen <= sizeof(*header))
> +       if (ctx->siglen > ctx->real_dblen - sizeof(*header))
>                 goto err_out;
>
> +       /* The actual dblen does not take into account the signature */
> +       ctx->dblen = ctx->real_dblen - ctx->siglen;
> +
>         /* verify signature */
>         if (!reglib_verify_db_signature(ctx->db, ctx->dblen, ctx->siglen))
>                 goto err_out;
> @@ -272,7 +291,7 @@ void reglib_free_regdb_ctx(const struct reglib_regdb_ctx *regdb_ctx)
>         free(ctx);
>  }
>
> -static void reg_rule2rd(uint8_t *db, int dblen,
> +static void reg_rule2rd(uint8_t *db, size_t dblen,
>         uint32_t ruleptr, struct ieee80211_reg_rule *rd_reg_rule)
>  {
>         struct regdb_file_reg_rule *rule;
> @@ -303,18 +322,21 @@ country2rd(const struct reglib_regdb_ctx *ctx,
>  {
>         struct regdb_file_reg_rules_collection *rcoll;
>         struct ieee80211_regdomain *rd;
> -       int i, num_rules, size_of_rd;
> +       unsigned int i, num_rules;
> +       size_t size_of_rd;
>
>         rcoll = reglib_get_file_ptr(ctx->db, ctx->dblen, sizeof(*rcoll),
>                                     country->reg_collection_ptr);
>         num_rules = ntohl(rcoll->reg_rule_num);
>         /* re-get pointer with sanity checking for num_rules */
>         rcoll = reglib_get_file_ptr(ctx->db, ctx->dblen,
> -                       sizeof(*rcoll) + num_rules * sizeof(uint32_t),
> -                       country->reg_collection_ptr);
> +                                   reglib_array_len(sizeof(*rcoll), num_rules,
> +                                                    sizeof(uint32_t)),
> +                                   country->reg_collection_ptr);
>
> -       size_of_rd = sizeof(struct ieee80211_regdomain) +
> -               num_rules * sizeof(struct ieee80211_reg_rule);
> +       size_of_rd = reglib_array_len(sizeof(struct ieee80211_regdomain),
> +                                     num_rules,
> +                                     sizeof(struct ieee80211_reg_rule));
>
>         rd = malloc(size_of_rd);
>         if (!rd)
> @@ -468,7 +490,8 @@ struct ieee80211_regdomain *
>  reglib_intersect_rds(const struct ieee80211_regdomain *rd1,
>                      const struct ieee80211_regdomain *rd2)
>  {
> -       int r, size_of_regd;
> +       int r;
> +       size_t size_of_regd;
>         unsigned int x, y;
>         unsigned int num_rules = 0, rule_idx = 0;
>         const struct ieee80211_reg_rule *rule1, *rule2;
> @@ -506,8 +529,9 @@ reglib_intersect_rds(const struct ieee80211_regdomain *rd1,
>         if (!num_rules)
>                 return NULL;
>
> -       size_of_regd = sizeof(struct ieee80211_regdomain) +
> -               ((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
> +       size_of_regd = reglib_array_len(sizeof(struct ieee80211_regdomain),
> +                                       num_rules + 1,
> +                                       sizeof(struct ieee80211_reg_rule));
>
>         rd = malloc(size_of_regd);
>         if (!rd)
> diff --git a/reglib.h b/reglib.h
> index 86087e3..57082fe 100644
> --- a/reglib.h
> +++ b/reglib.h
> @@ -112,8 +112,9 @@ static inline uint32_t reglib_min(uint32_t a, uint32_t b)
>         return (a > b) ? b : a;
>  }
>
> -void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr);
> -int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen);
> +void *
> +reglib_get_file_ptr(uint8_t *db, size_t dblen, size_t structlen, uint32_t ptr);
> +int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen);
>
>  /**
>   * reglib_malloc_regdb_ctx - create a regdb context for usage with reglib
>



More information about the wireless-regdb mailing list