versions.c 15 KB

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