[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[or-cvs] r9968: Enforce checks for number of arguments to items in directory (in tor/trunk: . src/or)



Author: nickm
Date: 2007-04-16 12:28:06 -0400 (Mon, 16 Apr 2007)
New Revision: 9968

Modified:
   tor/trunk/
   tor/trunk/src/or/routerparse.c
Log:
 r12401@catbus:  nickm | 2007-04-16 12:28:01 -0400
 Enforce checks for number of arguments to items in directory objects more uniformly.



Property changes on: tor/trunk
___________________________________________________________________
 svk:merge ticket from /tor/trunk [r12401] on 8246c3cf-6607-4228-993b-4d95d33730f1

Modified: tor/trunk/src/or/routerparse.c
===================================================================
--- tor/trunk/src/or/routerparse.c	2007-04-16 15:38:53 UTC (rev 9967)
+++ tor/trunk/src/or/routerparse.c	2007-04-16 16:28:06 UTC (rev 9968)
@@ -80,20 +80,13 @@
   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 @@
 
 /** 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 @@
   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 @@
   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 @@
   /* 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 @@
   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 @@
     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->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;
   }
-
-  tok = find_first_by_keyword(tokens, K_BANDWIDTH);
-  if (!tok) {
-    log_warn(LD_DIR,"No bandwidth declared; failing.");
+  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;
-  } 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);
+  }
+  router->addr = ntohl(in.s_addr);
 
-    if (!router->bandwidthrate) {
-      log_warn(LD_DIR, "bandwidthrate %s unreadable or 0. Failing.",
-               escaped(tok->args[0]));
-      goto err;
-    }
+  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);
 
-    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);
+  tok = find_first_by_keyword(tokens, K_BANDWIDTH);
+  tor_assert(tok && tok->n_args >= 3);
+  router->bandwidthrate =
+    tor_parse_long(tok->args[0],10,0,INT_MAX,NULL,NULL);
 
-    /* XXX we don't error-check these values? -RD */
+  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 */
 
   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 @@
   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 @@
   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->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 @@
   }
   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 @@
         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 @@
 
   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 @@
 
   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 @@
     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 @@
   }
 
   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 @@
   }
   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 @@
 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 @@
   }
   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,12 +1701,22 @@
   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) {
+      if (table[i].concat_args) {
+        /* The keyword takes the line as a single argument */
+        *s = eat_whitespace_no_nl(next);
+        next = strchr(*s, '\n');
+        if (!next)
+          RET_ERR("Unexpected EOF");
+        tok->args = tor_malloc(sizeof(char*));
+        tok->args[0] = tor_strndup(*s,next-*s);
+        tok->n_args = 1;
+        *s = eat_whitespace_no_nl(next+1);
+      } else {
         /* This keyword takes multiple arguments. */
-        i = 0;
+        j = 0;
         done = (*next == '\n');
         allocated = 32;
         tok->args = tor_malloc(sizeof(char*)*32);
@@ -1748,34 +1725,23 @@
           next = find_whitespace(*s);
           if (*next == '\n')
             done = 1;
-          if (i == allocated) {
+          if (j == allocated) {
             allocated *= 2;
             tok->args = tor_realloc(tok->args,sizeof(char*)*allocated);
           }
-          tok->args[i++] = tor_strndup(*s,next-*s);
+          tok->args[j++] = tor_strndup(*s,next-*s);
           *s = eat_whitespace_no_nl(next+1);
         }
-        tok->n_args = i;
-      } else if (a_syn == CONCAT_ARGS) {
-        /* The keyword takes the line as a single argument */
-        *s = eat_whitespace_no_nl(next);
-        next = strchr(*s, '\n');
-        if (!next)
-          RET_ERR("Unexpected EOF");
-        tok->args = tor_malloc(sizeof(char*));
-        tok->args[0] = tor_strndup(*s,next-*s);
-        tok->n_args = 1;
-        *s = eat_whitespace_no_nl(next+1);
-      } else {
-        /* The keyword takes no arguments. */
-        tor_assert(a_syn == NO_ARGS);
-        *s = eat_whitespace_no_nl(next);
-        if (**s != '\n') {
-          RET_ERR("Unexpected arguments");
-        }
-        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 @@
   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;