tor

The Tor anonymity network
git clone https://git.dasho.dev/tor.git
Log | Files | Refs | README | LICENSE

kvline.c (8084B)


      1 /* Copyright (c) 2001 Matej Pfajfar.
      2 * Copyright (c) 2001-2004, Roger Dingledine.
      3 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
      4 * Copyright (c) 2007-2021, The Tor Project, Inc. */
      5 /* See LICENSE for licensing information */
      6 
      7 /**
      8 * \file kvline.c
      9 *
     10 * \brief Manipulating lines of key-value pairs.
     11 **/
     12 
     13 #include "orconfig.h"
     14 
     15 #include "lib/container/smartlist.h"
     16 #include "lib/encoding/confline.h"
     17 #include "lib/encoding/cstring.h"
     18 #include "lib/encoding/kvline.h"
     19 #include "lib/encoding/qstring.h"
     20 #include "lib/malloc/malloc.h"
     21 #include "lib/string/compat_ctype.h"
     22 #include "lib/string/printf.h"
     23 #include "lib/string/util_string.h"
     24 #include "lib/log/escape.h"
     25 #include "lib/log/util_bug.h"
     26 
     27 #include <stdbool.h>
     28 #include <stddef.h>
     29 #include <string.h>
     30 
     31 /** Return true iff we need to quote and escape the string <b>s</b> to encode
     32 * it.
     33 *
     34 * kvline_can_encode_lines() also uses this (with
     35 * <b>as_keyless_val</b> true) to check whether a key would require
     36 * quoting.
     37 */
     38 static bool
     39 needs_escape(const char *s, bool as_keyless_val)
     40 {
     41  if (as_keyless_val && *s == 0)
     42    return true;
     43  /* Keyless values containing '=' need to be escaped. */
     44  if (as_keyless_val && strchr(s, '='))
     45    return true;
     46 
     47  for (; *s; ++s) {
     48    if (*s >= 127 || TOR_ISSPACE(*s) || ! TOR_ISPRINT(*s) ||
     49        *s == '\'' || *s == '\"') {
     50      return true;
     51    }
     52  }
     53  return false;
     54 }
     55 
     56 /**
     57 * Return true iff the key in <b>line</b> is not set.
     58 **/
     59 static bool
     60 line_has_no_key(const config_line_t *line)
     61 {
     62  return line->key == NULL || strlen(line->key) == 0;
     63 }
     64 
     65 /**
     66 * Return true iff the value in <b>line</b> is not set.
     67 **/
     68 static bool
     69 line_has_no_val(const config_line_t *line)
     70 {
     71  return line->value == NULL || strlen(line->value) == 0;
     72 }
     73 
     74 /**
     75 * Return true iff the all the lines in <b>line</b> can be encoded
     76 * using <b>flags</b>.
     77 **/
     78 static bool
     79 kvline_can_encode_lines(const config_line_t *line, unsigned flags)
     80 {
     81  for ( ; line; line = line->next) {
     82    const bool keyless = line_has_no_key(line);
     83    if (keyless && ! (flags & KV_OMIT_KEYS)) {
     84      /* If KV_OMIT_KEYS is not set, we can't encode a line with no key. */
     85      return false;
     86    }
     87 
     88    if (needs_escape(line->value, keyless) && ! (flags & (KV_QUOTED|KV_RAW))) {
     89      /* If both KV_QUOTED and KV_RAW are false, we can't encode a
     90         value that needs quotes. */
     91      return false;
     92    }
     93    if (!keyless && needs_escape(line->key, true)) {
     94      /* We can't handle keys that need quoting. */
     95      return false;
     96    }
     97  }
     98  return true;
     99 }
    100 
    101 /**
    102 * Encode a linked list of lines in <b>line</b> as a series of 'Key=Value'
    103 * pairs, using the provided <b>flags</b> to encode it.  Return a newly
    104 * allocated string on success, or NULL on failure.
    105 *
    106 * If KV_QUOTED is set in <b>flags</b>, then all values that contain
    107 * spaces or unusual characters are escaped and quoted.  Otherwise, such
    108 * values are not allowed.  Mutually exclusive with KV_RAW.
    109 *
    110 * If KV_OMIT_KEYS is set in <b>flags</b>, then pairs with empty keys are
    111 * allowed, and are encoded as 'Value'.  Otherwise, such pairs are not
    112 * allowed.
    113 *
    114 * If KV_OMIT_VALS is set in <b>flags</b>, then an empty value is
    115 * encoded as 'Key', not as 'Key=' or 'Key=""'.  Mutually exclusive with
    116 * KV_OMIT_KEYS.
    117 *
    118 * If KV_RAW is set in <b>flags</b>, then don't apply any quoting to
    119 * the value, and assume that the caller has adequately quoted it.
    120 * (The control protocol has some quirks that make this necessary.)
    121 * Mutually exclusive with KV_QUOTED.
    122 *
    123 * KV_QUOTED_QSTRING is not supported.
    124 */
    125 char *
    126 kvline_encode(const config_line_t *line,
    127              unsigned flags)
    128 {
    129  tor_assert(! (flags & KV_QUOTED_QSTRING));
    130 
    131  tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
    132             (KV_OMIT_KEYS|KV_OMIT_VALS));
    133  tor_assert((flags & (KV_QUOTED|KV_RAW)) != (KV_QUOTED|KV_RAW));
    134 
    135  if (!kvline_can_encode_lines(line, flags))
    136    return NULL;
    137 
    138  smartlist_t *elements = smartlist_new();
    139 
    140  for (; line; line = line->next) {
    141 
    142    const char *k = "";
    143    const char *eq = "=";
    144    const char *v = "";
    145    const bool keyless = line_has_no_key(line);
    146    bool esc = needs_escape(line->value, keyless);
    147    char *tmp = NULL;
    148 
    149    if (! keyless) {
    150      k = line->key;
    151    } else {
    152      eq = "";
    153    }
    154 
    155    if ((flags & KV_OMIT_VALS) && line_has_no_val(line)) {
    156      eq = "";
    157      v = "";
    158    } else if (!(flags & KV_RAW) && esc) {
    159      tmp = esc_for_log(line->value);
    160      v = tmp;
    161    } else {
    162      v = line->value;
    163    }
    164 
    165    smartlist_add_asprintf(elements, "%s%s%s", k, eq, v);
    166    tor_free(tmp);
    167  }
    168 
    169  char *result = smartlist_join_strings(elements, " ", 0, NULL);
    170 
    171  SMARTLIST_FOREACH(elements, char *, cp, tor_free(cp));
    172  smartlist_free(elements);
    173 
    174  return result;
    175 }
    176 
    177 /**
    178 * Decode a <b>line</b> containing a series of space-separated 'Key=Value'
    179 * pairs, using the provided <b>flags</b> to decode it.  Return a newly
    180 * allocated list of pairs on success, or NULL on failure.
    181 *
    182 * If KV_QUOTED is set in <b>flags</b>, then (double-)quoted values are
    183 * allowed and handled as C strings. Otherwise, such values are not allowed.
    184 *
    185 * If KV_OMIT_KEYS is set in <b>flags</b>, then values without keys are
    186 * allowed.  Otherwise, such values are not allowed.
    187 *
    188 * If KV_OMIT_VALS is set in <b>flags</b>, then keys without values are
    189 * allowed.  Otherwise, such keys are not allowed.  Mutually exclusive with
    190 * KV_OMIT_KEYS.
    191 *
    192 * If KV_QUOTED_QSTRING is set in <b>flags</b>, then double-quoted values
    193 * are allowed and handled as QuotedStrings per qstring.c.  Do not add
    194 * new users of this flag.
    195 *
    196 * KV_RAW is not supported.
    197 */
    198 config_line_t *
    199 kvline_parse(const char *line, unsigned flags)
    200 {
    201  tor_assert((flags & (KV_OMIT_KEYS|KV_OMIT_VALS)) !=
    202             (KV_OMIT_KEYS|KV_OMIT_VALS));
    203  tor_assert(!(flags & KV_RAW));
    204 
    205  const char *cp = line, *cplast = NULL;
    206  const bool omit_keys = (flags & KV_OMIT_KEYS) != 0;
    207  const bool omit_vals = (flags & KV_OMIT_VALS) != 0;
    208  const bool quoted = (flags & (KV_QUOTED|KV_QUOTED_QSTRING)) != 0;
    209  const bool c_quoted = (flags & (KV_QUOTED)) != 0;
    210 
    211  config_line_t *result = NULL;
    212  config_line_t **next_line = &result;
    213 
    214  char *key = NULL;
    215  char *val = NULL;
    216 
    217  while (*cp) {
    218    key = val = NULL;
    219    /* skip all spaces */
    220    {
    221      size_t idx = strspn(cp, " \t\r\v\n");
    222      cp += idx;
    223    }
    224    if (BUG(cp == cplast)) {
    225      /* If we didn't parse anything since the last loop, this code is
    226       * broken. */
    227      goto err; // LCOV_EXCL_LINE
    228    }
    229    cplast = cp;
    230    if (! *cp)
    231      break; /* End of string; we're done. */
    232 
    233    /* Possible formats are K=V, K="V", K, V, and "V", depending on flags. */
    234 
    235    /* Find where the key ends */
    236    if (*cp != '\"') {
    237      size_t idx = strcspn(cp, " \t\r\v\n=");
    238 
    239      if (cp[idx] == '=') {
    240        key = tor_memdup_nulterm(cp, idx);
    241        cp += idx + 1;
    242      } else if (omit_vals) {
    243        key = tor_memdup_nulterm(cp, idx);
    244        cp += idx;
    245        goto commit;
    246      } else {
    247        if (!omit_keys)
    248          goto err;
    249      }
    250    }
    251 
    252    if (*cp == '\"') {
    253      /* The type is "V". */
    254      if (!quoted)
    255        goto err;
    256      size_t len=0;
    257      if (c_quoted) {
    258        cp = unescape_string(cp, &val, &len);
    259      } else {
    260        cp = decode_qstring(cp, strlen(cp), &val, &len);
    261      }
    262      if (cp == NULL || len != strlen(val)) {
    263        // The string contains a NUL or is badly coded.
    264        goto err;
    265      }
    266    } else {
    267      size_t idx = strcspn(cp, " \t\r\v\n");
    268      val = tor_memdup_nulterm(cp, idx);
    269      cp += idx;
    270    }
    271 
    272  commit:
    273    if (key && strlen(key) == 0) {
    274      /* We don't allow empty keys. */
    275      goto err;
    276    }
    277 
    278    *next_line = tor_malloc_zero(sizeof(config_line_t));
    279    (*next_line)->key = key ? key : tor_strdup("");
    280    (*next_line)->value = val ? val : tor_strdup("");
    281    next_line = &(*next_line)->next;
    282    key = val = NULL;
    283  }
    284 
    285  if (! (flags & KV_QUOTED_QSTRING)) {
    286    if (!kvline_can_encode_lines(result, flags)) {
    287      goto err;
    288    }
    289  }
    290  return result;
    291 
    292 err:
    293  tor_free(key);
    294  tor_free(val);
    295  config_free_lines(result);
    296  return NULL;
    297 }