r12401@catbus: nickm | 2007-04-16 12:28:01 -0400

Enforce checks for number of arguments to items in directory objects more uniformly.


svn:r9968
This commit is contained in:
Nick Mathewson 2007-04-16 16:28:06 +00:00
parent 32c57918a0
commit 7fb7658a45

View File

@ -80,20 +80,13 @@ typedef struct directory_token_t {
size_t object_size; /**< Bytes in object_body */
char *object_body; /**< Contents of object, base64-decoded. */
crypto_pk_env_t *key; /**< For public keys only. */
const char *error; /**< For _ERR tokens only. */
char *error; /**< For _ERR tokens only. */
} directory_token_t;
/* ********************************************************************** */
/** We use a table of rules to decide how to parse each token type. */
/** Rules for how many arguments a keyword can take. */
typedef enum {
NO_ARGS, /**< No arguments, ever. */
ARGS, /**< A list of arguments separated by spaces. */
CONCAT_ARGS, /**< The rest of the line, treated as a single argument. */
} arg_syntax;
/** Rules for whether the keyword needs an object. */
typedef enum {
NO_OBJ, /**< No object, ever. */
@ -104,38 +97,46 @@ typedef enum {
/** DOCDOC */
typedef struct token_rule_t {
const char *t; directory_keyword v; arg_syntax s; obj_syntax os;
const char *t; directory_keyword v;
int min_args; int max_args; int concat_args;
obj_syntax os;
int min_cnt; int max_cnt;
} token_rule_t;
/** DOCDOC */
#define END_OF_TABLE { NULL, _NIL, NO_ARGS, NO_OBJ, 0, INT_MAX }
#define END_OF_TABLE { NULL, _NIL, 0,0,0, NO_OBJ, 0, INT_MAX }
#define T(s,t,a,o) { s, t, a, o, 0, INT_MAX }
#define T0N(s,t,a,o) { s, t, a, o, 0, INT_MAX }
#define T1(s,t,a,o) { s, t, a, o, 1, 1 }
#define T01(s,t,a,o) { s, t, a, o, 0, 1 }
#define ARGS 0,INT_MAX,0
#define NO_ARGS 0,0,0
#define CONCAT_ARGS 1,1,1
#define GE(n) n,INT_MAX,0
#define EQ(n) n,n,0
/** DOCDOC */
static token_rule_t routerdesc_token_table[] = {
T0N("accept", K_ACCEPT, ARGS, NO_OBJ ),
T0N("reject", K_REJECT, ARGS, NO_OBJ ),
T1( "router", K_ROUTER, ARGS, NO_OBJ ),
T1( "router", K_ROUTER, GE(5), NO_OBJ ),
T1( "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY ),
T1( "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY ),
T1( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ),
T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T01("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
T01("uptime", K_UPTIME, ARGS, NO_OBJ ),
T01("uptime", K_UPTIME, GE(1), NO_OBJ ),
T01("family", K_FAMILY, ARGS, NO_OBJ ),
T01("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
T01("hibernating", K_HIBERNATING, ARGS, NO_OBJ ),
T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ ),
T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ),
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
T01("eventdns", K_EVENTDNS, ARGS, NO_OBJ ),
T01("extra-info-digest", K_EXTRA_INFO_DIGEST, ARGS, NO_OBJ ),
T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ),
T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ),
T1("bandwidth", K_BANDWIDTH, ARGS, NO_OBJ ),
T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ),
T01("platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ ),
END_OF_TABLE
@ -147,13 +148,13 @@ static token_rule_t extrainfo_token_table[] = {
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ),
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
T1( "extra-info", K_EXTRA_INFO, ARGS, NO_OBJ ),
T1( "extra-info", K_EXTRA_INFO, GE(2), NO_OBJ ),
END_OF_TABLE
};
static token_rule_t rtrstatus_token_table[] = {
T1( "r", K_R, ARGS, NO_OBJ ),
T1( "r", K_R, GE(8), NO_OBJ ),
T1( "s", K_S, ARGS, NO_OBJ ),
T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
@ -164,20 +165,21 @@ static token_rule_t netstatus_token_table[] = {
T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
/* XXXX should dir-signing-key really have ARGS? */
T1( "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, NEED_KEY ),
T1( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
T1( "network-status-version", K_NETWORK_STATUS_VERSION,
ARGS, NO_OBJ ),
T1( "dir-source", K_DIR_SOURCE, ARGS, NO_OBJ ),
GE(1), NO_OBJ ),
T1( "dir-source", K_DIR_SOURCE, GE(3), NO_OBJ ),
T01("dir-options", K_DIR_OPTIONS, ARGS, NO_OBJ ),
T01("client-versions", K_CLIENT_VERSIONS, ARGS, NO_OBJ ),
T01("server-versions", K_SERVER_VERSIONS, ARGS, NO_OBJ ),
T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
END_OF_TABLE
};
static token_rule_t dir_footer_token_table[] = {
T1( "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ ),
T1( "directory-signature", K_DIRECTORY_SIGNATURE, EQ(1), NEED_OBJ ),
END_OF_TABLE
};
@ -185,12 +187,12 @@ static token_rule_t dir_token_table[] = {
/* don't enforce counts; this is obsolete. */
T( "network-status", K_NETWORK_STATUS, NO_ARGS, NO_OBJ ),
T( "directory-signature", K_DIRECTORY_SIGNATURE, ARGS, NEED_OBJ ),
T( "recommended-software",K_RECOMMENDED_SOFTWARE,ARGS, NO_OBJ ),
T( "recommended-software",K_RECOMMENDED_SOFTWARE,CONCAT_ARGS, NO_OBJ ),
T( "signed-directory", K_SIGNED_DIRECTORY, NO_ARGS, NO_OBJ ),
T( "running-routers", K_RUNNING_ROUTERS, ARGS, NO_OBJ ),
T( "router-status", K_ROUTER_STATUS, ARGS, NO_OBJ ),
T1("published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
T( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
T( "opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
T( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
T( "dir-signing-key", K_DIR_SIGNING_KEY, ARGS, OBJ_OK ),
@ -642,10 +644,7 @@ check_directory_signature(const char *digest,
char signed_digest[PK_BYTES];
crypto_pk_env_t *_pkey = NULL;
if (tok->n_args != 1) {
log_warn(LD_DIR, "Too many or too few arguments to directory-signature");
return -1;
}
tor_assert(tok->n_args == 1);
if (declared_key) {
if (!check_authority || dir_signing_key_is_trusted(declared_key))
@ -827,6 +826,7 @@ router_parse_entry_from_string(const char *s, const char *end,
log_warn(LD_DIR,"Entry does not start with \"router\"");
goto err;
}
tor_assert(tok->n_args >= 5);
router = tor_malloc_zero(sizeof(routerinfo_t));
router->routerlist_index = -1;
@ -835,70 +835,49 @@ router_parse_entry_from_string(const char *s, const char *end,
router->cache_info.signed_descriptor_len = end-s;
memcpy(router->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
if (tok->n_args >= 5) {
router->nickname = tor_strdup(tok->args[0]);
if (!is_legal_nickname(router->nickname)) {
log_warn(LD_DIR,"Router nickname is invalid");
goto err;
}
router->address = tor_strdup(tok->args[1]);
if (!tor_inet_aton(router->address, &in)) {
log_warn(LD_DIR,"Router address is not an IP.");
goto err;
}
router->addr = ntohl(in.s_addr);
router->or_port =
(uint16_t) tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
router->dir_port =
(uint16_t) tor_parse_long(tok->args[4],10,0,65535,NULL,NULL);
} else {
log_warn(LD_DIR,"Wrong # of arguments to \"router\" (%d)",tok->n_args);
router->nickname = tor_strdup(tok->args[0]);
if (!is_legal_nickname(router->nickname)) {
log_warn(LD_DIR,"Router nickname is invalid");
goto err;
}
router->address = tor_strdup(tok->args[1]);
if (!tor_inet_aton(router->address, &in)) {
log_warn(LD_DIR,"Router address is not an IP.");
goto err;
}
router->addr = ntohl(in.s_addr);
router->or_port =
(uint16_t) tor_parse_long(tok->args[2],10,0,65535,NULL,NULL);
router->dir_port =
(uint16_t) tor_parse_long(tok->args[4],10,0,65535,NULL,NULL);
tok = find_first_by_keyword(tokens, K_BANDWIDTH);
if (!tok) {
log_warn(LD_DIR,"No bandwidth declared; failing.");
tor_assert(tok && tok->n_args >= 3);
router->bandwidthrate =
tor_parse_long(tok->args[0],10,0,INT_MAX,NULL,NULL);
if (!router->bandwidthrate) {
log_warn(LD_DIR, "bandwidthrate %s unreadable or 0. Failing.",
escaped(tok->args[0]));
goto err;
} else {
if (tok->n_args < 3) {
log_warn(LD_DIR,
"Not enough arguments to \"bandwidth\" in server descriptor.");
goto err;
}
router->bandwidthrate =
tor_parse_long(tok->args[0],10,0,INT_MAX,NULL,NULL);
if (!router->bandwidthrate) {
log_warn(LD_DIR, "bandwidthrate %s unreadable or 0. Failing.",
escaped(tok->args[0]));
goto err;
}
router->bandwidthburst =
tor_parse_long(tok->args[1],10,0,INT_MAX,NULL,NULL);
router->bandwidthcapacity =
tor_parse_long(tok->args[2],10,0,INT_MAX,NULL,NULL);
/* XXX we don't error-check these values? -RD */
}
router->bandwidthburst =
tor_parse_long(tok->args[1],10,0,INT_MAX,NULL,NULL);
router->bandwidthcapacity =
tor_parse_long(tok->args[2],10,0,INT_MAX,NULL,NULL);
/* XXX we don't error-check these values? -RD */
if ((tok = find_first_by_keyword(tokens, K_UPTIME))) {
if (tok->n_args != 1) {
log_warn(LD_DIR, "Unrecognized number of args on K_UPTIME; skipping.");
} else {
router->uptime = tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL);
}
tor_assert(tok->n_args >= 1);
router->uptime = tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL);
}
if ((tok = find_first_by_keyword(tokens, K_HIBERNATING))) {
if (tok->n_args < 1) {
log_warn(LD_DIR, "Too few args on 'hibernating' keyword. Skipping.");
} else {
router->is_hibernating
= (tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL) != 0);
}
tor_assert(tok->n_args >= 1);
router->is_hibernating
= (tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL) != 0);
}
tok = find_first_by_keyword(tokens, K_PUBLISHED);
@ -934,10 +913,7 @@ router_parse_entry_from_string(const char *s, const char *end,
if ((tok = find_first_by_keyword(tokens, K_FINGERPRINT))) {
/* If there's a fingerprint line, it must match the identity digest. */
char d[DIGEST_LEN];
if (tok->n_args < 1) {
log_warn(LD_DIR, "Too few arguments to router fingerprint");
goto err;
}
tor_assert(tok->n_args == 1);
tor_strstrip(tok->args[0], " ");
if (base16_decode(d, DIGEST_LEN, tok->args[0], strlen(tok->args[0]))) {
log_warn(LD_DIR, "Couldn't decode router fingerprint %s",
@ -989,8 +965,8 @@ router_parse_entry_from_string(const char *s, const char *end,
if ((tok = find_first_by_keyword(tokens, K_CACHES_EXTRA_INFO)))
router->caches_extra_info = 1;
if ((tok = find_first_by_keyword(tokens, K_EXTRA_INFO_DIGEST)) &&
tok->n_args) {
if ((tok = find_first_by_keyword(tokens, K_EXTRA_INFO_DIGEST))) {
tor_assert(tok->n_args >= 1);
if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
base16_decode(router->extra_info_digest, DIGEST_LEN, tok->args[0],
HEX_DIGEST_LEN);
@ -1094,11 +1070,7 @@ extrainfo_parse_entry_from_string(const char *s, const char *end,
extrainfo->cache_info.signed_descriptor_body = tor_strndup(s, end-s);
extrainfo->cache_info.signed_descriptor_len = end-s;
memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
if (tok->n_args < 2) {
log_warn(LD_DIR,"Insufficient arguments to \"extra-info\"");
goto err;
}
tor_assert(tok->n_args >= 2);
if (!is_legal_nickname(tok->args[0])) {
log_warn(LD_DIR,"Bad nickname %s on \"extra-info\"",escaped(tok->args[0]));
goto err;
@ -1203,10 +1175,7 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens)
}
tok = find_first_by_keyword(tokens, K_R);
tor_assert(tok);
if (tok->n_args < 8) {
log_warn(LD_DIR,
"Too few arguments to 'r' keywork in router status; skipping.");
}
tor_assert(tok->n_args >= 8);
rs = tor_malloc_zero(sizeof(routerstatus_t));
if (!is_legal_nickname(tok->args[0])) {
@ -1272,7 +1241,8 @@ routerstatus_parse_entry_from_string(const char **s, smartlist_t *tokens)
rs->is_bad_directory = 1;
}
}
if ((tok = find_first_by_keyword(tokens, K_V)) && tok->n_args) {
if ((tok = find_first_by_keyword(tokens, K_V))) {
tor_assert(tok->n_args == 1);
rs->version_known = 1;
if (strcmpstart(tok->args[0], "Tor ")) {
rs->version_supports_begindir = 1;
@ -1352,10 +1322,7 @@ networkstatus_parse_from_string(const char *s)
tok = find_first_by_keyword(tokens, K_DIR_SOURCE);
tor_assert(tok);
if (tok->n_args < 3) {
log_warn(LD_DIR, "Too few arguments to dir-source keyword");
goto err;
}
tor_assert(tok->n_args >= 3);
ns->source_address = tok->args[0]; tok->args[0] = NULL;
if (tor_inet_aton(tok->args[1], &in) == 0) {
log_warn(LD_DIR, "Error parsing network-status source address %s",
@ -1372,10 +1339,7 @@ networkstatus_parse_from_string(const char *s)
tok = find_first_by_keyword(tokens, K_FINGERPRINT);
tor_assert(tok);
if (tok->n_args < 1) {
log_warn(LD_DIR, "Too few arguments to networkstatus fingerprint");
goto err;
}
tor_assert(tok->n_args);
if (base16_decode(ns->identity_digest, DIGEST_LEN, tok->args[0],
strlen(tok->args[0]))) {
log_warn(LD_DIR, "Couldn't decode networkstatus fingerprint %s",
@ -1383,7 +1347,8 @@ networkstatus_parse_from_string(const char *s)
goto err;
}
if ((tok = find_first_by_keyword(tokens, K_CONTACT)) && tok->n_args) {
if ((tok = find_first_by_keyword(tokens, K_CONTACT))) {
tor_assert(tok->n_args);
ns->contact = tok->args[0];
tok->args[0] = NULL;
}
@ -1417,8 +1382,7 @@ networkstatus_parse_from_string(const char *s)
}
if (ns->recommends_versions) {
if (!(tok = find_first_by_keyword(tokens, K_CLIENT_VERSIONS)) ||
tok->n_args<1) {
if (!(tok = find_first_by_keyword(tokens, K_CLIENT_VERSIONS))) {
log_warn(LD_DIR, "Missing client-versions");
}
ns->client_versions = tok->args[0];
@ -1684,6 +1648,7 @@ token_free(directory_token_t *tok)
}
tor_free(tok->object_type);
tor_free(tok->object_body);
tor_free(tok->error);
if (tok->key)
crypto_free_pk_env(tok->key);
tor_free(tok);
@ -1696,17 +1661,19 @@ static directory_token_t *
get_next_token(const char **s, struct token_rule_t *table)
{
const char *next, *obstart;
int i, done, allocated, is_opt;
int i, j, done, allocated, is_opt;
directory_token_t *tok;
arg_syntax a_syn;
obj_syntax o_syn = NO_OBJ;
char ebuf[128];
const char *kwd = "";
#define RET_ERR(msg) \
do { if (tok) token_free(tok); \
tok = tor_malloc_zero(sizeof(directory_token_t));\
tok->tp = _ERR; \
tok->error = msg; \
goto done_tokenizing; } while (0)
#define RET_ERR(msg) \
do { \
if (tok) token_free(tok); \
tok = tor_malloc_zero(sizeof(directory_token_t)); \
tok->tp = _ERR; \
tok->error = tor_strdup(msg); \
goto done_tokenizing; } while (0)
tok = tor_malloc_zero(sizeof(directory_token_t));
tok->tp = _ERR;
@ -1718,7 +1685,7 @@ get_next_token(const char **s, struct token_rule_t *table)
}
next = find_whitespace(*s);
if (!next) {
tok->error = "Unexpected EOF"; return tok;
tok->error = tor_strdup("Unexpected EOF"); return tok;
}
/* It's a keyword... but which one? */
is_opt = !strncmp("opt", *s, next-*s);
@ -1734,29 +1701,10 @@ get_next_token(const char **s, struct token_rule_t *table)
for (i = 0; table[i].t ; ++i) {
if (!strncmp(table[i].t, *s, next-*s)) {
/* We've found the keyword. */
kwd = table[i].t;
tok->tp = table[i].v;
a_syn = table[i].s;
o_syn = table[i].os;
if (a_syn == ARGS) {
/* This keyword takes multiple arguments. */
i = 0;
done = (*next == '\n');
allocated = 32;
tok->args = tor_malloc(sizeof(char*)*32);
*s = eat_whitespace_no_nl(next);
while (**s != '\n' && !done) {
next = find_whitespace(*s);
if (*next == '\n')
done = 1;
if (i == allocated) {
allocated *= 2;
tok->args = tor_realloc(tok->args,sizeof(char*)*allocated);
}
tok->args[i++] = tor_strndup(*s,next-*s);
*s = eat_whitespace_no_nl(next+1);
}
tok->n_args = i;
} else if (a_syn == CONCAT_ARGS) {
if (table[i].concat_args) {
/* The keyword takes the line as a single argument */
*s = eat_whitespace_no_nl(next);
next = strchr(*s, '\n');
@ -1767,15 +1715,33 @@ get_next_token(const char **s, struct token_rule_t *table)
tok->n_args = 1;
*s = eat_whitespace_no_nl(next+1);
} else {
/* The keyword takes no arguments. */
tor_assert(a_syn == NO_ARGS);
/* This keyword takes multiple arguments. */
j = 0;
done = (*next == '\n');
allocated = 32;
tok->args = tor_malloc(sizeof(char*)*32);
*s = eat_whitespace_no_nl(next);
if (**s != '\n') {
RET_ERR("Unexpected arguments");
while (**s != '\n' && !done) {
next = find_whitespace(*s);
if (*next == '\n')
done = 1;
if (j == allocated) {
allocated *= 2;
tok->args = tor_realloc(tok->args,sizeof(char*)*allocated);
}
tok->args[j++] = tor_strndup(*s,next-*s);
*s = eat_whitespace_no_nl(next+1);
}
tok->n_args = 0;
*s = eat_whitespace_no_nl(*s+1);
tok->n_args = j;
}
if (tok->n_args < table[i].min_args) {
tor_snprintf(ebuf, sizeof(ebuf), "Too few arguments to %s", kwd);
RET_ERR(ebuf);
} else if (tok->n_args > table[i].max_args) {
tor_snprintf(ebuf, sizeof(ebuf), "Too many arguments to %s", kwd);
RET_ERR(ebuf);
}
break;
}
}
@ -1832,18 +1798,26 @@ get_next_token(const char **s, struct token_rule_t *table)
switch (o_syn)
{
case NO_OBJ:
if (tok->object_body)
RET_ERR("Unexpected object for keyword");
if (tok->key)
RET_ERR("Unexpected public key for keyword");
if (tok->object_body) {
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected object for %s", kwd);
RET_ERR(ebuf);
}
if (tok->key) {
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected public key for %s", kwd);
RET_ERR(ebuf);
}
break;
case NEED_OBJ:
if (!tok->object_body)
RET_ERR("Missing object for keyword");
if (!tok->object_body) {
tor_snprintf(ebuf, sizeof(ebuf), "Missing object for %s", kwd);
RET_ERR(ebuf);
}
break;
case NEED_KEY:
if (!tok->key)
RET_ERR("Missing public key for keyword");
if (!tok->key) {
tor_snprintf(ebuf, sizeof(ebuf), "Missing public key for %s", kwd);
RET_ERR(ebuf);
}
break;
case OBJ_OK:
break;