LCOV - code coverage report
Current view: top level - core/or - versions.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 220 231 95.2 %
Date: 2021-11-24 03:28:48 Functions: 12 12 100.0 %

          Line data    Source code
       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           5 : tor_get_approx_release_date(void)
      26             : {
      27           5 :   char tbuf[ISO_TIME_LEN+1];
      28           5 :   tor_snprintf(tbuf, sizeof(tbuf),
      29             :                "%s 00:00:00", APPROX_RELEASE_DATE);
      30           5 :   time_t result = 0;
      31           5 :   int r = parse_iso_time(tbuf, &result);
      32           5 :   if (BUG(r < 0)) {
      33           0 :     result = 0;
      34             :   }
      35           5 :   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          17 : tor_version_is_obsolete(const char *myversion, const char *versionlist)
      54             : {
      55          17 :   tor_version_t mine, other;
      56          17 :   int found_newer = 0, found_older = 0, found_newer_in_series = 0,
      57          17 :     found_any_in_series = 0, r, same;
      58          17 :   version_status_t ret = VS_UNRECOMMENDED;
      59          17 :   smartlist_t *version_sl;
      60             : 
      61          17 :   log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'",
      62             :             myversion, versionlist);
      63             : 
      64          17 :   if (tor_version_parse(myversion, &mine)) {
      65           0 :     log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion);
      66           0 :     tor_assert(0);
      67             :   }
      68          17 :   version_sl = smartlist_new();
      69          17 :   smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
      70             : 
      71          17 :   if (!strlen(versionlist)) { /* no authorities cared or agreed */
      72           0 :     ret = VS_EMPTY;
      73           0 :     goto done;
      74             :   }
      75             : 
      76          50 :   SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) {
      77          35 :     if (!strcmpstart(cp, "Tor "))
      78          26 :       cp += 4;
      79             : 
      80          35 :     if (tor_version_parse(cp, &other)) {
      81             :       /* Couldn't parse other; it can't be a match. */
      82             :     } else {
      83          34 :       same = tor_version_same_series(&mine, &other);
      84          34 :       if (same)
      85          11 :         found_any_in_series = 1;
      86          34 :       r = tor_version_compare(&mine, &other);
      87          34 :       if (r==0) {
      88           2 :         ret = VS_RECOMMENDED;
      89           2 :         goto done;
      90          32 :       } else if (r<0) {
      91          20 :         found_newer = 1;
      92          20 :         if (same)
      93           5 :           found_newer_in_series = 1;
      94             :       } else if (r>0) {
      95             :         found_older = 1;
      96             :       }
      97             :     }
      98          33 :   } SMARTLIST_FOREACH_END(cp);
      99             : 
     100             :   /* We didn't find the listed version. Is it new or old? */
     101          15 :   if (found_any_in_series && !found_newer_in_series && found_newer) {
     102             :     ret = VS_NEW_IN_SERIES;
     103          12 :   } else if (found_newer && !found_older) {
     104             :     ret = VS_OLD;
     105           5 :   } else if (found_older && !found_newer) {
     106             :     ret = VS_NEW;
     107             :   } else {
     108           2 :     ret = VS_UNRECOMMENDED;
     109             :   }
     110             : 
     111          17 :  done:
     112          54 :   SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
     113          17 :   smartlist_free(version_sl);
     114          17 :   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         224 : tor_version_parse_platform(const char *platform,
     128             :                            tor_version_t *router_version,
     129             :                            int strict)
     130             : {
     131         224 :   char tmp[128];
     132         224 :   char *s, *s2, *start;
     133             : 
     134         224 :   if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
     135             :     return 0;
     136             : 
     137         197 :   start = (char *)eat_whitespace(platform+3);
     138         197 :   if (!*start) return -1;
     139         197 :   s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
     140         197 :   s2 = (char*)eat_whitespace(s);
     141         197 :   if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
     142          12 :     s = (char*)find_whitespace(s2);
     143             : 
     144         197 :   if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
     145             :     return -1;
     146         197 :   strlcpy(tmp, start, s-start+1);
     147             : 
     148         197 :   if (tor_version_parse(tmp, router_version)<0) {
     149          16 :     log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
     150          16 :     return -1;
     151             :   }
     152             : 
     153         181 :   if (strict) {
     154           2 :     if (router_version->major < 0 ||
     155           2 :         router_version->minor < 0 ||
     156           2 :         router_version->micro < 0 ||
     157           2 :         router_version->patchlevel < 0 ||
     158           2 :         router_version->svn_revision < 0) {
     159           0 :       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         220 : tor_version_as_new_as(const char *platform, const char *cutoff)
     172             : {
     173         220 :   tor_version_t cutoff_version, router_version;
     174         220 :   int r;
     175         220 :   tor_assert(platform);
     176             : 
     177         220 :   if (tor_version_parse(cutoff, &cutoff_version)<0) {
     178           4 :     log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
     179           4 :     return 0;
     180             :   }
     181             : 
     182         216 :   r = tor_version_parse_platform(platform, &router_version, 0);
     183         216 :   if (r == 0) {
     184             :     /* nonstandard Tor; be safe and say yes */
     185             :     return 1;
     186         195 :   } 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         179 :   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        2169 : tor_version_parse(const char *s, tor_version_t *out)
     207             : {
     208        2169 :   char *eos=NULL;
     209        2169 :   const char *cp=NULL;
     210        2169 :   int ok = 1;
     211             :   /* Format is:
     212             :    *   "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
     213             :    */
     214        2169 :   tor_assert(s);
     215        2169 :   tor_assert(out);
     216             : 
     217        2169 :   memset(out, 0, sizeof(tor_version_t));
     218        2169 :   out->status = VER_RELEASE;
     219        2169 :   if (!strcasecmpstart(s, "Tor "))
     220          21 :     s += 4;
     221             : 
     222        2169 :   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        2169 :   NUMBER(major);
     244        2158 :   DOT();
     245        2152 :   NUMBER(minor);
     246        2129 :   if (*cp == 0)
     247             :     return 0;
     248        2096 :   else if (*cp == '-')
     249           1 :     goto status_tag;
     250        2095 :   DOT();
     251        2095 :   NUMBER(micro);
     252             : 
     253             :   /* Get status */
     254        2095 :   if (*cp == 0) {
     255             :     return 0;
     256        2066 :   } else if (*cp == '.') {
     257        2041 :     ++cp;
     258          25 :   } else if (*cp == '-') {
     259          18 :     goto status_tag;
     260           7 :   } else if (0==strncmp(cp, "pre", 3)) {
     261           3 :     out->status = VER_PRE;
     262           3 :     cp += 3;
     263           4 :   } else if (0==strncmp(cp, "rc", 2)) {
     264           4 :     out->status = VER_RC;
     265           4 :     cp += 2;
     266             :   } else {
     267             :     return -1;
     268             :   }
     269             : 
     270        2048 :   NUMBER(patchlevel);
     271             : 
     272        2067 :  status_tag:
     273             :   /* Get status tag. */
     274        2067 :   if (*cp == '-' || *cp == '.')
     275         237 :     ++cp;
     276        2067 :   eos = (char*) find_whitespace(cp);
     277        2067 :   if (eos-cp >= (int)sizeof(out->status_tag))
     278           0 :     strlcpy(out->status_tag, cp, sizeof(out->status_tag));
     279             :   else {
     280        2067 :     memcpy(out->status_tag, cp, eos-cp);
     281        2067 :     out->status_tag[eos-cp] = 0;
     282             :   }
     283        2067 :   cp = eat_whitespace(eos);
     284             : 
     285        2067 :   if (!strcmpstart(cp, "(r")) {
     286          11 :     cp += 2;
     287          11 :     out->svn_revision = (int) strtol(cp,&eos,10);
     288        2056 :   } else if (!strcmpstart(cp, "(git-")) {
     289          19 :     char *close_paren = strchr(cp, ')');
     290          19 :     int hexlen;
     291          19 :     char digest[DIGEST_LEN];
     292          19 :     if (! close_paren)
     293           3 :       return -1;
     294          19 :     cp += 5;
     295          19 :     if (close_paren-cp > HEX_DIGEST_LEN)
     296             :       return -1;
     297          18 :     hexlen = (int)(close_paren-cp);
     298          18 :     memwipe(digest, 0, sizeof(digest));
     299          18 :     if (hexlen == 0 || (hexlen % 2) == 1)
     300             :       return -1;
     301          17 :     if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
     302             :       return -1;
     303          16 :     memcpy(out->git_tag, digest, hexlen/2);
     304          16 :     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        1029 : tor_version_compare(tor_version_t *a, tor_version_t *b)
     316             : {
     317        1029 :   int i;
     318        1029 :   tor_assert(a);
     319        1029 :   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        1029 :   CMP(major);
     337        1001 :   CMP(minor);
     338         859 :   CMP(micro);
     339         817 :   CMP(status);
     340         814 :   CMP(patchlevel);
     341         241 :   if ((i = strcmp(a->status_tag, b->status_tag)))
     342             :      return i;
     343         240 :   CMP(svn_revision);
     344         234 :   CMP(git_tag_len);
     345         230 :   if (a->git_tag_len)
     346           3 :      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          34 : tor_version_same_series(tor_version_t *a, tor_version_t *b)
     357             : {
     358          34 :   tor_assert(a);
     359          34 :   tor_assert(b);
     360          68 :   return ((a->major == b->major) &&
     361          34 :           (a->minor == b->minor) &&
     362          26 :           (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         819 : compare_tor_version_str_ptr_(const void **_a, const void **_b)
     370             : {
     371         819 :   const char *a = *_a, *b = *_b;
     372         819 :   int ca, cb;
     373         819 :   tor_version_t va, vb;
     374         819 :   ca = tor_version_parse(a, &va);
     375         819 :   cb = tor_version_parse(b, &vb);
     376             :   /* If they both parse, compare them. */
     377         819 :   if (!ca && !cb)
     378         816 :     return tor_version_compare(&va,&vb);
     379             :   /* If one parses, it comes first. */
     380           3 :   if (!ca && cb)
     381             :     return -1;
     382           1 :   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           0 :   return strcmp(a,b);
     387             : }
     388             : 
     389             : /** Sort a list of string-representations of versions in ascending order. */
     390             : void
     391        1379 : sort_version_list(smartlist_t *versions, int remove_duplicates)
     392             : {
     393        1379 :   smartlist_sort(versions, compare_tor_version_str_ptr_);
     394             : 
     395        1379 :   if (remove_duplicates)
     396        1253 :     smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_);
     397        1379 : }
     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         679 : memoize_protover_summary(protover_summary_flags_t *out,
     418             :                          const char *protocols)
     419             : {
     420         679 :   if (!protover_summary_map)
     421          59 :     protover_summary_map = strmap_new();
     422             : 
     423         679 :   if (strmap_size(protover_summary_map) >= MAX_PROTOVER_SUMMARY_MAP_LEN) {
     424           0 :     protover_summary_cache_free_all();
     425           0 :     tor_assert(protover_summary_map == NULL);
     426           0 :     protover_summary_map = strmap_new();
     427             :   }
     428             : 
     429         679 :   const protover_summary_flags_t *cached =
     430         679 :     strmap_get(protover_summary_map, protocols);
     431             : 
     432         679 :   if (cached != NULL) {
     433             :     /* We found a cached entry; no need to parse this one. */
     434         582 :     memcpy(out, cached, sizeof(protover_summary_flags_t));
     435         582 :     tor_assert(out->protocols_known);
     436             :     return;
     437             :   }
     438             : 
     439          97 :   memset(out, 0, sizeof(*out));
     440          97 :   out->protocols_known = 1;
     441             : 
     442         194 :   out->supports_ed25519_link_handshake_compat =
     443          97 :     protocol_list_supports_protocol(protocols, PRT_LINKAUTH,
     444             :                                     PROTOVER_LINKAUTH_ED25519_HANDSHAKE);
     445         194 :   out->supports_ed25519_link_handshake_any =
     446          97 :     protocol_list_supports_protocol_or_later(
     447             :                                      protocols,
     448             :                                      PRT_LINKAUTH,
     449             :                                      PROTOVER_LINKAUTH_ED25519_HANDSHAKE);
     450             : 
     451         194 :   out->supports_extend2_cells =
     452          97 :     protocol_list_supports_protocol(protocols, PRT_RELAY,
     453             :                                     PROTOVER_RELAY_EXTEND2);
     454         194 :   out->supports_accepting_ipv6_extends = (
     455          97 :     protocol_list_supports_protocol(protocols, PRT_RELAY,
     456         176 :                                     PROTOVER_RELAY_ACCEPT_IPV6) ||
     457          79 :     protocol_list_supports_protocol(protocols, PRT_RELAY,
     458             :                                     PROTOVER_RELAY_EXTEND_IPV6));
     459         194 :   out->supports_initiating_ipv6_extends =
     460          97 :     protocol_list_supports_protocol(protocols, PRT_RELAY,
     461             :                                     PROTOVER_RELAY_EXTEND_IPV6);
     462         194 :   out->supports_canonical_ipv6_conns =
     463          97 :     protocol_list_supports_protocol(protocols, PRT_RELAY,
     464             :                                     PROTOVER_RELAY_CANONICAL_IPV6);
     465             : 
     466         194 :   out->supports_ed25519_hs_intro =
     467          97 :     protocol_list_supports_protocol(protocols, PRT_HSINTRO,
     468             :                                     PROTOVER_HS_INTRO_V3);
     469         194 :   out->supports_establish_intro_dos_extension =
     470          97 :     protocol_list_supports_protocol(protocols, PRT_HSINTRO,
     471             :                                     PROTOVER_HS_INTRO_DOS);
     472             : 
     473         194 :   out->supports_v3_rendezvous_point =
     474          97 :     protocol_list_supports_protocol(protocols, PRT_HSREND,
     475             :                                     PROTOVER_HS_RENDEZVOUS_POINT_V3);
     476             : 
     477         194 :   out->supports_v3_hsdir =
     478          97 :     protocol_list_supports_protocol(protocols, PRT_HSDIR,
     479             :                                     PROTOVER_HSDIR_V3);
     480             : 
     481         194 :   out->supports_hs_setup_padding =
     482          97 :     protocol_list_supports_protocol(protocols, PRT_PADDING,
     483             :                                     PROTOVER_HS_SETUP_PADDING);
     484             : 
     485          97 :   protover_summary_flags_t *new_cached = tor_memdup(out, sizeof(*out));
     486          97 :   cached = strmap_set(protover_summary_map, protocols, new_cached);
     487          97 :   tor_assert(!cached);
     488             : }
     489             : 
     490             : /** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
     491             :  * falling back or correcting them based on <b>version</b> as appropriate.
     492             :  *
     493             :  * If protocols and version are both NULL or "", returns a summary with no
     494             :  * flags set.
     495             :  *
     496             :  * If the protover string does not contain any recognised protocols, and the
     497             :  * version is not recognised, sets protocols_known, but does not set any other
     498             :  * flags. (Empty strings are also treated this way.)
     499             :  */
     500             : void
     501        1021 : summarize_protover_flags(protover_summary_flags_t *out,
     502             :                          const char *protocols,
     503             :                          const char *version)
     504             : {
     505        1021 :   tor_assert(out);
     506        1021 :   memset(out, 0, sizeof(*out));
     507        1021 :   if (protocols && strcmp(protocols, "")) {
     508         679 :     memoize_protover_summary(out, protocols);
     509             :   }
     510        1021 :   if (version && strcmp(version, "") && !strcmpstart(version, "Tor ")) {
     511         130 :     if (!out->protocols_known) {
     512             :       /* The version is a "Tor" version, and where there is no
     513             :        * list of protocol versions that we should be looking at instead. */
     514             : 
     515         106 :       out->supports_extend2_cells =
     516          53 :         tor_version_as_new_as(version, "0.2.4.8-alpha");
     517          53 :       out->protocols_known = 1;
     518             :     } else {
     519             :       /* Bug #22447 forces us to filter on this version. */
     520          77 :       if (!tor_version_as_new_as(version, "0.3.0.8")) {
     521          22 :         out->supports_v3_hsdir = 0;
     522             :       }
     523             :     }
     524             :   }
     525        1021 : }
     526             : 
     527             : /**
     528             :  * Free all space held in the protover_summary_map.
     529             :  */
     530             : void
     531         236 : protover_summary_cache_free_all(void)
     532             : {
     533         236 :   strmap_free(protover_summary_map, tor_free_);
     534         236 :   protover_summary_map = NULL;
     535         236 : }

Generated by: LCOV version 1.14