Skip to content

Commit f4c3e3f

Browse files
bwhacksmcgrof
authored andcommitted
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>
1 parent 6e8102e commit f4c3e3f

File tree

2 files changed

+44
-19
lines changed

2 files changed

+44
-19
lines changed

reglib.c

+41-17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <stdbool.h>
1111
#include <unistd.h>
1212
#include <string.h>
13+
#include <limits.h>
1314

1415
#include <arpa/inet.h> /* ntohl */
1516

@@ -37,10 +38,16 @@
3738
#include "keys-gcrypt.c"
3839
#endif
3940

40-
void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr)
41+
void *
42+
reglib_get_file_ptr(uint8_t *db, size_t dblen, size_t structlen, uint32_t ptr)
4143
{
4244
uint32_t p = ntohl(ptr);
4345

46+
if (structlen > dblen) {
47+
fprintf(stderr, "Invalid database file, too short!\n");
48+
exit(3);
49+
}
50+
4451
if (p > dblen - structlen) {
4552
fprintf(stderr, "Invalid database file, bad pointer!\n");
4653
exit(3);
@@ -49,6 +56,17 @@ void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr)
4956
return (void *)(db + p);
5057
}
5158

59+
static size_t
60+
reglib_array_len(size_t baselen, unsigned int elemcount, size_t elemlen)
61+
{
62+
if (elemcount > (SIZE_MAX - baselen) / elemlen) {
63+
fprintf(stderr, "Invalid database file, count too large!\n");
64+
exit(3);
65+
}
66+
67+
return baselen + elemcount * elemlen;
68+
}
69+
5270
/*
5371
* reglib_verify_db_signature():
5472
*
@@ -59,7 +77,7 @@ void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr)
5977
*/
6078

6179
#ifdef USE_OPENSSL
62-
int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
80+
int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
6381
{
6482
RSA *rsa;
6583
uint8_t hash[SHA_DIGEST_LENGTH];
@@ -118,7 +136,7 @@ int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
118136
#endif /* USE_OPENSSL */
119137

120138
#ifdef USE_GCRYPT
121-
int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
139+
int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
122140
{
123141
gcry_mpi_t mpi_e, mpi_n;
124142
gcry_sexp_t rsa, signature, data;
@@ -180,7 +198,7 @@ int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
180198
#endif /* USE_GCRYPT */
181199

182200
#if !defined(USE_OPENSSL) && !defined(USE_GCRYPT)
183-
int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen)
201+
int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen)
184202
{
185203
return 1;
186204
}
@@ -220,7 +238,7 @@ const struct reglib_regdb_ctx *reglib_malloc_regdb_ctx(const char *regdb_file)
220238
return NULL;
221239
}
222240

223-
ctx->header = reglib_get_file_ptr(ctx->db, ctx->dblen,
241+
ctx->header = reglib_get_file_ptr(ctx->db, ctx->real_dblen,
224242
sizeof(struct regdb_file_header),
225243
0);
226244
header = ctx->header;
@@ -232,12 +250,13 @@ const struct reglib_regdb_ctx *reglib_malloc_regdb_ctx(const char *regdb_file)
232250
goto err_out;
233251

234252
ctx->siglen = ntohl(header->signature_length);
235-
/* The actual dblen does not take into account the signature */
236-
ctx->dblen = ctx->real_dblen - ctx->siglen;
237253

238-
if (ctx->dblen <= sizeof(*header))
254+
if (ctx->siglen > ctx->real_dblen - sizeof(*header))
239255
goto err_out;
240256

257+
/* The actual dblen does not take into account the signature */
258+
ctx->dblen = ctx->real_dblen - ctx->siglen;
259+
241260
/* verify signature */
242261
if (!reglib_verify_db_signature(ctx->db, ctx->dblen, ctx->siglen))
243262
goto err_out;
@@ -272,7 +291,7 @@ void reglib_free_regdb_ctx(const struct reglib_regdb_ctx *regdb_ctx)
272291
free(ctx);
273292
}
274293

275-
static void reg_rule2rd(uint8_t *db, int dblen,
294+
static void reg_rule2rd(uint8_t *db, size_t dblen,
276295
uint32_t ruleptr, struct ieee80211_reg_rule *rd_reg_rule)
277296
{
278297
struct regdb_file_reg_rule *rule;
@@ -303,18 +322,21 @@ country2rd(const struct reglib_regdb_ctx *ctx,
303322
{
304323
struct regdb_file_reg_rules_collection *rcoll;
305324
struct ieee80211_regdomain *rd;
306-
int i, num_rules, size_of_rd;
325+
unsigned int i, num_rules;
326+
size_t size_of_rd;
307327

308328
rcoll = reglib_get_file_ptr(ctx->db, ctx->dblen, sizeof(*rcoll),
309329
country->reg_collection_ptr);
310330
num_rules = ntohl(rcoll->reg_rule_num);
311331
/* re-get pointer with sanity checking for num_rules */
312332
rcoll = reglib_get_file_ptr(ctx->db, ctx->dblen,
313-
sizeof(*rcoll) + num_rules * sizeof(uint32_t),
314-
country->reg_collection_ptr);
333+
reglib_array_len(sizeof(*rcoll), num_rules,
334+
sizeof(uint32_t)),
335+
country->reg_collection_ptr);
315336

316-
size_of_rd = sizeof(struct ieee80211_regdomain) +
317-
num_rules * sizeof(struct ieee80211_reg_rule);
337+
size_of_rd = reglib_array_len(sizeof(struct ieee80211_regdomain),
338+
num_rules,
339+
sizeof(struct ieee80211_reg_rule));
318340

319341
rd = malloc(size_of_rd);
320342
if (!rd)
@@ -468,7 +490,8 @@ struct ieee80211_regdomain *
468490
reglib_intersect_rds(const struct ieee80211_regdomain *rd1,
469491
const struct ieee80211_regdomain *rd2)
470492
{
471-
int r, size_of_regd;
493+
int r;
494+
size_t size_of_regd;
472495
unsigned int x, y;
473496
unsigned int num_rules = 0, rule_idx = 0;
474497
const struct ieee80211_reg_rule *rule1, *rule2;
@@ -506,8 +529,9 @@ reglib_intersect_rds(const struct ieee80211_regdomain *rd1,
506529
if (!num_rules)
507530
return NULL;
508531

509-
size_of_regd = sizeof(struct ieee80211_regdomain) +
510-
((num_rules + 1) * sizeof(struct ieee80211_reg_rule));
532+
size_of_regd = reglib_array_len(sizeof(struct ieee80211_regdomain),
533+
num_rules + 1,
534+
sizeof(struct ieee80211_reg_rule));
511535

512536
rd = malloc(size_of_regd);
513537
if (!rd)

reglib.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,9 @@ static inline uint32_t reglib_min(uint32_t a, uint32_t b)
112112
return (a > b) ? b : a;
113113
}
114114

115-
void *reglib_get_file_ptr(uint8_t *db, int dblen, int structlen, uint32_t ptr);
116-
int reglib_verify_db_signature(uint8_t *db, int dblen, int siglen);
115+
void *
116+
reglib_get_file_ptr(uint8_t *db, size_t dblen, size_t structlen, uint32_t ptr);
117+
int reglib_verify_db_signature(uint8_t *db, size_t dblen, size_t siglen);
117118

118119
/**
119120
* reglib_malloc_regdb_ctx - create a regdb context for usage with reglib

0 commit comments

Comments
 (0)