diff options
author | Ben Hutchings <ben@decadent.org.uk> | 2013-07-01 00:49:31 +0100 |
---|---|---|
committer | Luis R. Rodriguez <mcgrof@do-not-panic.com> | 2013-07-16 18:15:34 -0700 |
commit | f4c3e3fcae69d90ad3763d2602765104dcb04708 (patch) | |
tree | ed7b0ba3e4c3126ef102b039ef5a202d0d46ec49 | |
parent | 6e8102e3dc16976be0ef53ee66d4726cca72e49f (diff) | |
download | crda-f4c3e3fcae69d90ad3763d2602765104dcb04708.tar.gz |
reglib: Validate all structure and array lengths
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@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@decadent.org.uk>
Signed-off-by: Luis R. Rodriguez <mcgrof@do-not-panic.com>
-rw-r--r-- | reglib.c | 58 | ||||
-rw-r--r-- | reglib.h | 5 |
2 files changed, 44 insertions, 19 deletions
@@ -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) @@ -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 |