tor

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

versions.c (17834B)


      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 versions.c
      9 * \brief Code to manipulate, parse, and compare Tor versions.
     10 */
     11 #include "core/or/or.h"
     12 
     13 #include "core/or/protover.h"
     14 #include "core/or/versions.h"
     15 #include "lib/crypt_ops/crypto_util.h"
     16 
     17 #include "core/or/tor_version_st.h"
     18 
     19 /**
     20 * Return the approximate date when this release came out, or was
     21 * scheduled to come out, according to the APPROX_RELEASE_DATE set in
     22 * configure.ac
     23 **/
     24 time_t
     25 tor_get_approx_release_date(void)
     26 {
     27  char tbuf[ISO_TIME_LEN+1];
     28  tor_snprintf(tbuf, sizeof(tbuf),
     29               "%s 00:00:00", APPROX_RELEASE_DATE);
     30  time_t result = 0;
     31  int r = parse_iso_time(tbuf, &result);
     32  if (BUG(r < 0)) {
     33    result = 0;
     34  }
     35  return result;
     36 }
     37 
     38 /** Return VS_RECOMMENDED if <b>myversion</b> is contained in
     39 * <b>versionlist</b>.  Else, return VS_EMPTY if versionlist has no
     40 * entries. Else, return VS_OLD if every member of
     41 * <b>versionlist</b> is newer than <b>myversion</b>.  Else, return
     42 * VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in
     43 * the same series (major.minor.micro) as <b>myversion</b>, but no such member
     44 * is newer than <b>myversion.</b>.  Else, return VS_NEW if every member of
     45 * <b>versionlist</b> is older than <b>myversion</b>.  Else, return
     46 * VS_UNRECOMMENDED.
     47 *
     48 * (versionlist is a comma-separated list of version strings,
     49 * optionally prefixed with "Tor".  Versions that can't be parsed are
     50 * ignored.)
     51 */
     52 version_status_t
     53 tor_version_is_obsolete(const char *myversion, const char *versionlist)
     54 {
     55  tor_version_t mine, other;
     56  int found_newer = 0, found_older = 0, found_newer_in_series = 0,
     57    found_any_in_series = 0, r, same;
     58  version_status_t ret = VS_UNRECOMMENDED;
     59  smartlist_t *version_sl;
     60 
     61  log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'",
     62            myversion, versionlist);
     63 
     64  if (tor_version_parse(myversion, &mine)) {
     65    log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion);
     66    tor_assert(0);
     67  }
     68  version_sl = smartlist_new();
     69  smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
     70 
     71  if (!strlen(versionlist)) { /* no authorities cared or agreed */
     72    ret = VS_EMPTY;
     73    goto done;
     74  }
     75 
     76  SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) {
     77    if (!strcmpstart(cp, "Tor "))
     78      cp += 4;
     79 
     80    if (tor_version_parse(cp, &other)) {
     81      /* Couldn't parse other; it can't be a match. */
     82    } else {
     83      same = tor_version_same_series(&mine, &other);
     84      if (same)
     85        found_any_in_series = 1;
     86      r = tor_version_compare(&mine, &other);
     87      if (r==0) {
     88        ret = VS_RECOMMENDED;
     89        goto done;
     90      } else if (r<0) {
     91        found_newer = 1;
     92        if (same)
     93          found_newer_in_series = 1;
     94      } else if (r>0) {
     95        found_older = 1;
     96      }
     97    }
     98  } SMARTLIST_FOREACH_END(cp);
     99 
    100  /* We didn't find the listed version. Is it new or old? */
    101  if (found_any_in_series && !found_newer_in_series && found_newer) {
    102    ret = VS_NEW_IN_SERIES;
    103  } else if (found_newer && !found_older) {
    104    ret = VS_OLD;
    105  } else if (found_older && !found_newer) {
    106    ret = VS_NEW;
    107  } else {
    108    ret = VS_UNRECOMMENDED;
    109  }
    110 
    111 done:
    112  SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
    113  smartlist_free(version_sl);
    114  return ret;
    115 }
    116 
    117 /** Extract a Tor version from a <b>platform</b> line from a router
    118 * descriptor, and place the result in <b>router_version</b>.
    119 *
    120 * Return 1 on success, -1 on parsing failure, and 0 if the
    121 * platform line does not indicate some version of Tor.
    122 *
    123 * If <b>strict</b> is non-zero, finding any weird version components
    124 * (like negative numbers) counts as a parsing failure.
    125 */
    126 int
    127 tor_version_parse_platform(const char *platform,
    128                           tor_version_t *router_version,
    129                           int strict)
    130 {
    131  char tmp[128];
    132  char *s, *s2, *start;
    133 
    134  if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
    135    return 0;
    136 
    137  start = (char *)eat_whitespace(platform+3);
    138  if (!*start) return -1;
    139  s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
    140  s2 = (char*)eat_whitespace(s);
    141  if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
    142    s = (char*)find_whitespace(s2);
    143 
    144  if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
    145    return -1;
    146  strlcpy(tmp, start, s-start+1);
    147 
    148  if (tor_version_parse(tmp, router_version)<0) {
    149    log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
    150    return -1;
    151  }
    152 
    153  if (strict) {
    154    if (router_version->major < 0 ||
    155        router_version->minor < 0 ||
    156        router_version->micro < 0 ||
    157        router_version->patchlevel < 0 ||
    158        router_version->svn_revision < 0) {
    159      return -1;
    160    }
    161  }
    162 
    163  return 1;
    164 }
    165 
    166 /** Parse the Tor version of the platform string <b>platform</b>,
    167 * and compare it to the version in <b>cutoff</b>. Return 1 if
    168 * the router is at least as new as the cutoff, else return 0.
    169 */
    170 int
    171 tor_version_as_new_as(const char *platform, const char *cutoff)
    172 {
    173  tor_version_t cutoff_version, router_version;
    174  int r;
    175  tor_assert(platform);
    176 
    177  if (tor_version_parse(cutoff, &cutoff_version)<0) {
    178    log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
    179    return 0;
    180  }
    181 
    182  r = tor_version_parse_platform(platform, &router_version, 0);
    183  if (r == 0) {
    184    /* nonstandard Tor; be safe and say yes */
    185    return 1;
    186  } else if (r < 0) {
    187    /* unparseable version; be safe and say yes. */
    188    return 1;
    189  }
    190 
    191  /* Here's why we don't need to do any special handling for svn revisions:
    192   * - If neither has an svn revision, we're fine.
    193   * - If the router doesn't have an svn revision, we can't assume that it
    194   *   is "at least" any svn revision, so we need to return 0.
    195   * - If the target version doesn't have an svn revision, any svn revision
    196   *   (or none at all) is good enough, so return 1.
    197   * - If both target and router have an svn revision, we compare them.
    198   */
    199 
    200  return tor_version_compare(&router_version, &cutoff_version) >= 0;
    201 }
    202 
    203 /** Parse a tor version from <b>s</b>, and store the result in <b>out</b>.
    204 * Return 0 on success, -1 on failure. */
    205 int
    206 tor_version_parse(const char *s, tor_version_t *out)
    207 {
    208  char *eos=NULL;
    209  const char *cp=NULL;
    210  int ok = 1;
    211  /* Format is:
    212   *   "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
    213   */
    214  tor_assert(s);
    215  tor_assert(out);
    216 
    217  memset(out, 0, sizeof(tor_version_t));
    218  out->status = VER_RELEASE;
    219  if (!strcasecmpstart(s, "Tor "))
    220    s += 4;
    221 
    222  cp = s;
    223 
    224 #define NUMBER(m)                               \
    225  do {                                          \
    226    if (!cp || *cp < '0' || *cp > '9')          \
    227      return -1;                                \
    228    out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos);    \
    229    if (!ok)                                    \
    230      return -1;                                \
    231    if (!eos || eos == cp)                      \
    232      return -1;                                \
    233    cp = eos;                                   \
    234  } while (0)
    235 
    236 #define DOT()                                   \
    237  do {                                          \
    238    if (*cp != '.')                             \
    239      return -1;                                \
    240    ++cp;                                       \
    241  } while (0)
    242 
    243  NUMBER(major);
    244  DOT();
    245  NUMBER(minor);
    246  if (*cp == 0)
    247    return 0;
    248  else if (*cp == '-')
    249    goto status_tag;
    250  DOT();
    251  NUMBER(micro);
    252 
    253  /* Get status */
    254  if (*cp == 0) {
    255    return 0;
    256  } else if (*cp == '.') {
    257    ++cp;
    258  } else if (*cp == '-') {
    259    goto status_tag;
    260  } else if (0==strncmp(cp, "pre", 3)) {
    261    out->status = VER_PRE;
    262    cp += 3;
    263  } else if (0==strncmp(cp, "rc", 2)) {
    264    out->status = VER_RC;
    265    cp += 2;
    266  } else {
    267    return -1;
    268  }
    269 
    270  NUMBER(patchlevel);
    271 
    272 status_tag:
    273  /* Get status tag. */
    274  if (*cp == '-' || *cp == '.')
    275    ++cp;
    276  eos = (char*) find_whitespace(cp);
    277  if (eos-cp >= (int)sizeof(out->status_tag))
    278    strlcpy(out->status_tag, cp, sizeof(out->status_tag));
    279  else {
    280    memcpy(out->status_tag, cp, eos-cp);
    281    out->status_tag[eos-cp] = 0;
    282  }
    283  cp = eat_whitespace(eos);
    284 
    285  if (!strcmpstart(cp, "(r")) {
    286    cp += 2;
    287    out->svn_revision = (int) strtol(cp,&eos,10);
    288  } else if (!strcmpstart(cp, "(git-")) {
    289    const char *close_paren = strchr(cp, ')');
    290    int hexlen;
    291    char digest[DIGEST_LEN];
    292    if (! close_paren)
    293      return -1;
    294    cp += 5;
    295    if (close_paren-cp > HEX_DIGEST_LEN)
    296      return -1;
    297    hexlen = (int)(close_paren-cp);
    298    memwipe(digest, 0, sizeof(digest));
    299    if (hexlen == 0 || (hexlen % 2) == 1)
    300      return -1;
    301    if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
    302      return -1;
    303    memcpy(out->git_tag, digest, hexlen/2);
    304    out->git_tag_len = hexlen/2;
    305  }
    306 
    307  return 0;
    308 #undef NUMBER
    309 #undef DOT
    310 }
    311 
    312 /** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
    313 * b. */
    314 int
    315 tor_version_compare(tor_version_t *a, tor_version_t *b)
    316 {
    317  int i;
    318  tor_assert(a);
    319  tor_assert(b);
    320 
    321  /* We take this approach to comparison to ensure the same (bogus!) behavior
    322   * on all inputs as we would have seen before bug #21278 was fixed. The
    323   * only important difference here is that this method doesn't cause
    324   * a signed integer underflow.
    325   */
    326 #define CMP(field) do {                               \
    327    unsigned aval = (unsigned) a->field;              \
    328    unsigned bval = (unsigned) b->field;              \
    329    int result = (int) (aval - bval);                 \
    330    if (result < 0)                                   \
    331      return -1;                                      \
    332    else if (result > 0)                              \
    333      return 1;                                       \
    334  } while (0)
    335 
    336  CMP(major);
    337  CMP(minor);
    338  CMP(micro);
    339  CMP(status);
    340  CMP(patchlevel);
    341  if ((i = strcmp(a->status_tag, b->status_tag)))
    342     return i;
    343  CMP(svn_revision);
    344  CMP(git_tag_len);
    345  if (a->git_tag_len)
    346     return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
    347  else
    348     return 0;
    349 
    350 #undef CMP
    351 }
    352 
    353 /** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
    354 */
    355 int
    356 tor_version_same_series(tor_version_t *a, tor_version_t *b)
    357 {
    358  tor_assert(a);
    359  tor_assert(b);
    360  return ((a->major == b->major) &&
    361          (a->minor == b->minor) &&
    362          (a->micro == b->micro));
    363 }
    364 
    365 /** Helper: Given pointers to two strings describing tor versions, return -1
    366 * if _a precedes _b, 1 if _b precedes _a, and 0 if they are equivalent.
    367 * Used to sort a list of versions. */
    368 static int
    369 compare_tor_version_str_ptr_(const void **_a, const void **_b)
    370 {
    371  const char *a = *_a, *b = *_b;
    372  int ca, cb;
    373  tor_version_t va, vb;
    374  ca = tor_version_parse(a, &va);
    375  cb = tor_version_parse(b, &vb);
    376  /* If they both parse, compare them. */
    377  if (!ca && !cb)
    378    return tor_version_compare(&va,&vb);
    379  /* If one parses, it comes first. */
    380  if (!ca && cb)
    381    return -1;
    382  if (ca && !cb)
    383    return 1;
    384  /* If neither parses, compare strings.  Also, the directory server admin
    385  ** needs to be smacked upside the head.  But Tor is tolerant and gentle. */
    386  return strcmp(a,b);
    387 }
    388 
    389 /** Sort a list of string-representations of versions in ascending order. */
    390 void
    391 sort_version_list(smartlist_t *versions, int remove_duplicates)
    392 {
    393  smartlist_sort(versions, compare_tor_version_str_ptr_);
    394 
    395  if (remove_duplicates)
    396    smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_);
    397 }
    398 
    399 /** If there are more than this many entries, we're probably under
    400 * some kind of weird DoS. */
    401 static const int MAX_PROTOVER_SUMMARY_MAP_LEN = 1024;
    402 
    403 /**
    404 * Map from protover string to protover_summary_flags_t.
    405 */
    406 static strmap_t *protover_summary_map = NULL;
    407 
    408 /**
    409 * Helper.  Given a non-NULL protover string <b>protocols</b>, set <b>out</b>
    410 * to its summary, and memoize the result in <b>protover_summary_map</b>.
    411 *
    412 * If the protover string does not contain any recognised protocols, sets
    413 * protocols_known, but does not set any other flags. (Empty strings are also
    414 * treated this way.)
    415 */
    416 static void
    417 memoize_protover_summary(protover_summary_flags_t *out,
    418                         const char *protocols)
    419 {
    420  if (!protover_summary_map)
    421    protover_summary_map = strmap_new();
    422 
    423  if (strmap_size(protover_summary_map) >= MAX_PROTOVER_SUMMARY_MAP_LEN) {
    424    protover_summary_cache_free_all();
    425    tor_assert(protover_summary_map == NULL);
    426    protover_summary_map = strmap_new();
    427  }
    428 
    429  const protover_summary_flags_t *cached =
    430    strmap_get(protover_summary_map, protocols);
    431 
    432  if (cached != NULL) {
    433    /* We found a cached entry; no need to parse this one. */
    434    memcpy(out, cached, sizeof(protover_summary_flags_t));
    435    tor_assert(out->protocols_known);
    436    return;
    437  }
    438 
    439  memset(out, 0, sizeof(*out));
    440  out->protocols_known = 1;
    441 
    442  out->supports_ed25519_link_handshake_compat =
    443    protocol_list_supports_protocol(protocols, PRT_LINKAUTH,
    444                                    PROTOVER_LINKAUTH_ED25519_HANDSHAKE);
    445  out->supports_ed25519_link_handshake_any =
    446    protocol_list_supports_protocol_or_later(
    447                                     protocols,
    448                                     PRT_LINKAUTH,
    449                                     PROTOVER_LINKAUTH_ED25519_HANDSHAKE);
    450 
    451  out->supports_extend2_cells =
    452    protocol_list_supports_protocol(protocols, PRT_RELAY,
    453                                    PROTOVER_RELAY_EXTEND2);
    454  out->supports_accepting_ipv6_extends = (
    455    protocol_list_supports_protocol(protocols, PRT_RELAY,
    456                                    PROTOVER_RELAY_ACCEPT_IPV6) ||
    457    protocol_list_supports_protocol(protocols, PRT_RELAY,
    458                                    PROTOVER_RELAY_EXTEND_IPV6));
    459  out->supports_initiating_ipv6_extends =
    460    protocol_list_supports_protocol(protocols, PRT_RELAY,
    461                                    PROTOVER_RELAY_EXTEND_IPV6);
    462  out->supports_canonical_ipv6_conns =
    463    protocol_list_supports_protocol(protocols, PRT_RELAY,
    464                                    PROTOVER_RELAY_CANONICAL_IPV6);
    465 
    466  out->supports_ed25519_hs_intro =
    467    protocol_list_supports_protocol(protocols, PRT_HSINTRO,
    468                                    PROTOVER_HS_INTRO_V3);
    469  out->supports_establish_intro_dos_extension =
    470    protocol_list_supports_protocol(protocols, PRT_HSINTRO,
    471                                    PROTOVER_HS_INTRO_DOS);
    472 
    473  out->supports_v3_rendezvous_point =
    474    protocol_list_supports_protocol(protocols, PRT_HSREND,
    475                                    PROTOVER_HS_RENDEZVOUS_POINT_V3);
    476 
    477  out->supports_v3_hsdir =
    478    protocol_list_supports_protocol(protocols, PRT_HSDIR,
    479                                    PROTOVER_HSDIR_V3);
    480 
    481  out->supports_hs_setup_padding =
    482    protocol_list_supports_protocol(protocols, PRT_PADDING,
    483                                    PROTOVER_HS_SETUP_PADDING);
    484 
    485  out->supports_congestion_control =
    486    protocol_list_supports_protocol(protocols, PRT_FLOWCTRL,
    487                                    PROTOVER_FLOWCTRL_CC) &&
    488    protocol_list_supports_protocol(protocols, PRT_RELAY,
    489                                    PROTOVER_RELAY_NTOR_V3);
    490 
    491  /* Conflux requires congestion control. */
    492  out->supports_conflux =
    493    protocol_list_supports_protocol(protocols, PRT_FLOWCTRL,
    494                                    PROTOVER_FLOWCTRL_CC) &&
    495    protocol_list_supports_protocol(protocols, PRT_CONFLUX,
    496                                    PROTOVER_CONFLUX_V1);
    497 
    498  out->supports_ntor_v3 =
    499    protocol_list_supports_protocol(protocols, PRT_RELAY,
    500                                    PROTOVER_RELAY_NTOR_V3);
    501 
    502  /* CGO requires congestion control and subproto negotiation. */
    503  out->supports_cgo =
    504    protocol_list_supports_protocol(protocols, PRT_FLOWCTRL,
    505                                    PROTOVER_FLOWCTRL_CC) &&
    506    protocol_list_supports_protocol(protocols, PRT_RELAY,
    507                                    PROTOVER_RELAY_NEGOTIATE_SUBPROTO) &&
    508    protocol_list_supports_protocol(protocols, PRT_RELAY,
    509                                    PROTOVER_RELAY_CRYPT_CGO);
    510 
    511  protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out));
    512  cached = strmap_set(protover_summary_map, protocols, new_cached);
    513  tor_assert(!cached);
    514 }
    515 
    516 /** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
    517 * falling back or correcting them based on <b>version</b> as appropriate.
    518 *
    519 * If protocols and version are both NULL or "", returns a summary with no
    520 * flags set.
    521 *
    522 * If the protover string does not contain any recognised protocols, and the
    523 * version is not recognised, sets protocols_known, but does not set any other
    524 * flags. (Empty strings are also treated this way.)
    525 */
    526 void
    527 summarize_protover_flags(protover_summary_flags_t *out,
    528                         const char *protocols,
    529                         const char *version)
    530 {
    531  tor_assert(out);
    532  memset(out, 0, sizeof(*out));
    533  if (protocols && strcmp(protocols, "")) {
    534    memoize_protover_summary(out, protocols);
    535  }
    536  if (version && strcmp(version, "") && !strcmpstart(version, "Tor ")) {
    537    if (!out->protocols_known) {
    538      /* The version is a "Tor" version, and where there is no
    539       * list of protocol versions that we should be looking at instead. */
    540 
    541      out->supports_extend2_cells =
    542        tor_version_as_new_as(version, "0.2.4.8-alpha");
    543      out->protocols_known = 1;
    544    } else {
    545      /* Bug #22447 forces us to filter on this version. */
    546      if (!tor_version_as_new_as(version, "0.3.0.8")) {
    547        out->supports_v3_hsdir = 0;
    548      }
    549    }
    550  }
    551 }
    552 
    553 /**
    554 * Free all space held in the protover_summary_map.
    555 */
    556 void
    557 protover_summary_cache_free_all(void)
    558 {
    559  strmap_free(protover_summary_map, tor_free_);
    560  protover_summary_map = NULL;
    561 }