2016-05-18 02:04:16 +02:00
|
|
|
/* Copyright (c) 2001 Matej Pfajfar.
|
2006-02-09 06:46:49 +01:00
|
|
|
* Copyright (c) 2001-2004, Roger Dingledine.
|
2007-12-12 22:09:01 +01:00
|
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
2017-03-15 21:13:17 +01:00
|
|
|
* Copyright (c) 2007-2017, The Tor Project, Inc. */
|
2004-05-10 19:30:51 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file routerparse.c
|
2016-10-15 02:08:51 +02:00
|
|
|
* \brief Code to parse and validate router descriptors, consenus directories,
|
|
|
|
* and similar objects.
|
|
|
|
*
|
|
|
|
* The objects parsed by this module use a common text-based metaformat,
|
|
|
|
* documented in dir-spec.txt in torspec.git. This module is itself divided
|
|
|
|
* into two major kinds of function: code to handle the metaformat, and code
|
|
|
|
* to convert from particular instances of the metaformat into the
|
|
|
|
* objects that Tor uses.
|
|
|
|
*
|
|
|
|
* The generic parsing code works by calling a table-based tokenizer on the
|
|
|
|
* input string. Each token corresponds to a single line with a token, plus
|
|
|
|
* optional arguments on that line, plus an optional base-64 encoded object
|
|
|
|
* after that line. Each token has a definition in a table of token_rule_t
|
|
|
|
* entries that describes how many arguments it can take, whether it takes an
|
|
|
|
* object, how many times it may appear, whether it must appear first, and so
|
|
|
|
* on.
|
|
|
|
*
|
|
|
|
* The tokenizer function tokenize_string() converts its string input into a
|
|
|
|
* smartlist full of instances of directory_token_t, according to a provided
|
|
|
|
* table of token_rule_t.
|
|
|
|
*
|
|
|
|
* The generic parts of this module additionally include functions for
|
|
|
|
* finding the start and end of signed information inside a signed object, and
|
|
|
|
* computing the digest that will be signed.
|
|
|
|
*
|
|
|
|
* There are also functions for saving objects to disk that have caused
|
|
|
|
* parsing to fail.
|
|
|
|
*
|
|
|
|
* The specific parts of this module describe conversions between
|
|
|
|
* particular lists of directory_token_t and particular objects. The
|
|
|
|
* kinds of objects that can be parsed here are:
|
|
|
|
* <ul>
|
|
|
|
* <li>router descriptors (managed from routerlist.c)
|
|
|
|
* <li>extra-info documents (managed from routerlist.c)
|
|
|
|
* <li>microdescriptors (managed from microdesc.c)
|
|
|
|
* <li>vote and consensus networkstatus documents, and the routerstatus_t
|
|
|
|
* objects that they comprise (managed from networkstatus.c)
|
|
|
|
* <li>detached-signature objects used by authorities for gathering
|
|
|
|
* signatures on the networkstatus consensus (managed from dirvote.c)
|
|
|
|
* <li>authority key certificates (managed from routerlist.c)
|
|
|
|
* <li>hidden service descriptors (managed from rendcommon.c and rendcache.c)
|
|
|
|
* </ul>
|
|
|
|
*
|
|
|
|
* For no terribly good reason, the functions to <i>generate</i> signatures on
|
|
|
|
* the above directory objects are also in this module.
|
2004-05-10 19:30:51 +02:00
|
|
|
**/
|
|
|
|
|
2015-01-29 15:57:00 +01:00
|
|
|
#define ROUTERPARSE_PRIVATE
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
#include "or.h"
|
2012-10-15 20:48:34 +02:00
|
|
|
#include "circuitstats.h"
|
2018-04-06 23:42:15 +02:00
|
|
|
#include "config.h"
|
2018-04-06 23:23:29 +02:00
|
|
|
#include "crypto_util.h"
|
2018-05-03 19:50:18 +02:00
|
|
|
#include "dirauth/shared_random.h"
|
2010-07-22 12:09:49 +02:00
|
|
|
#include "dirserv.h"
|
2018-04-06 23:42:15 +02:00
|
|
|
#include "entrynodes.h"
|
|
|
|
#include "memarea.h"
|
|
|
|
#include "microdesc.h"
|
|
|
|
#include "networkstatus.h"
|
2016-03-11 19:26:04 +01:00
|
|
|
#include "parsecommon.h"
|
2010-07-23 20:51:25 +02:00
|
|
|
#include "policies.h"
|
2016-08-26 18:49:00 +02:00
|
|
|
#include "protover.h"
|
2010-07-21 17:52:54 +02:00
|
|
|
#include "rendcommon.h"
|
2010-07-23 22:57:20 +02:00
|
|
|
#include "rephist.h"
|
2018-04-06 23:42:15 +02:00
|
|
|
#include "router.h"
|
2014-10-01 17:54:07 +02:00
|
|
|
#include "routerkeys.h"
|
2018-04-06 23:42:15 +02:00
|
|
|
#include "routerlist.h"
|
2010-07-23 23:23:43 +02:00
|
|
|
#include "routerparse.h"
|
2016-06-30 00:47:41 +02:00
|
|
|
#include "sandbox.h"
|
2018-05-01 17:31:33 +02:00
|
|
|
#include "shared_random_client.h"
|
2018-04-06 23:42:15 +02:00
|
|
|
#include "torcert.h"
|
2018-05-01 17:10:58 +02:00
|
|
|
#include "voting_schedule.h"
|
2014-10-01 05:36:47 +02:00
|
|
|
|
2018-04-05 20:27:30 +02:00
|
|
|
#include "dirauth/dirvote.h"
|
|
|
|
|
2018-06-15 19:27:11 +02:00
|
|
|
#include "document_signature_st.h"
|
2018-06-15 19:45:15 +02:00
|
|
|
#include "networkstatus_st.h"
|
2018-06-15 19:31:47 +02:00
|
|
|
#include "networkstatus_voter_info_st.h"
|
2018-06-15 19:49:30 +02:00
|
|
|
#include "ns_detached_signatures_st.h"
|
2018-06-15 18:36:59 +02:00
|
|
|
#include "rend_authorized_client_st.h"
|
2018-06-15 18:23:06 +02:00
|
|
|
#include "rend_intro_point_st.h"
|
2018-06-15 18:18:17 +02:00
|
|
|
#include "rend_service_descriptor_st.h"
|
2018-06-15 20:07:17 +02:00
|
|
|
#include "routerlist_st.h"
|
2018-06-15 18:12:15 +02:00
|
|
|
#include "tor_version_st.h"
|
2018-06-15 19:23:02 +02:00
|
|
|
#include "vote_microdesc_hash_st.h"
|
|
|
|
#include "vote_routerstatus_st.h"
|
2018-06-15 18:12:15 +02:00
|
|
|
|
2018-06-15 18:18:17 +02:00
|
|
|
#undef log
|
|
|
|
#include <math.h>
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
/****************************************************************************/
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in router descriptors */
|
2007-04-16 06:18:21 +02:00
|
|
|
static token_rule_t routerdesc_token_table[] = {
|
|
|
|
T0N("reject", K_REJECT, ARGS, NO_OBJ ),
|
2007-05-15 00:51:05 +02:00
|
|
|
T0N("accept", K_ACCEPT, ARGS, NO_OBJ ),
|
2011-03-06 19:26:38 +01:00
|
|
|
T0N("reject6", K_REJECT6, ARGS, NO_OBJ ),
|
|
|
|
T0N("accept6", K_ACCEPT6, ARGS, NO_OBJ ),
|
2008-07-24 15:44:04 +02:00
|
|
|
T1_START( "router", K_ROUTER, GE(5), NO_OBJ ),
|
2012-10-25 03:59:55 +02:00
|
|
|
T01("ipv6-policy", K_IPV6_POLICY, CONCAT_ARGS, NO_OBJ),
|
2007-05-02 23:37:55 +02:00
|
|
|
T1( "signing-key", K_SIGNING_KEY, NO_ARGS, NEED_KEY_1024 ),
|
|
|
|
T1( "onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024 ),
|
2012-12-04 21:58:18 +01:00
|
|
|
T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
|
2007-06-06 06:51:30 +02:00
|
|
|
T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
|
2007-04-16 18:28:06 +02:00
|
|
|
T01("uptime", K_UPTIME, GE(1), NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
T01("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
|
2007-04-16 18:28:06 +02:00
|
|
|
T01("hibernating", K_HIBERNATING, GE(1), NO_OBJ ),
|
2007-05-15 00:51:05 +02:00
|
|
|
T01("platform", K_PLATFORM, CONCAT_ARGS, NO_OBJ ),
|
2016-08-19 20:10:20 +02:00
|
|
|
T01("proto", K_PROTO, CONCAT_ARGS, NO_OBJ ),
|
2007-05-15 00:51:05 +02:00
|
|
|
T01("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
T01("read-history", K_READ_HISTORY, ARGS, NO_OBJ ),
|
|
|
|
T01("write-history", K_WRITE_HISTORY, ARGS, NO_OBJ ),
|
2007-04-16 18:28:06 +02:00
|
|
|
T01("extra-info-digest", K_EXTRA_INFO_DIGEST, GE(1), NO_OBJ ),
|
2007-10-29 20:10:42 +01:00
|
|
|
T01("hidden-service-dir", K_HIDDEN_SERVICE_DIR, NO_ARGS, NO_OBJ ),
|
2014-10-01 05:36:47 +02:00
|
|
|
T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ),
|
2015-06-01 17:24:55 +02:00
|
|
|
T01("master-key-ed25519", K_MASTER_KEY_ED25519, GE(1), NO_OBJ ),
|
2014-10-01 05:36:47 +02:00
|
|
|
T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ),
|
2014-10-01 17:54:07 +02:00
|
|
|
T01("onion-key-crosscert", K_ONION_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ),
|
|
|
|
T01("ntor-onion-key-crosscert", K_NTOR_ONION_KEY_CROSSCERT,
|
|
|
|
EQ(1), NEED_OBJ ),
|
2014-10-01 05:36:47 +02:00
|
|
|
|
2008-09-26 20:58:45 +02:00
|
|
|
T01("allow-single-hop-exits",K_ALLOW_SINGLE_HOP_EXITS, NO_ARGS, NO_OBJ ),
|
2007-05-15 00:51:05 +02:00
|
|
|
|
|
|
|
T01("family", K_FAMILY, ARGS, NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
T01("caches-extra-info", K_CACHES_EXTRA_INFO, NO_ARGS, NO_OBJ ),
|
2011-11-08 22:51:30 +01:00
|
|
|
T0N("or-address", K_OR_ADDRESS, GE(1), NO_OBJ ),
|
2007-05-15 00:51:05 +02:00
|
|
|
|
|
|
|
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
2007-04-16 18:28:06 +02:00
|
|
|
T1( "bandwidth", K_BANDWIDTH, GE(3), NO_OBJ ),
|
2007-09-26 18:19:44 +02:00
|
|
|
A01("@purpose", A_PURPOSE, GE(1), NO_OBJ ),
|
2014-10-28 18:12:52 +01:00
|
|
|
T01("tunnelled-dir-server",K_DIR_TUNNELLED, NO_ARGS, NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in extra-info documents. */
|
2007-04-16 06:18:21 +02:00
|
|
|
static token_rule_t extrainfo_token_table[] = {
|
2007-06-06 06:51:30 +02:00
|
|
|
T1_END( "router-signature", K_ROUTER_SIGNATURE, NO_ARGS, NEED_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
T1( "published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
|
2015-05-28 16:42:22 +02:00
|
|
|
T01("identity-ed25519", K_IDENTITY_ED25519, NO_ARGS, NEED_OBJ ),
|
|
|
|
T01("router-sig-ed25519", K_ROUTER_SIG_ED25519, GE(1), NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
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 ),
|
2009-08-14 15:30:24 +02:00
|
|
|
T01("dirreq-stats-end", K_DIRREQ_END, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v2-ips", K_DIRREQ_V2_IPS, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v3-ips", K_DIRREQ_V3_IPS, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v2-reqs", K_DIRREQ_V2_REQS, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v3-reqs", K_DIRREQ_V3_REQS, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v2-share", K_DIRREQ_V2_SHARE, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v3-share", K_DIRREQ_V3_SHARE, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v2-resp", K_DIRREQ_V2_RESP, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v3-resp", K_DIRREQ_V3_RESP, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v2-direct-dl", K_DIRREQ_V2_DIR, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v3-direct-dl", K_DIRREQ_V3_DIR, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v2-tunneled-dl", K_DIRREQ_V2_TUN, ARGS, NO_OBJ ),
|
|
|
|
T01("dirreq-v3-tunneled-dl", K_DIRREQ_V3_TUN, ARGS, NO_OBJ ),
|
|
|
|
T01("entry-stats-end", K_ENTRY_END, ARGS, NO_OBJ ),
|
|
|
|
T01("entry-ips", K_ENTRY_IPS, ARGS, NO_OBJ ),
|
|
|
|
T01("cell-stats-end", K_CELL_END, ARGS, NO_OBJ ),
|
|
|
|
T01("cell-processed-cells", K_CELL_PROCESSED, ARGS, NO_OBJ ),
|
|
|
|
T01("cell-queued-cells", K_CELL_QUEUED, ARGS, NO_OBJ ),
|
|
|
|
T01("cell-time-in-queue", K_CELL_TIME, ARGS, NO_OBJ ),
|
|
|
|
T01("cell-circuits-per-decile", K_CELL_CIRCS, ARGS, NO_OBJ ),
|
|
|
|
T01("exit-stats-end", K_EXIT_END, ARGS, NO_OBJ ),
|
|
|
|
T01("exit-kibibytes-written", K_EXIT_WRITTEN, ARGS, NO_OBJ ),
|
|
|
|
T01("exit-kibibytes-read", K_EXIT_READ, ARGS, NO_OBJ ),
|
|
|
|
T01("exit-streams-opened", K_EXIT_OPENED, ARGS, NO_OBJ ),
|
|
|
|
|
2007-06-06 06:51:30 +02:00
|
|
|
T1_START( "extra-info", K_EXTRA_INFO, GE(2), NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2014-01-30 12:48:49 +01:00
|
|
|
/** List of tokens recognized in the body part of v3 networkstatus
|
2007-05-15 00:51:05 +02:00
|
|
|
* documents. */
|
2007-04-16 06:18:21 +02:00
|
|
|
static token_rule_t rtrstatus_token_table[] = {
|
2008-08-15 01:01:21 +02:00
|
|
|
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
|
2009-09-23 21:23:04 +02:00
|
|
|
T1( "r", K_R, GE(7), NO_OBJ ),
|
2012-08-14 14:03:58 +02:00
|
|
|
T0N("a", K_A, GE(1), NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
T1( "s", K_S, ARGS, NO_OBJ ),
|
|
|
|
T01("v", K_V, CONCAT_ARGS, NO_OBJ ),
|
2008-08-15 01:00:44 +02:00
|
|
|
T01("w", K_W, ARGS, NO_OBJ ),
|
2009-08-20 17:39:22 +02:00
|
|
|
T0N("m", K_M, CONCAT_ARGS, NO_OBJ ),
|
2014-10-29 18:36:21 +01:00
|
|
|
T0N("id", K_ID, GE(2), NO_OBJ ),
|
2016-09-22 16:14:39 +02:00
|
|
|
T01("pr", K_PROTO, CONCAT_ARGS, NO_OBJ ),
|
2007-04-16 06:18:21 +02:00
|
|
|
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2008-12-23 18:56:31 +01:00
|
|
|
/** List of tokens common to V3 authority certificates and V3 consensuses. */
|
2007-05-02 23:37:55 +02:00
|
|
|
#define CERTIFICATE_MEMBERS \
|
|
|
|
T1("dir-key-certificate-version", K_DIR_KEY_CERTIFICATE_VERSION, \
|
2007-05-25 21:41:31 +02:00
|
|
|
GE(1), NO_OBJ ), \
|
2007-05-02 23:37:55 +02:00
|
|
|
T1("dir-identity-key", K_DIR_IDENTITY_KEY, NO_ARGS, NEED_KEY ),\
|
|
|
|
T1("dir-key-published",K_DIR_KEY_PUBLISHED, CONCAT_ARGS, NO_OBJ), \
|
|
|
|
T1("dir-key-expires", K_DIR_KEY_EXPIRES, CONCAT_ARGS, NO_OBJ), \
|
|
|
|
T1("dir-signing-key", K_DIR_SIGNING_KEY, NO_ARGS, NEED_KEY ),\
|
2013-11-14 15:37:41 +01:00
|
|
|
T1("dir-key-crosscert", K_DIR_KEY_CROSSCERT, NO_ARGS, NEED_OBJ ),\
|
2007-05-02 23:37:55 +02:00
|
|
|
T1("dir-key-certification", K_DIR_KEY_CERTIFICATION, \
|
2007-10-19 18:28:10 +02:00
|
|
|
NO_ARGS, NEED_OBJ), \
|
|
|
|
T01("dir-address", K_DIR_ADDRESS, GE(1), NO_OBJ),
|
2007-05-02 23:37:55 +02:00
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in V3 authority certificates. */
|
2007-05-02 23:37:55 +02:00
|
|
|
static token_rule_t dir_key_certificate_table[] = {
|
|
|
|
CERTIFICATE_MEMBERS
|
|
|
|
T1("fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in rendezvous service descriptors */
|
2007-10-28 20:48:14 +01:00
|
|
|
static token_rule_t desc_token_table[] = {
|
2007-10-28 20:48:16 +01:00
|
|
|
T1_START("rendezvous-service-descriptor", R_RENDEZVOUS_SERVICE_DESCRIPTOR,
|
|
|
|
EQ(1), NO_OBJ),
|
2007-10-28 20:48:14 +01:00
|
|
|
T1("version", R_VERSION, EQ(1), NO_OBJ),
|
|
|
|
T1("permanent-key", R_PERMANENT_KEY, NO_ARGS, NEED_KEY_1024),
|
|
|
|
T1("secret-id-part", R_SECRET_ID_PART, EQ(1), NO_OBJ),
|
|
|
|
T1("publication-time", R_PUBLICATION_TIME, CONCAT_ARGS, NO_OBJ),
|
|
|
|
T1("protocol-versions", R_PROTOCOL_VERSIONS, EQ(1), NO_OBJ),
|
2007-11-27 22:06:34 +01:00
|
|
|
T01("introduction-points", R_INTRODUCTION_POINTS, NO_ARGS, NEED_OBJ),
|
2007-10-28 20:48:16 +01:00
|
|
|
T1_END("signature", R_SIGNATURE, NO_ARGS, NEED_OBJ),
|
2007-10-28 20:48:14 +01:00
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in the (encrypted) list of introduction points of
|
2007-10-28 20:48:14 +01:00
|
|
|
* rendezvous service descriptors */
|
|
|
|
static token_rule_t ipo_token_table[] = {
|
2007-10-28 20:48:16 +01:00
|
|
|
T1_START("introduction-point", R_IPO_IDENTIFIER, EQ(1), NO_OBJ),
|
2007-10-28 20:48:14 +01:00
|
|
|
T1("ip-address", R_IPO_IP_ADDRESS, EQ(1), NO_OBJ),
|
|
|
|
T1("onion-port", R_IPO_ONION_PORT, EQ(1), NO_OBJ),
|
|
|
|
T1("onion-key", R_IPO_ONION_KEY, NO_ARGS, NEED_KEY_1024),
|
|
|
|
T1("service-key", R_IPO_SERVICE_KEY, NO_ARGS, NEED_KEY_1024),
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in the (possibly encrypted) list of introduction
|
2008-08-08 16:36:11 +02:00
|
|
|
* points of rendezvous service descriptors */
|
|
|
|
static token_rule_t client_keys_token_table[] = {
|
|
|
|
T1_START("client-name", C_CLIENT_NAME, CONCAT_ARGS, NO_OBJ),
|
|
|
|
T1("descriptor-cookie", C_DESCRIPTOR_COOKIE, EQ(1), NO_OBJ),
|
|
|
|
T01("client-key", C_CLIENT_KEY, NO_ARGS, NEED_SKEY_1024),
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in V3 networkstatus votes. */
|
2008-02-05 22:39:29 +01:00
|
|
|
static token_rule_t networkstatus_token_table[] = {
|
2010-02-22 07:58:10 +01:00
|
|
|
T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
|
2007-05-02 23:37:55 +02:00
|
|
|
GE(1), NO_OBJ ),
|
|
|
|
T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
|
|
|
|
T1("published", K_PUBLISHED, CONCAT_ARGS, NO_OBJ ),
|
2007-06-03 05:05:10 +02:00
|
|
|
T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
2007-06-03 05:05:10 +02:00
|
|
|
T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
|
2007-06-13 20:16:05 +02:00
|
|
|
T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
|
2009-09-15 04:15:57 +02:00
|
|
|
T01("params", K_PARAMS, ARGS, NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
|
2016-05-03 17:36:09 +02:00
|
|
|
T01("signing-ed25519", K_SIGNING_CERT_ED, NO_ARGS , NEED_OBJ ),
|
|
|
|
T01("shared-rand-participate",K_SR_FLAG, NO_ARGS, NO_OBJ ),
|
|
|
|
T0N("shared-rand-commit", K_COMMIT, GE(3), NO_OBJ ),
|
|
|
|
T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2), NO_OBJ ),
|
|
|
|
T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
|
2015-01-09 17:36:47 +01:00
|
|
|
T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
|
2016-08-19 23:31:49 +02:00
|
|
|
T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
|
|
|
|
CERTIFICATE_MEMBERS
|
|
|
|
|
|
|
|
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
|
|
|
T1( "contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
|
2007-06-04 21:19:01 +02:00
|
|
|
T1( "dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
|
2008-05-12 04:14:01 +02:00
|
|
|
T01("legacy-dir-key", K_LEGACY_DIR_KEY, GE(1), NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
2007-07-17 19:49:16 +02:00
|
|
|
T1( "consensus-methods", K_CONSENSUS_METHODS, GE(1), NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
2008-12-23 18:56:31 +01:00
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in V3 networkstatus consensuses. */
|
2007-06-05 00:29:00 +02:00
|
|
|
static token_rule_t networkstatus_consensus_token_table[] = {
|
2010-02-22 07:58:10 +01:00
|
|
|
T1_START("network-status-version", K_NETWORK_STATUS_VERSION,
|
2007-05-02 23:37:55 +02:00
|
|
|
GE(1), NO_OBJ ),
|
|
|
|
T1("vote-status", K_VOTE_STATUS, GE(1), NO_OBJ ),
|
2007-06-03 05:05:10 +02:00
|
|
|
T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
2007-06-03 05:05:10 +02:00
|
|
|
T1("voting-delay", K_VOTING_DELAY, GE(2), NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
|
|
|
|
T0N("opt", K_OPT, CONCAT_ARGS, OBJ_OK ),
|
|
|
|
|
2009-04-09 21:58:16 +02:00
|
|
|
T1N("dir-source", K_DIR_SOURCE, GE(6), NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
T1N("contact", K_CONTACT, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T1N("vote-digest", K_VOTE_DIGEST, GE(1), NO_OBJ ),
|
|
|
|
|
|
|
|
T1( "known-flags", K_KNOWN_FLAGS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
|
|
|
|
T01("client-versions", K_CLIENT_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
2007-10-19 04:15:47 +02:00
|
|
|
T01("server-versions", K_SERVER_VERSIONS, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ),
|
2009-09-15 04:15:57 +02:00
|
|
|
T01("params", K_PARAMS, ARGS, NO_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
|
2016-05-03 17:36:09 +02:00
|
|
|
T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2), NO_OBJ ),
|
|
|
|
T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
|
|
|
|
|
2016-08-19 23:31:49 +02:00
|
|
|
T01("recommended-client-protocols", K_RECOMMENDED_CLIENT_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("recommended-relay-protocols", K_RECOMMENDED_RELAY_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("required-client-protocols", K_REQUIRED_CLIENT_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T01("required-relay-protocols", K_REQUIRED_RELAY_PROTOCOLS,
|
|
|
|
CONCAT_ARGS, NO_OBJ ),
|
|
|
|
|
2007-05-02 23:37:55 +02:00
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2014-01-30 12:48:49 +01:00
|
|
|
/** List of tokens recognized in the footer of v1 directory footers. */
|
2007-06-03 05:05:10 +02:00
|
|
|
static token_rule_t networkstatus_vote_footer_token_table[] = {
|
2010-02-27 22:33:46 +01:00
|
|
|
T01("directory-footer", K_DIRECTORY_FOOTER, NO_ARGS, NO_OBJ ),
|
|
|
|
T01("bandwidth-weights", K_BW_WEIGHTS, ARGS, NO_OBJ ),
|
|
|
|
T( "directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
|
2007-05-02 23:37:55 +02:00
|
|
|
END_OF_TABLE
|
|
|
|
};
|
2007-06-03 05:05:10 +02:00
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in detached networkstatus signature documents. */
|
2007-07-27 20:33:28 +02:00
|
|
|
static token_rule_t networkstatus_detached_signature_token_table[] = {
|
|
|
|
T1_START("consensus-digest", K_CONSENSUS_DIGEST, GE(1), NO_OBJ ),
|
2009-09-16 18:34:44 +02:00
|
|
|
T("additional-digest", K_ADDITIONAL_DIGEST,GE(3), NO_OBJ ),
|
2007-07-27 20:33:28 +02:00
|
|
|
T1("valid-after", K_VALID_AFTER, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T1("fresh-until", K_FRESH_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
T1("valid-until", K_VALID_UNTIL, CONCAT_ARGS, NO_OBJ ),
|
2009-09-16 18:34:44 +02:00
|
|
|
T("additional-signature", K_ADDITIONAL_SIGNATURE, GE(4), NEED_OBJ ),
|
|
|
|
T1N("directory-signature", K_DIRECTORY_SIGNATURE, GE(2), NEED_OBJ ),
|
2007-07-27 20:33:28 +02:00
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** List of tokens recognized in microdescriptors */
|
2009-08-24 18:51:33 +02:00
|
|
|
static token_rule_t microdesc_token_table[] = {
|
|
|
|
T1_START("onion-key", K_ONION_KEY, NO_ARGS, NEED_KEY_1024),
|
2012-12-04 21:58:18 +01:00
|
|
|
T01("ntor-onion-key", K_ONION_KEY_NTOR, GE(1), NO_OBJ ),
|
2014-10-08 17:33:59 +02:00
|
|
|
T0N("id", K_ID, GE(2), NO_OBJ ),
|
2012-08-31 23:02:19 +02:00
|
|
|
T0N("a", K_A, GE(1), NO_OBJ ),
|
2009-08-24 18:51:33 +02:00
|
|
|
T01("family", K_FAMILY, ARGS, NO_OBJ ),
|
|
|
|
T01("p", K_P, CONCAT_ARGS, NO_OBJ ),
|
2012-10-25 03:59:55 +02:00
|
|
|
T01("p6", K_P6, CONCAT_ARGS, NO_OBJ ),
|
2009-08-24 18:51:33 +02:00
|
|
|
A01("@last-listed", A_LAST_LISTED, CONCAT_ARGS, NO_OBJ ),
|
|
|
|
END_OF_TABLE
|
|
|
|
};
|
|
|
|
|
2007-04-16 06:18:21 +02:00
|
|
|
#undef T
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
/* static function prototypes */
|
|
|
|
static int router_add_exit_policy(routerinfo_t *router,directory_token_t *tok);
|
2012-10-24 21:03:29 +02:00
|
|
|
static addr_policy_t *router_parse_addr_policy(directory_token_t *tok,
|
|
|
|
unsigned fmt_flags);
|
2006-12-29 21:50:24 +01:00
|
|
|
static addr_policy_t *router_parse_addr_policy_private(directory_token_t *tok);
|
|
|
|
|
2014-10-01 05:36:47 +02:00
|
|
|
static int router_get_hash_impl_helper(const char *s, size_t s_len,
|
|
|
|
const char *start_str,
|
|
|
|
const char *end_str, char end_c,
|
2017-05-03 17:48:08 +02:00
|
|
|
int log_severity,
|
2014-10-01 05:36:47 +02:00
|
|
|
const char **start_out, const char **end_out);
|
2010-02-25 10:31:36 +01:00
|
|
|
static int router_get_hash_impl(const char *s, size_t s_len, char *digest,
|
2007-05-22 20:52:32 +02:00
|
|
|
const char *start_str, const char *end_str,
|
2009-08-24 18:51:33 +02:00
|
|
|
char end_char,
|
|
|
|
digest_algorithm_t alg);
|
2010-02-27 21:34:02 +01:00
|
|
|
static int router_get_hashes_impl(const char *s, size_t s_len,
|
2016-02-10 21:28:19 +01:00
|
|
|
common_digests_t *digests,
|
2009-09-16 23:01:01 +02:00
|
|
|
const char *start_str, const char *end_str,
|
|
|
|
char end_char);
|
2004-05-10 19:30:51 +02:00
|
|
|
static smartlist_t *find_all_exitpolicy(smartlist_t *s);
|
2016-03-11 19:26:04 +01:00
|
|
|
|
2016-12-13 15:36:59 +01:00
|
|
|
#define CST_NO_CHECK_OBJTYPE (1<<0)
|
2007-05-15 00:51:05 +02:00
|
|
|
static int check_signature_token(const char *digest,
|
2009-09-14 18:02:00 +02:00
|
|
|
ssize_t digest_len,
|
2007-05-15 00:51:05 +02:00
|
|
|
directory_token_t *tok,
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_pk_t *pkey,
|
2008-12-12 19:31:39 +01:00
|
|
|
int flags,
|
2007-05-15 00:51:05 +02:00
|
|
|
const char *doctype);
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-03-26 18:50:27 +01:00
|
|
|
#undef DEBUG_AREA_ALLOC
|
|
|
|
|
|
|
|
#ifdef DEBUG_AREA_ALLOC
|
|
|
|
#define DUMP_AREA(a,name) STMT_BEGIN \
|
|
|
|
size_t alloc=0, used=0; \
|
|
|
|
memarea_get_stats((a),&alloc,&used); \
|
|
|
|
log_debug(LD_MM, "Area for %s has %lu allocated; using %lu.", \
|
|
|
|
name, (unsigned long)alloc, (unsigned long)used); \
|
|
|
|
STMT_END
|
2017-09-15 22:24:44 +02:00
|
|
|
#else /* !(defined(DEBUG_AREA_ALLOC)) */
|
2008-03-26 18:50:27 +01:00
|
|
|
#define DUMP_AREA(a,name) STMT_NIL
|
2017-09-15 22:24:44 +02:00
|
|
|
#endif /* defined(DEBUG_AREA_ALLOC) */
|
2008-03-26 18:50:27 +01:00
|
|
|
|
2016-06-25 07:20:46 +02:00
|
|
|
/* Dump mechanism for unparseable descriptors */
|
|
|
|
|
2016-06-18 00:35:58 +02:00
|
|
|
/** List of dumped descriptors for FIFO cleanup purposes */
|
2016-06-25 09:47:53 +02:00
|
|
|
STATIC smartlist_t *descs_dumped = NULL;
|
2016-06-18 00:35:58 +02:00
|
|
|
/** Total size of dumped descriptors for FIFO cleanup */
|
2016-06-30 00:40:28 +02:00
|
|
|
STATIC uint64_t len_descs_dumped = 0;
|
2016-06-30 02:39:29 +02:00
|
|
|
/** Directory to stash dumps in */
|
|
|
|
static int have_dump_desc_dir = 0;
|
|
|
|
static int problem_with_dump_desc_dir = 0;
|
|
|
|
|
|
|
|
#define DESC_DUMP_DATADIR_SUBDIR "unparseable-descs"
|
2016-06-30 06:36:23 +02:00
|
|
|
#define DESC_DUMP_BASE_FILENAME "unparseable-desc"
|
2016-06-18 00:35:58 +02:00
|
|
|
|
2016-06-30 02:39:29 +02:00
|
|
|
/** Find the dump directory and check if we'll be able to create it */
|
|
|
|
static void
|
|
|
|
dump_desc_init(void)
|
|
|
|
{
|
|
|
|
char *dump_desc_dir;
|
|
|
|
|
|
|
|
dump_desc_dir = get_datadir_fname(DESC_DUMP_DATADIR_SUBDIR);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We just check for it, don't create it at this point; we'll
|
|
|
|
* create it when we need it if it isn't already there.
|
|
|
|
*/
|
|
|
|
if (check_private_dir(dump_desc_dir, CPD_CHECK, get_options()->User) < 0) {
|
|
|
|
/* Error, log and flag it as having a problem */
|
|
|
|
log_notice(LD_DIR,
|
|
|
|
"Doesn't look like we'll be able to create descriptor dump "
|
|
|
|
"directory %s; dumps will be disabled.",
|
|
|
|
dump_desc_dir);
|
|
|
|
problem_with_dump_desc_dir = 1;
|
|
|
|
tor_free(dump_desc_dir);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if it exists */
|
|
|
|
switch (file_status(dump_desc_dir)) {
|
|
|
|
case FN_DIR:
|
|
|
|
/* We already have a directory */
|
|
|
|
have_dump_desc_dir = 1;
|
|
|
|
break;
|
|
|
|
case FN_NOENT:
|
|
|
|
/* Nothing, we'll need to create it later */
|
|
|
|
have_dump_desc_dir = 0;
|
|
|
|
break;
|
|
|
|
case FN_ERROR:
|
|
|
|
/* Log and flag having a problem */
|
|
|
|
log_notice(LD_DIR,
|
|
|
|
"Couldn't check whether descriptor dump directory %s already"
|
|
|
|
" exists: %s",
|
|
|
|
dump_desc_dir, strerror(errno));
|
|
|
|
problem_with_dump_desc_dir = 1;
|
2016-07-01 16:02:22 +02:00
|
|
|
break;
|
2016-06-30 02:39:29 +02:00
|
|
|
case FN_FILE:
|
|
|
|
case FN_EMPTY:
|
|
|
|
default:
|
|
|
|
/* Something else was here! */
|
|
|
|
log_notice(LD_DIR,
|
|
|
|
"Descriptor dump directory %s already exists and isn't a "
|
|
|
|
"directory",
|
|
|
|
dump_desc_dir);
|
|
|
|
problem_with_dump_desc_dir = 1;
|
|
|
|
}
|
|
|
|
|
2016-06-30 06:36:23 +02:00
|
|
|
if (have_dump_desc_dir && !problem_with_dump_desc_dir) {
|
|
|
|
dump_desc_populate_fifo_from_directory(dump_desc_dir);
|
|
|
|
}
|
|
|
|
|
2016-06-30 02:39:29 +02:00
|
|
|
tor_free(dump_desc_dir);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Create the dump directory if needed and possible */
|
|
|
|
static void
|
|
|
|
dump_desc_create_dir(void)
|
|
|
|
{
|
|
|
|
char *dump_desc_dir;
|
|
|
|
|
|
|
|
/* If the problem flag is set, skip it */
|
|
|
|
if (problem_with_dump_desc_dir) return;
|
|
|
|
|
|
|
|
/* Do we need it? */
|
|
|
|
if (!have_dump_desc_dir) {
|
|
|
|
dump_desc_dir = get_datadir_fname(DESC_DUMP_DATADIR_SUBDIR);
|
|
|
|
|
|
|
|
if (check_private_dir(dump_desc_dir, CPD_CREATE,
|
|
|
|
get_options()->User) < 0) {
|
|
|
|
log_notice(LD_DIR,
|
|
|
|
"Failed to create descriptor dump directory %s",
|
|
|
|
dump_desc_dir);
|
|
|
|
problem_with_dump_desc_dir = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Okay, we created it */
|
|
|
|
have_dump_desc_dir = 1;
|
|
|
|
|
|
|
|
tor_free(dump_desc_dir);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-18 00:35:58 +02:00
|
|
|
/** Dump desc FIFO/cleanup; take ownership of the given filename, add it to
|
|
|
|
* the FIFO, and clean up the oldest entries to the extent they exceed the
|
2016-06-25 07:20:46 +02:00
|
|
|
* configured cap. If any old entries with a matching hash existed, they
|
|
|
|
* just got overwritten right before this was called and we should adjust
|
|
|
|
* the total size counter without deleting them.
|
|
|
|
*/
|
2016-06-18 00:35:58 +02:00
|
|
|
static void
|
2016-06-25 07:20:46 +02:00
|
|
|
dump_desc_fifo_add_and_clean(char *filename, const uint8_t *digest_sha256,
|
|
|
|
size_t len)
|
2016-06-18 00:35:58 +02:00
|
|
|
{
|
|
|
|
dumped_desc_t *ent = NULL, *tmp;
|
2016-06-30 00:40:28 +02:00
|
|
|
uint64_t max_len;
|
2016-06-18 00:35:58 +02:00
|
|
|
|
2016-06-25 07:20:46 +02:00
|
|
|
tor_assert(filename != NULL);
|
|
|
|
tor_assert(digest_sha256 != NULL);
|
|
|
|
|
2016-06-18 00:35:58 +02:00
|
|
|
if (descs_dumped == NULL) {
|
|
|
|
/* We better have no length, then */
|
|
|
|
tor_assert(len_descs_dumped == 0);
|
|
|
|
/* Make a smartlist */
|
|
|
|
descs_dumped = smartlist_new();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Make a new entry to put this one in */
|
|
|
|
ent = tor_malloc_zero(sizeof(*ent));
|
|
|
|
ent->filename = filename;
|
|
|
|
ent->len = len;
|
2016-06-30 06:36:23 +02:00
|
|
|
ent->when = time(NULL);
|
2016-06-25 07:20:46 +02:00
|
|
|
memcpy(ent->digest_sha256, digest_sha256, DIGEST256_LEN);
|
2016-06-18 00:35:58 +02:00
|
|
|
|
|
|
|
/* Do we need to do some cleanup? */
|
2016-06-25 07:20:46 +02:00
|
|
|
max_len = get_options()->MaxUnparseableDescSizeToLog;
|
|
|
|
/* Iterate over the list until we've freed enough space */
|
2016-06-30 00:40:28 +02:00
|
|
|
while (len > max_len - len_descs_dumped &&
|
2016-06-25 07:20:46 +02:00
|
|
|
smartlist_len(descs_dumped) > 0) {
|
|
|
|
/* Get the oldest thing on the list */
|
|
|
|
tmp = (dumped_desc_t *)(smartlist_get(descs_dumped, 0));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if it matches the filename we just added, so we don't delete
|
|
|
|
* something we just emitted if we get repeated identical descriptors.
|
|
|
|
*/
|
|
|
|
if (strcmp(tmp->filename, filename) != 0) {
|
|
|
|
/* Delete it and adjust the length counter */
|
2016-06-25 08:26:44 +02:00
|
|
|
tor_unlink(tmp->filename);
|
2016-06-25 07:20:46 +02:00
|
|
|
tor_assert(len_descs_dumped >= tmp->len);
|
|
|
|
len_descs_dumped -= tmp->len;
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Deleting old unparseable descriptor dump %s due to "
|
|
|
|
"space limits",
|
|
|
|
tmp->filename);
|
|
|
|
} else {
|
2016-06-18 00:35:58 +02:00
|
|
|
/*
|
2016-06-25 07:20:46 +02:00
|
|
|
* Don't delete, but do adjust the counter since we will bump it
|
|
|
|
* later
|
2016-06-18 00:35:58 +02:00
|
|
|
*/
|
2016-06-25 07:20:46 +02:00
|
|
|
tor_assert(len_descs_dumped >= tmp->len);
|
|
|
|
len_descs_dumped -= tmp->len;
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Replacing old descriptor dump %s with new identical one",
|
|
|
|
tmp->filename);
|
2016-06-18 00:35:58 +02:00
|
|
|
}
|
2016-06-25 07:20:46 +02:00
|
|
|
|
|
|
|
/* Free it and remove it from the list */
|
|
|
|
smartlist_del_keeporder(descs_dumped, 0);
|
|
|
|
tor_free(tmp->filename);
|
|
|
|
tor_free(tmp);
|
2016-06-18 00:35:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Append our entry to the end of the list and bump the counter */
|
|
|
|
smartlist_add(descs_dumped, ent);
|
|
|
|
len_descs_dumped += len;
|
|
|
|
}
|
|
|
|
|
2016-06-25 07:20:46 +02:00
|
|
|
/** Check if we already have a descriptor for this hash and move it to the
|
|
|
|
* head of the queue if so. Return 1 if one existed and 0 otherwise.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dump_desc_fifo_bump_hash(const uint8_t *digest_sha256)
|
|
|
|
{
|
|
|
|
dumped_desc_t *match = NULL;
|
|
|
|
|
|
|
|
tor_assert(digest_sha256);
|
|
|
|
|
|
|
|
if (descs_dumped) {
|
|
|
|
/* Find a match if one exists */
|
|
|
|
SMARTLIST_FOREACH_BEGIN(descs_dumped, dumped_desc_t *, ent) {
|
|
|
|
if (ent &&
|
2016-06-30 21:34:16 +02:00
|
|
|
tor_memeq(ent->digest_sha256, digest_sha256, DIGEST256_LEN)) {
|
2016-06-25 07:20:46 +02:00
|
|
|
/*
|
|
|
|
* Save a pointer to the match and remove it from its current
|
|
|
|
* position.
|
|
|
|
*/
|
|
|
|
match = ent;
|
|
|
|
SMARTLIST_DEL_CURRENT_KEEPORDER(descs_dumped, ent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(ent);
|
|
|
|
|
|
|
|
if (match) {
|
2016-06-30 06:36:23 +02:00
|
|
|
/* Update the timestamp */
|
|
|
|
match->when = time(NULL);
|
2016-06-25 07:20:46 +02:00
|
|
|
/* Add it back at the end of the list */
|
|
|
|
smartlist_add(descs_dumped, match);
|
|
|
|
|
|
|
|
/* Indicate we found one */
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-18 00:35:58 +02:00
|
|
|
/** Clean up on exit; just memory, leave the dumps behind
|
|
|
|
*/
|
2016-06-25 09:47:53 +02:00
|
|
|
STATIC void
|
2016-06-18 00:35:58 +02:00
|
|
|
dump_desc_fifo_cleanup(void)
|
|
|
|
{
|
|
|
|
if (descs_dumped) {
|
|
|
|
/* Free each descriptor */
|
|
|
|
SMARTLIST_FOREACH_BEGIN(descs_dumped, dumped_desc_t *, ent) {
|
|
|
|
tor_assert(ent);
|
|
|
|
tor_free(ent->filename);
|
|
|
|
tor_free(ent);
|
|
|
|
} SMARTLIST_FOREACH_END(ent);
|
|
|
|
/* Free the list */
|
|
|
|
smartlist_free(descs_dumped);
|
|
|
|
descs_dumped = NULL;
|
|
|
|
len_descs_dumped = 0;
|
|
|
|
}
|
|
|
|
}
|
2009-08-20 18:45:03 +02:00
|
|
|
|
2016-06-30 06:36:23 +02:00
|
|
|
/** Handle one file for dump_desc_populate_fifo_from_directory(); make sure
|
|
|
|
* the filename is sensibly formed and matches the file content, and either
|
|
|
|
* return a dumped_desc_t for it or remove the file and return NULL.
|
|
|
|
*/
|
2016-06-30 08:27:14 +02:00
|
|
|
MOCK_IMPL(STATIC dumped_desc_t *,
|
|
|
|
dump_desc_populate_one_file, (const char *dirname, const char *f))
|
2016-06-30 06:36:23 +02:00
|
|
|
{
|
|
|
|
dumped_desc_t *ent = NULL;
|
|
|
|
char *path = NULL, *desc = NULL;
|
|
|
|
const char *digest_str;
|
|
|
|
char digest[DIGEST256_LEN], content_digest[DIGEST256_LEN];
|
|
|
|
/* Expected prefix before digest in filenames */
|
|
|
|
const char *f_pfx = DESC_DUMP_BASE_FILENAME ".";
|
|
|
|
/*
|
|
|
|
* Stat while reading; this is important in case the file
|
|
|
|
* contains a NUL character.
|
|
|
|
*/
|
|
|
|
struct stat st;
|
|
|
|
|
|
|
|
/* Sanity-check args */
|
|
|
|
tor_assert(dirname != NULL);
|
|
|
|
tor_assert(f != NULL);
|
|
|
|
|
|
|
|
/* Form the full path */
|
|
|
|
tor_asprintf(&path, "%s" PATH_SEPARATOR "%s", dirname, f);
|
|
|
|
|
|
|
|
/* Check that f has the form DESC_DUMP_BASE_FILENAME.<digest256> */
|
|
|
|
|
|
|
|
if (!strcmpstart(f, f_pfx)) {
|
|
|
|
/* It matches the form, but is the digest parseable as such? */
|
|
|
|
digest_str = f + strlen(f_pfx);
|
|
|
|
if (base16_decode(digest, DIGEST256_LEN,
|
|
|
|
digest_str, strlen(digest_str)) != DIGEST256_LEN) {
|
|
|
|
/* We failed to decode it */
|
|
|
|
digest_str = NULL;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* No match */
|
|
|
|
digest_str = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!digest_str) {
|
|
|
|
/* We couldn't get a sensible digest */
|
|
|
|
log_notice(LD_DIR,
|
|
|
|
"Removing unrecognized filename %s from unparseable "
|
|
|
|
"descriptors directory", f);
|
|
|
|
tor_unlink(path);
|
|
|
|
/* We're done */
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The filename has the form DESC_DUMP_BASE_FILENAME "." <digest256> and
|
|
|
|
* we've decoded the digest. Next, check that we can read it and the
|
|
|
|
* content matches this digest. We are relying on the fact that if the
|
|
|
|
* file contains a '\0', read_file_to_str() will allocate space for and
|
|
|
|
* read the entire file and return the correct size in st.
|
|
|
|
*/
|
2016-07-01 00:23:58 +02:00
|
|
|
desc = read_file_to_str(path, RFTS_IGNORE_MISSING|RFTS_BIN, &st);
|
2016-06-30 06:36:23 +02:00
|
|
|
if (!desc) {
|
|
|
|
/* We couldn't read it */
|
|
|
|
log_notice(LD_DIR,
|
|
|
|
"Failed to read %s from unparseable descriptors directory; "
|
|
|
|
"attempting to remove it.", f);
|
|
|
|
tor_unlink(path);
|
|
|
|
/* We're done */
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2016-06-30 20:20:04 +02:00
|
|
|
#if SIZE_MAX > UINT64_MAX
|
|
|
|
if (BUG((uint64_t)st.st_size > (uint64_t)SIZE_MAX)) {
|
|
|
|
/* LCOV_EXCL_START
|
|
|
|
* Should be impossible since RFTS above should have failed to read the
|
|
|
|
* huge file into RAM. */
|
|
|
|
goto done;
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
}
|
2017-09-15 22:24:44 +02:00
|
|
|
#endif /* SIZE_MAX > UINT64_MAX */
|
2016-06-30 20:20:04 +02:00
|
|
|
if (BUG(st.st_size < 0)) {
|
|
|
|
/* LCOV_EXCL_START
|
|
|
|
* Should be impossible, since the OS isn't supposed to be b0rken. */
|
|
|
|
goto done;
|
|
|
|
/* LCOV_EXCL_STOP */
|
|
|
|
}
|
|
|
|
/* (Now we can be sure that st.st_size is safe to cast to a size_t.) */
|
|
|
|
|
2016-06-30 06:36:23 +02:00
|
|
|
/*
|
|
|
|
* We got one; now compute its digest and check that it matches the
|
|
|
|
* filename.
|
|
|
|
*/
|
2016-06-30 20:20:04 +02:00
|
|
|
if (crypto_digest256((char *)content_digest, desc, (size_t) st.st_size,
|
2016-12-05 16:31:31 +01:00
|
|
|
DIGEST_SHA256) < 0) {
|
2016-06-30 06:36:23 +02:00
|
|
|
/* Weird, but okay */
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Unable to hash content of %s from unparseable descriptors "
|
|
|
|
"directory", f);
|
|
|
|
tor_unlink(path);
|
|
|
|
/* We're done */
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare the digests */
|
2016-06-30 21:34:16 +02:00
|
|
|
if (tor_memneq(digest, content_digest, DIGEST256_LEN)) {
|
2016-06-30 06:36:23 +02:00
|
|
|
/* No match */
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Hash of %s from unparseable descriptors directory didn't "
|
|
|
|
"match its filename; removing it", f);
|
|
|
|
tor_unlink(path);
|
|
|
|
/* We're done */
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Okay, it's a match, we should prepare ent */
|
|
|
|
ent = tor_malloc_zero(sizeof(dumped_desc_t));
|
|
|
|
ent->filename = path;
|
|
|
|
memcpy(ent->digest_sha256, digest, DIGEST256_LEN);
|
2016-06-30 20:20:04 +02:00
|
|
|
ent->len = (size_t) st.st_size;
|
2016-06-30 06:36:23 +02:00
|
|
|
ent->when = st.st_mtime;
|
|
|
|
/* Null out path so we don't free it out from under ent */
|
|
|
|
path = NULL;
|
|
|
|
|
|
|
|
done:
|
|
|
|
/* Free allocations if we had them */
|
|
|
|
tor_free(desc);
|
|
|
|
tor_free(path);
|
|
|
|
|
|
|
|
return ent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sort helper for dump_desc_populate_fifo_from_directory(); compares
|
|
|
|
* the when field of dumped_desc_ts in a smartlist to put the FIFO in
|
|
|
|
* the correct order after reconstructing it from the directory.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
dump_desc_compare_fifo_entries(const void **a_v, const void **b_v)
|
|
|
|
{
|
|
|
|
const dumped_desc_t **a = (const dumped_desc_t **)a_v;
|
|
|
|
const dumped_desc_t **b = (const dumped_desc_t **)b_v;
|
|
|
|
|
|
|
|
if ((a != NULL) && (*a != NULL)) {
|
|
|
|
if ((b != NULL) && (*b != NULL)) {
|
|
|
|
/* We have sensible dumped_desc_ts to compare */
|
|
|
|
if ((*a)->when < (*b)->when) {
|
|
|
|
return -1;
|
|
|
|
} else if ((*a)->when == (*b)->when) {
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We shouldn't see this, but what the hell, NULLs precede everythin
|
|
|
|
* else
|
|
|
|
*/
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Scan the contents of the directory, and update FIFO/counters; this will
|
|
|
|
* consistency-check descriptor dump filenames against hashes of descriptor
|
|
|
|
* dump file content, and remove any inconsistent/unreadable dumps, and then
|
|
|
|
* reconstruct the dump FIFO as closely as possible for the last time the
|
|
|
|
* tor process shut down. If a previous dump was repeated more than once and
|
|
|
|
* moved ahead in the FIFO, the mtime will not have been updated and the
|
|
|
|
* reconstructed order will be wrong, but will always be a permutation of
|
|
|
|
* the original.
|
|
|
|
*/
|
2016-06-30 08:59:29 +02:00
|
|
|
STATIC void
|
2016-06-30 06:36:23 +02:00
|
|
|
dump_desc_populate_fifo_from_directory(const char *dirname)
|
|
|
|
{
|
|
|
|
smartlist_t *files = NULL;
|
|
|
|
dumped_desc_t *ent = NULL;
|
|
|
|
|
|
|
|
tor_assert(dirname != NULL);
|
|
|
|
|
|
|
|
/* Get a list of files */
|
|
|
|
files = tor_listdir(dirname);
|
|
|
|
if (!files) {
|
|
|
|
log_notice(LD_DIR,
|
|
|
|
"Unable to get contents of unparseable descriptor dump "
|
|
|
|
"directory %s",
|
|
|
|
dirname);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Iterate through the list and decide which files should go in the
|
|
|
|
* FIFO and which should be purged.
|
|
|
|
*/
|
|
|
|
|
|
|
|
SMARTLIST_FOREACH_BEGIN(files, char *, f) {
|
|
|
|
/* Try to get a FIFO entry */
|
|
|
|
ent = dump_desc_populate_one_file(dirname, f);
|
|
|
|
if (ent) {
|
|
|
|
/*
|
|
|
|
* We got one; add it to the FIFO. No need for duplicate checking
|
|
|
|
* here since we just verified the name and digest match.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Make sure we have a list to add it to */
|
|
|
|
if (!descs_dumped) {
|
|
|
|
descs_dumped = smartlist_new();
|
|
|
|
len_descs_dumped = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Add it and adjust the counter */
|
|
|
|
smartlist_add(descs_dumped, ent);
|
|
|
|
len_descs_dumped += ent->len;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* If we didn't, we will have unlinked the file if necessary and
|
|
|
|
* possible, and emitted a log message about it, so just go on to
|
|
|
|
* the next.
|
|
|
|
*/
|
|
|
|
} SMARTLIST_FOREACH_END(f);
|
|
|
|
|
|
|
|
/* Did we get anything? */
|
|
|
|
if (descs_dumped != NULL) {
|
|
|
|
/* Sort the FIFO in order of increasing timestamp */
|
|
|
|
smartlist_sort(descs_dumped, dump_desc_compare_fifo_entries);
|
|
|
|
|
|
|
|
/* Log some stats */
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Reloaded unparseable descriptor dump FIFO with %d dump(s) "
|
|
|
|
"totaling " U64_FORMAT " bytes",
|
|
|
|
smartlist_len(descs_dumped), U64_PRINTF_ARG(len_descs_dumped));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Free the original list */
|
|
|
|
SMARTLIST_FOREACH(files, char *, f, tor_free(f));
|
|
|
|
smartlist_free(files);
|
|
|
|
}
|
|
|
|
|
2009-08-20 18:45:03 +02:00
|
|
|
/** For debugging purposes, dump unparseable descriptor *<b>desc</b> of
|
|
|
|
* type *<b>type</b> to file $DATADIR/unparseable-desc. Do not write more
|
|
|
|
* than one descriptor to disk per minute. If there is already such a
|
|
|
|
* file in the data directory, overwrite it. */
|
2016-12-14 02:22:34 +01:00
|
|
|
MOCK_IMPL(STATIC void,
|
|
|
|
dump_desc,(const char *desc, const char *type))
|
2009-08-20 18:45:03 +02:00
|
|
|
{
|
|
|
|
tor_assert(desc);
|
|
|
|
tor_assert(type);
|
2016-06-18 00:35:58 +02:00
|
|
|
size_t len;
|
|
|
|
/* The SHA256 of the string */
|
|
|
|
uint8_t digest_sha256[DIGEST256_LEN];
|
|
|
|
char digest_sha256_hex[HEX_DIGEST256_LEN+1];
|
|
|
|
/* Filename to log it to */
|
|
|
|
char *debugfile, *debugfile_base;
|
|
|
|
|
|
|
|
/* Get the hash for logging purposes anyway */
|
|
|
|
len = strlen(desc);
|
|
|
|
if (crypto_digest256((char *)digest_sha256, desc, len,
|
2016-12-05 16:31:31 +01:00
|
|
|
DIGEST_SHA256) < 0) {
|
2016-06-18 00:35:58 +02:00
|
|
|
log_info(LD_DIR,
|
|
|
|
"Unable to parse descriptor of type %s, and unable to even hash"
|
|
|
|
" it!", type);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
base16_encode(digest_sha256_hex, sizeof(digest_sha256_hex),
|
|
|
|
(const char *)digest_sha256, sizeof(digest_sha256));
|
|
|
|
|
2016-06-25 07:20:46 +02:00
|
|
|
/*
|
|
|
|
* We mention type and hash in the main log; don't clutter up the files
|
|
|
|
* with anything but the exact dump.
|
|
|
|
*/
|
2016-06-30 06:36:23 +02:00
|
|
|
tor_asprintf(&debugfile_base,
|
|
|
|
DESC_DUMP_BASE_FILENAME ".%s", digest_sha256_hex);
|
2016-06-30 02:39:29 +02:00
|
|
|
debugfile = get_datadir_fname2(DESC_DUMP_DATADIR_SUBDIR, debugfile_base);
|
2016-06-25 07:20:46 +02:00
|
|
|
|
2016-06-30 11:37:23 +02:00
|
|
|
/*
|
|
|
|
* Check if the sandbox is active or will become active; see comment
|
|
|
|
* below at the log message for why.
|
|
|
|
*/
|
|
|
|
if (!(sandbox_is_active() || get_options()->Sandbox)) {
|
2016-06-30 00:47:41 +02:00
|
|
|
if (len <= get_options()->MaxUnparseableDescSizeToLog) {
|
|
|
|
if (!dump_desc_fifo_bump_hash(digest_sha256)) {
|
2016-06-30 02:39:29 +02:00
|
|
|
/* Create the directory if needed */
|
|
|
|
dump_desc_create_dir();
|
|
|
|
/* Make sure we've got it */
|
|
|
|
if (have_dump_desc_dir && !problem_with_dump_desc_dir) {
|
|
|
|
/* Write it, and tell the main log about it */
|
|
|
|
write_str_to_file(debugfile, desc, 1);
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Unable to parse descriptor of type %s with hash %s and "
|
|
|
|
"length %lu. See file %s in data directory for details.",
|
|
|
|
type, digest_sha256_hex, (unsigned long)len,
|
|
|
|
debugfile_base);
|
|
|
|
dump_desc_fifo_add_and_clean(debugfile, digest_sha256, len);
|
|
|
|
/* Since we handed ownership over, don't free debugfile later */
|
|
|
|
debugfile = NULL;
|
|
|
|
} else {
|
|
|
|
/* Problem with the subdirectory */
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Unable to parse descriptor of type %s with hash %s and "
|
|
|
|
"length %lu. Descriptor not dumped because we had a "
|
|
|
|
"problem creating the " DESC_DUMP_DATADIR_SUBDIR
|
|
|
|
" subdirectory",
|
|
|
|
type, digest_sha256_hex, (unsigned long)len);
|
|
|
|
/* We do have to free debugfile in this case */
|
|
|
|
}
|
2016-06-30 00:47:41 +02:00
|
|
|
} else {
|
|
|
|
/* We already had one with this hash dumped */
|
|
|
|
log_info(LD_DIR,
|
|
|
|
"Unable to parse descriptor of type %s with hash %s and "
|
|
|
|
"length %lu. Descriptor not dumped because one with that "
|
|
|
|
"hash has already been dumped.",
|
|
|
|
type, digest_sha256_hex, (unsigned long)len);
|
|
|
|
/* We do have to free debugfile in this case */
|
|
|
|
}
|
2016-06-25 07:20:46 +02:00
|
|
|
} else {
|
2016-06-30 00:47:41 +02:00
|
|
|
/* Just log that it happened without dumping */
|
2016-06-25 07:20:46 +02:00
|
|
|
log_info(LD_DIR,
|
|
|
|
"Unable to parse descriptor of type %s with hash %s and "
|
2016-06-30 00:47:41 +02:00
|
|
|
"length %lu. Descriptor not dumped because it exceeds maximum"
|
|
|
|
" log size all by itself.",
|
2016-06-25 07:20:46 +02:00
|
|
|
type, digest_sha256_hex, (unsigned long)len);
|
|
|
|
/* We do have to free debugfile in this case */
|
2016-06-18 00:35:58 +02:00
|
|
|
}
|
|
|
|
} else {
|
2016-06-30 00:47:41 +02:00
|
|
|
/*
|
|
|
|
* Not logging because the sandbox is active and seccomp2 apparently
|
|
|
|
* doesn't have a sensible way to allow filenames according to a pattern
|
|
|
|
* match. (If we ever figure out how to say "allow writes to /regex/",
|
|
|
|
* remove this checK).
|
|
|
|
*/
|
2016-06-18 00:35:58 +02:00
|
|
|
log_info(LD_DIR,
|
2016-06-30 00:47:41 +02:00
|
|
|
"Unable to parse descriptor of type %s with hash %s and "
|
|
|
|
"length %lu. Descriptor not dumped because the sandbox is "
|
2016-06-30 11:37:23 +02:00
|
|
|
"configured",
|
2016-06-25 07:20:46 +02:00
|
|
|
type, digest_sha256_hex, (unsigned long)len);
|
2009-08-20 18:45:03 +02:00
|
|
|
}
|
2016-06-18 00:35:58 +02:00
|
|
|
|
2016-06-25 07:20:46 +02:00
|
|
|
tor_free(debugfile_base);
|
|
|
|
tor_free(debugfile);
|
2016-06-18 00:35:58 +02:00
|
|
|
|
|
|
|
err:
|
|
|
|
return;
|
2009-08-20 18:45:03 +02:00
|
|
|
}
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
/** Set <b>digest</b> to the SHA-1 digest of the hash of the directory in
|
2005-09-13 23:14:55 +02:00
|
|
|
* <b>s</b>. Return 0 on success, -1 on failure.
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
router_get_dir_hash(const char *s, char *digest)
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
2010-02-25 10:31:36 +01:00
|
|
|
return router_get_hash_impl(s, strlen(s), digest,
|
2009-08-24 18:51:33 +02:00
|
|
|
"signed-directory","\ndirectory-signature",'\n',
|
|
|
|
DIGEST_SHA1);
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Set <b>digest</b> to the SHA-1 digest of the hash of the first router in
|
2005-09-13 23:14:55 +02:00
|
|
|
* <b>s</b>. Return 0 on success, -1 on failure.
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2010-02-25 10:31:36 +01:00
|
|
|
router_get_router_hash(const char *s, size_t s_len, char *digest)
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
2010-02-25 10:31:36 +01:00
|
|
|
return router_get_hash_impl(s, s_len, digest,
|
2009-08-24 18:51:33 +02:00
|
|
|
"router ","\nrouter-signature", '\n',
|
|
|
|
DIGEST_SHA1);
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
2017-05-03 17:48:08 +02:00
|
|
|
/** Try to find the start and end of the signed portion of a networkstatus
|
|
|
|
* document in <b>s</b>. On success, set <b>start_out</b> to the first
|
|
|
|
* character of the document, and <b>end_out</b> to a position one after the
|
|
|
|
* final character of the signed document, and return 0. On failure, return
|
|
|
|
* -1. */
|
|
|
|
int
|
|
|
|
router_get_networkstatus_v3_signed_boundaries(const char *s,
|
|
|
|
const char **start_out,
|
|
|
|
const char **end_out)
|
|
|
|
{
|
|
|
|
return router_get_hash_impl_helper(s, strlen(s),
|
|
|
|
"network-status-version",
|
|
|
|
"\ndirectory-signature",
|
|
|
|
' ', LOG_INFO,
|
|
|
|
start_out, end_out);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Set <b>digest_out</b> to the SHA3-256 digest of the signed portion of the
|
|
|
|
* networkstatus vote in <b>s</b> -- or of the entirety of <b>s</b> if no
|
|
|
|
* signed portion can be identified. Return 0 on success, -1 on failure. */
|
|
|
|
int
|
|
|
|
router_get_networkstatus_v3_sha3_as_signed(uint8_t *digest_out,
|
|
|
|
const char *s)
|
|
|
|
{
|
|
|
|
const char *start, *end;
|
|
|
|
if (router_get_networkstatus_v3_signed_boundaries(s, &start, &end) < 0) {
|
|
|
|
start = s;
|
|
|
|
end = s + strlen(s);
|
|
|
|
}
|
|
|
|
tor_assert(start);
|
|
|
|
tor_assert(end);
|
|
|
|
return crypto_digest256((char*)digest_out, start, end-start,
|
|
|
|
DIGEST_SHA3_256);
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Set <b>digests</b> to all the digests of the consensus document in
|
|
|
|
* <b>s</b> */
|
2009-09-16 23:01:01 +02:00
|
|
|
int
|
2016-02-10 21:28:19 +01:00
|
|
|
router_get_networkstatus_v3_hashes(const char *s, common_digests_t *digests)
|
2009-09-16 23:01:01 +02:00
|
|
|
{
|
2010-02-27 21:34:02 +01:00
|
|
|
return router_get_hashes_impl(s,strlen(s),digests,
|
2009-09-16 23:01:01 +02:00
|
|
|
"network-status-version",
|
|
|
|
"\ndirectory-signature",
|
|
|
|
' ');
|
|
|
|
}
|
|
|
|
|
2012-05-10 23:27:16 +02:00
|
|
|
/** Set <b>digest</b> to the SHA-1 digest of the hash of the <b>s_len</b>-byte
|
|
|
|
* extrainfo string at <b>s</b>. Return 0 on success, -1 on failure. */
|
2007-04-16 06:17:58 +02:00
|
|
|
int
|
2012-05-10 23:27:16 +02:00
|
|
|
router_get_extrainfo_hash(const char *s, size_t s_len, char *digest)
|
2007-04-16 06:17:58 +02:00
|
|
|
{
|
2012-05-10 23:27:16 +02:00
|
|
|
return router_get_hash_impl(s, s_len, digest, "extra-info",
|
2010-02-27 21:34:02 +01:00
|
|
|
"\nrouter-signature",'\n', DIGEST_SHA1);
|
2007-04-16 06:17:58 +02:00
|
|
|
}
|
|
|
|
|
2005-08-26 17:34:53 +02:00
|
|
|
/** Helper: used to generate signatures for routers, directories and
|
2013-03-18 20:00:52 +01:00
|
|
|
* network-status objects. Given a <b>digest_len</b>-byte digest in
|
|
|
|
* <b>digest</b> and a secret <b>private_key</b>, generate an PKCS1-padded
|
|
|
|
* signature, BASE64-encode it, surround it with -----BEGIN/END----- pairs,
|
|
|
|
* and return the new signature on success or NULL on failure.
|
2005-08-26 17:34:53 +02:00
|
|
|
*/
|
2013-02-22 18:53:45 +01:00
|
|
|
char *
|
|
|
|
router_get_dirobj_signature(const char *digest,
|
|
|
|
size_t digest_len,
|
2014-10-01 17:54:07 +02:00
|
|
|
const crypto_pk_t *private_key)
|
2005-08-26 17:34:53 +02:00
|
|
|
{
|
2007-05-02 23:37:55 +02:00
|
|
|
char *signature;
|
2011-01-13 20:36:41 +01:00
|
|
|
size_t i, keysize;
|
2009-09-14 17:57:19 +02:00
|
|
|
int siglen;
|
2013-02-22 18:53:45 +01:00
|
|
|
char *buf = NULL;
|
|
|
|
size_t buf_len;
|
|
|
|
/* overestimate of BEGIN/END lines total len. */
|
|
|
|
#define BEGIN_END_OVERHEAD_LEN 64
|
2005-08-26 17:34:53 +02:00
|
|
|
|
2011-01-13 20:36:41 +01:00
|
|
|
keysize = crypto_pk_keysize(private_key);
|
|
|
|
signature = tor_malloc(keysize);
|
2011-01-15 18:02:55 +01:00
|
|
|
siglen = crypto_pk_private_sign(private_key, signature, keysize,
|
|
|
|
digest, digest_len);
|
2009-09-14 17:57:19 +02:00
|
|
|
if (siglen < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"Couldn't sign digest.");
|
2007-05-02 23:37:55 +02:00
|
|
|
goto err;
|
2005-08-26 17:34:53 +02:00
|
|
|
}
|
2013-02-22 18:53:45 +01:00
|
|
|
|
|
|
|
/* The *2 here is a ridiculous overestimate of base-64 overhead. */
|
|
|
|
buf_len = (siglen * 2) + BEGIN_END_OVERHEAD_LEN;
|
|
|
|
buf = tor_malloc(buf_len);
|
|
|
|
|
|
|
|
if (strlcpy(buf, "-----BEGIN SIGNATURE-----\n", buf_len) >= buf_len)
|
2005-08-26 17:34:53 +02:00
|
|
|
goto truncated;
|
|
|
|
|
|
|
|
i = strlen(buf);
|
2015-04-10 13:25:08 +02:00
|
|
|
if (base64_encode(buf+i, buf_len-i, signature, siglen,
|
|
|
|
BASE64_ENCODE_MULTILINE) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"couldn't base64-encode signature");
|
2007-05-02 23:37:55 +02:00
|
|
|
goto err;
|
2005-08-26 17:34:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (strlcat(buf, "-----END SIGNATURE-----\n", buf_len) >= buf_len)
|
|
|
|
goto truncated;
|
|
|
|
|
2007-05-02 23:37:55 +02:00
|
|
|
tor_free(signature);
|
2013-02-22 18:53:45 +01:00
|
|
|
return buf;
|
2007-05-02 23:37:55 +02:00
|
|
|
|
2005-08-26 17:34:53 +02:00
|
|
|
truncated:
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_BUG,"tried to exceed string length.");
|
2007-05-02 23:37:55 +02:00
|
|
|
err:
|
|
|
|
tor_free(signature);
|
2013-02-22 18:53:45 +01:00
|
|
|
tor_free(buf);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Helper: used to generate signatures for routers, directories and
|
|
|
|
* network-status objects. Given a digest in <b>digest</b> and a secret
|
2014-03-23 05:42:18 +01:00
|
|
|
* <b>private_key</b>, generate a PKCS1-padded signature, BASE64-encode it,
|
2013-02-22 18:53:45 +01:00
|
|
|
* surround it with -----BEGIN/END----- pairs, and write it to the
|
|
|
|
* <b>buf_len</b>-byte buffer at <b>buf</b>. Return 0 on success, -1 on
|
|
|
|
* failure.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
router_append_dirobj_signature(char *buf, size_t buf_len, const char *digest,
|
|
|
|
size_t digest_len, crypto_pk_t *private_key)
|
|
|
|
{
|
|
|
|
size_t sig_len, s_len;
|
|
|
|
char *sig = router_get_dirobj_signature(digest, digest_len, private_key);
|
|
|
|
if (!sig) {
|
|
|
|
log_warn(LD_BUG, "No signature generated");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
sig_len = strlen(sig);
|
|
|
|
s_len = strlen(buf);
|
|
|
|
if (sig_len + s_len + 1 > buf_len) {
|
|
|
|
log_warn(LD_BUG, "Not enough room for signature");
|
|
|
|
tor_free(sig);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memcpy(buf+s_len, sig, sig_len+1);
|
2014-03-23 05:42:18 +01:00
|
|
|
tor_free(sig);
|
2013-02-22 18:53:45 +01:00
|
|
|
return 0;
|
2005-08-26 17:34:53 +02:00
|
|
|
}
|
|
|
|
|
2005-09-30 22:04:55 +02:00
|
|
|
/** Return VS_RECOMMENDED if <b>myversion</b> is contained in
|
2008-02-06 13:45:04 +01:00
|
|
|
* <b>versionlist</b>. Else, return VS_EMPTY if versionlist has no
|
|
|
|
* entries. Else, return VS_OLD if every member of
|
2005-09-30 22:04:55 +02:00
|
|
|
* <b>versionlist</b> is newer than <b>myversion</b>. Else, return
|
|
|
|
* VS_NEW_IN_SERIES if there is at least one member of <b>versionlist</b> in
|
|
|
|
* the same series (major.minor.micro) as <b>myversion</b>, but no such member
|
2009-05-27 23:55:51 +02:00
|
|
|
* is newer than <b>myversion.</b>. Else, return VS_NEW if every member of
|
2005-09-30 22:04:55 +02:00
|
|
|
* <b>versionlist</b> is older than <b>myversion</b>. Else, return
|
|
|
|
* VS_UNRECOMMENDED.
|
2005-03-01 02:44:57 +01:00
|
|
|
*
|
2004-07-17 01:33:42 +02:00
|
|
|
* (versionlist is a comma-separated list of version strings,
|
|
|
|
* optionally prefixed with "Tor". Versions that can't be parsed are
|
2005-09-21 02:41:06 +02:00
|
|
|
* ignored.)
|
2005-09-30 22:04:55 +02:00
|
|
|
*/
|
2005-09-21 02:41:06 +02:00
|
|
|
version_status_t
|
|
|
|
tor_version_is_obsolete(const char *myversion, const char *versionlist)
|
|
|
|
{
|
2004-07-17 01:33:42 +02:00
|
|
|
tor_version_t mine, other;
|
2005-09-21 02:41:06 +02:00
|
|
|
int found_newer = 0, found_older = 0, found_newer_in_series = 0,
|
|
|
|
found_any_in_series = 0, r, same;
|
|
|
|
version_status_t ret = VS_UNRECOMMENDED;
|
2004-09-02 20:39:59 +02:00
|
|
|
smartlist_t *version_sl;
|
2004-06-01 20:19:01 +02:00
|
|
|
|
2006-02-13 11:33:00 +01:00
|
|
|
log_debug(LD_CONFIG,"Checking whether version '%s' is in '%s'",
|
|
|
|
myversion, versionlist);
|
2004-06-01 20:19:01 +02:00
|
|
|
|
2004-07-17 01:33:42 +02:00
|
|
|
if (tor_version_parse(myversion, &mine)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_err(LD_BUG,"I couldn't parse my own version (%s)", myversion);
|
2004-07-17 01:33:42 +02:00
|
|
|
tor_assert(0);
|
|
|
|
}
|
2012-01-18 21:53:30 +01:00
|
|
|
version_sl = smartlist_new();
|
2004-09-02 20:39:59 +02:00
|
|
|
smartlist_split_string(version_sl, versionlist, ",", SPLIT_SKIP_SPACE, 0);
|
2004-07-17 01:33:42 +02:00
|
|
|
|
2008-02-06 13:45:04 +01:00
|
|
|
if (!strlen(versionlist)) { /* no authorities cared or agreed */
|
|
|
|
ret = VS_EMPTY;
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
2012-07-17 15:33:38 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(version_sl, const char *, cp) {
|
2004-09-02 20:39:59 +02:00
|
|
|
if (!strcmpstart(cp, "Tor "))
|
2004-07-17 01:33:42 +02:00
|
|
|
cp += 4;
|
|
|
|
|
|
|
|
if (tor_version_parse(cp, &other)) {
|
|
|
|
/* Couldn't parse other; it can't be a match. */
|
|
|
|
} else {
|
2005-03-01 02:44:57 +01:00
|
|
|
same = tor_version_same_series(&mine, &other);
|
|
|
|
if (same)
|
|
|
|
found_any_in_series = 1;
|
2004-07-17 01:33:42 +02:00
|
|
|
r = tor_version_compare(&mine, &other);
|
|
|
|
if (r==0) {
|
2005-09-21 02:41:06 +02:00
|
|
|
ret = VS_RECOMMENDED;
|
2004-09-02 20:39:59 +02:00
|
|
|
goto done;
|
2004-07-17 01:33:42 +02:00
|
|
|
} else if (r<0) {
|
|
|
|
found_newer = 1;
|
2005-03-01 02:44:57 +01:00
|
|
|
if (same)
|
|
|
|
found_newer_in_series = 1;
|
2005-09-21 02:41:06 +02:00
|
|
|
} else if (r>0) {
|
|
|
|
found_older = 1;
|
2004-07-17 01:33:42 +02:00
|
|
|
}
|
|
|
|
}
|
2012-07-17 15:33:38 +02:00
|
|
|
} SMARTLIST_FOREACH_END(cp);
|
2004-09-02 20:39:59 +02:00
|
|
|
|
2005-03-01 02:44:57 +01:00
|
|
|
/* We didn't find the listed version. Is it new or old? */
|
2007-02-08 08:24:06 +01:00
|
|
|
if (found_any_in_series && !found_newer_in_series && found_newer) {
|
2005-09-21 02:41:06 +02:00
|
|
|
ret = VS_NEW_IN_SERIES;
|
|
|
|
} else if (found_newer && !found_older) {
|
|
|
|
ret = VS_OLD;
|
|
|
|
} else if (found_older && !found_newer) {
|
|
|
|
ret = VS_NEW;
|
2004-07-17 01:33:42 +02:00
|
|
|
} else {
|
2005-09-21 02:41:06 +02:00
|
|
|
ret = VS_UNRECOMMENDED;
|
2004-06-01 20:19:01 +02:00
|
|
|
}
|
2004-09-02 20:39:59 +02:00
|
|
|
|
|
|
|
done:
|
|
|
|
SMARTLIST_FOREACH(version_sl, char *, version, tor_free(version));
|
|
|
|
smartlist_free(version_sl);
|
|
|
|
return ret;
|
2004-06-01 20:19:01 +02:00
|
|
|
}
|
|
|
|
|
2016-12-19 21:11:27 +01:00
|
|
|
MOCK_IMPL(STATIC int,
|
|
|
|
signed_digest_equals, (const uint8_t *d1, const uint8_t *d2, size_t len))
|
|
|
|
{
|
|
|
|
return tor_memeq(d1, d2, len);
|
|
|
|
}
|
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/** Check whether the object body of the token in <b>tok</b> has a good
|
2016-12-13 15:36:59 +01:00
|
|
|
* signature for <b>digest</b> using key <b>pkey</b>.
|
|
|
|
* If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check
|
2008-12-12 19:31:39 +01:00
|
|
|
* the object type of the signature object. Use <b>doctype</b> as the type of
|
|
|
|
* the document when generating log messages. Return 0 on success, negative
|
|
|
|
* on failure.
|
2004-10-07 23:10:40 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static int
|
2007-05-15 00:51:05 +02:00
|
|
|
check_signature_token(const char *digest,
|
2009-09-14 18:02:00 +02:00
|
|
|
ssize_t digest_len,
|
2007-05-15 00:51:05 +02:00
|
|
|
directory_token_t *tok,
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_pk_t *pkey,
|
2008-12-12 19:31:39 +01:00
|
|
|
int flags,
|
2007-05-15 00:51:05 +02:00
|
|
|
const char *doctype)
|
2004-06-25 02:29:31 +02:00
|
|
|
{
|
2007-05-02 23:37:55 +02:00
|
|
|
char *signed_digest;
|
2011-01-13 20:36:41 +01:00
|
|
|
size_t keysize;
|
2008-12-12 19:31:39 +01:00
|
|
|
const int check_objtype = ! (flags & CST_NO_CHECK_OBJTYPE);
|
2004-08-07 03:48:50 +02:00
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
tor_assert(pkey);
|
|
|
|
tor_assert(tok);
|
|
|
|
tor_assert(digest);
|
|
|
|
tor_assert(doctype);
|
|
|
|
|
2008-12-12 19:31:39 +01:00
|
|
|
if (check_objtype) {
|
|
|
|
if (strcmp(tok->object_type, "SIGNATURE")) {
|
|
|
|
log_warn(LD_DIR, "Bad object type on %s signature", doctype);
|
|
|
|
return -1;
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2004-08-07 03:48:50 +02:00
|
|
|
|
2011-01-13 20:36:41 +01:00
|
|
|
keysize = crypto_pk_keysize(pkey);
|
|
|
|
signed_digest = tor_malloc(keysize);
|
|
|
|
if (crypto_pk_public_checksig(pkey, signed_digest, keysize,
|
|
|
|
tok->object_body, tok->object_size)
|
2011-01-25 23:15:22 +01:00
|
|
|
< digest_len) {
|
2007-05-15 00:51:05 +02:00
|
|
|
log_warn(LD_DIR, "Error reading %s: invalid signature.", doctype);
|
2007-05-27 06:38:50 +02:00
|
|
|
tor_free(signed_digest);
|
2004-08-07 03:48:50 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2014-10-01 17:54:07 +02:00
|
|
|
// log_debug(LD_DIR,"Signed %s hash starts %s", doctype,
|
|
|
|
// hex_str(signed_digest,4));
|
2016-12-19 21:11:27 +01:00
|
|
|
if (! signed_digest_equals((const uint8_t *)digest,
|
|
|
|
(const uint8_t *)signed_digest, digest_len)) {
|
2007-05-15 00:51:05 +02:00
|
|
|
log_warn(LD_DIR, "Error reading %s: signature does not match.", doctype);
|
2007-05-27 06:38:50 +02:00
|
|
|
tor_free(signed_digest);
|
2004-08-07 03:48:50 +02:00
|
|
|
return -1;
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2007-05-27 06:38:50 +02:00
|
|
|
tor_free(signed_digest);
|
2004-06-25 02:29:31 +02:00
|
|
|
return 0;
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
2007-10-04 18:21:58 +02:00
|
|
|
/** Helper: move *<b>s_ptr</b> ahead to the next router, the next extra-info,
|
|
|
|
* or to the first of the annotations proceeding the next router or
|
|
|
|
* extra-info---whichever comes first. Set <b>is_extrainfo_out</b> to true if
|
|
|
|
* we found an extrainfo, or false if found a router. Do not scan beyond
|
|
|
|
* <b>eos</b>. Return -1 if we found nothing; 0 if we found something. */
|
2007-10-02 03:22:42 +02:00
|
|
|
static int
|
|
|
|
find_start_of_next_router_or_extrainfo(const char **s_ptr,
|
|
|
|
const char *eos,
|
|
|
|
int *is_extrainfo_out)
|
|
|
|
{
|
|
|
|
const char *annotations = NULL;
|
|
|
|
const char *s = *s_ptr;
|
|
|
|
|
|
|
|
s = eat_whitespace_eos(s, eos);
|
|
|
|
|
|
|
|
while (s < eos-32) { /* 32 gives enough room for a the first keyword. */
|
|
|
|
/* We're at the start of a line. */
|
|
|
|
tor_assert(*s != '\n');
|
|
|
|
|
|
|
|
if (*s == '@' && !annotations) {
|
|
|
|
annotations = s;
|
|
|
|
} else if (*s == 'r' && !strcmpstart(s, "router ")) {
|
|
|
|
*s_ptr = annotations ? annotations : s;
|
|
|
|
*is_extrainfo_out = 0;
|
|
|
|
return 0;
|
|
|
|
} else if (*s == 'e' && !strcmpstart(s, "extra-info ")) {
|
|
|
|
*s_ptr = annotations ? annotations : s;
|
|
|
|
*is_extrainfo_out = 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(s = memchr(s+1, '\n', eos-(s+1))))
|
|
|
|
break;
|
|
|
|
s = eat_whitespace_eos(s, eos);
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2004-07-17 01:05:40 +02:00
|
|
|
/** Given a string *<b>s</b> containing a concatenated sequence of router
|
2007-05-15 00:51:05 +02:00
|
|
|
* descriptors (or extra-info documents if <b>is_extrainfo</b> is set), parses
|
|
|
|
* them and stores the result in <b>dest</b>. All routers are marked running
|
|
|
|
* and valid. Advances *s to a point immediately following the last router
|
|
|
|
* entry. Ignore any trailing router entries that are not complete.
|
2007-01-22 08:51:06 +01:00
|
|
|
*
|
|
|
|
* If <b>saved_location</b> isn't SAVED_IN_CACHE, make a local copy of each
|
|
|
|
* descriptor in the signed_descriptor_body field of each routerinfo_t. If it
|
|
|
|
* isn't SAVED_NOWHERE, remember the offset of each descriptor.
|
|
|
|
*
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
* Returns 0 on success and -1 on failure. Adds a digest to
|
2014-10-13 20:15:56 +02:00
|
|
|
* <b>invalid_digests_out</b> for every entry that was unparseable or
|
|
|
|
* invalid. (This may cause duplicate entries.)
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
|
|
|
int
|
2007-05-22 04:20:52 +02:00
|
|
|
router_parse_list_from_string(const char **s, const char *eos,
|
|
|
|
smartlist_t *dest,
|
2007-04-30 21:48:39 +02:00
|
|
|
saved_location_t saved_location,
|
2007-09-26 18:19:44 +02:00
|
|
|
int want_extrainfo,
|
2007-09-27 18:08:10 +02:00
|
|
|
int allow_annotations,
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
const char *prepend_annotations,
|
|
|
|
smartlist_t *invalid_digests_out)
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
|
|
|
routerinfo_t *router;
|
2007-04-30 21:48:39 +02:00
|
|
|
extrainfo_t *extrainfo;
|
2014-09-02 17:55:53 +02:00
|
|
|
signed_descriptor_t *signed_desc = NULL;
|
2007-04-30 21:48:39 +02:00
|
|
|
void *elt;
|
2007-05-15 09:13:56 +02:00
|
|
|
const char *end, *start;
|
|
|
|
int have_extrainfo;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(s);
|
|
|
|
tor_assert(*s);
|
2005-09-30 23:22:25 +02:00
|
|
|
tor_assert(dest);
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2006-04-29 20:42:26 +02:00
|
|
|
start = *s;
|
2007-05-22 04:20:52 +02:00
|
|
|
if (!eos)
|
|
|
|
eos = *s + strlen(*s);
|
|
|
|
|
|
|
|
tor_assert(eos >= *s);
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
while (1) {
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
char raw_digest[DIGEST_LEN];
|
|
|
|
int have_raw_digest = 0;
|
|
|
|
int dl_again = 0;
|
2007-10-02 03:22:42 +02:00
|
|
|
if (find_start_of_next_router_or_extrainfo(s, eos, &have_extrainfo) < 0)
|
2007-05-22 04:20:52 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
end = tor_memstr(*s, eos-*s, "\nrouter-signature");
|
2007-05-15 09:13:56 +02:00
|
|
|
if (end)
|
2007-07-16 05:39:21 +02:00
|
|
|
end = tor_memstr(end, eos-end, "\n-----END SIGNATURE-----\n");
|
2007-05-15 09:13:56 +02:00
|
|
|
if (end)
|
|
|
|
end += strlen("\n-----END SIGNATURE-----\n");
|
2005-10-14 04:26:13 +02:00
|
|
|
|
2007-05-15 09:13:56 +02:00
|
|
|
if (!end)
|
|
|
|
break;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2007-05-29 17:08:55 +02:00
|
|
|
elt = NULL;
|
|
|
|
|
2007-05-15 09:13:56 +02:00
|
|
|
if (have_extrainfo && want_extrainfo) {
|
2007-04-30 21:48:39 +02:00
|
|
|
routerlist_t *rl = router_get_routerlist();
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
have_raw_digest = router_get_extrainfo_hash(*s, end-*s, raw_digest) == 0;
|
2007-04-30 21:48:39 +02:00
|
|
|
extrainfo = extrainfo_parse_entry_from_string(*s, end,
|
|
|
|
saved_location != SAVED_IN_CACHE,
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
rl->identity_map, &dl_again);
|
2007-05-29 17:08:55 +02:00
|
|
|
if (extrainfo) {
|
|
|
|
signed_desc = &extrainfo->cache_info;
|
|
|
|
elt = extrainfo;
|
|
|
|
}
|
2007-05-15 09:13:56 +02:00
|
|
|
} else if (!have_extrainfo && !want_extrainfo) {
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
have_raw_digest = router_get_router_hash(*s, end-*s, raw_digest) == 0;
|
2007-04-30 21:48:39 +02:00
|
|
|
router = router_parse_entry_from_string(*s, end,
|
2007-09-26 18:19:44 +02:00
|
|
|
saved_location != SAVED_IN_CACHE,
|
2007-09-27 18:08:10 +02:00
|
|
|
allow_annotations,
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
prepend_annotations, &dl_again);
|
2007-05-29 17:08:55 +02:00
|
|
|
if (router) {
|
2007-12-19 04:11:02 +01:00
|
|
|
log_debug(LD_DIR, "Read router '%s', purpose '%s'",
|
2011-05-16 03:58:46 +02:00
|
|
|
router_describe(router),
|
|
|
|
router_purpose_to_string(router->purpose));
|
2007-05-29 17:08:55 +02:00
|
|
|
signed_desc = &router->cache_info;
|
|
|
|
elt = router;
|
|
|
|
}
|
|
|
|
}
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
if (! elt && ! dl_again && have_raw_digest && invalid_digests_out) {
|
|
|
|
smartlist_add(invalid_digests_out, tor_memdup(raw_digest, DIGEST_LEN));
|
|
|
|
}
|
2007-05-29 17:08:55 +02:00
|
|
|
if (!elt) {
|
2006-06-22 09:19:28 +02:00
|
|
|
*s = end;
|
2004-05-10 19:30:51 +02:00
|
|
|
continue;
|
|
|
|
}
|
2006-06-22 09:01:54 +02:00
|
|
|
if (saved_location != SAVED_NOWHERE) {
|
2014-09-02 17:55:53 +02:00
|
|
|
tor_assert(signed_desc);
|
2007-04-30 21:48:39 +02:00
|
|
|
signed_desc->saved_location = saved_location;
|
|
|
|
signed_desc->saved_offset = *s - start;
|
2006-04-29 20:42:26 +02:00
|
|
|
}
|
2006-06-22 09:19:28 +02:00
|
|
|
*s = end;
|
2007-04-30 21:48:39 +02:00
|
|
|
smartlist_add(dest, elt);
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2006-11-30 23:23:01 +01:00
|
|
|
/* For debugging: define to count every descriptor digest we've seen so we
|
|
|
|
* know if we need to try harder to avoid duplicate verifies. */
|
|
|
|
#undef COUNT_DISTINCT_DIGESTS
|
|
|
|
|
|
|
|
#ifdef COUNT_DISTINCT_DIGESTS
|
|
|
|
static digestmap_t *verified_digests = NULL;
|
|
|
|
#endif
|
|
|
|
|
2007-02-16 21:01:02 +01:00
|
|
|
/** Log the total count of the number of distinct router digests we've ever
|
|
|
|
* verified. When compared to the number of times we've verified routerdesc
|
|
|
|
* signatures <i>in toto</i>, this will tell us if we're doing too much
|
|
|
|
* multiple-verification. */
|
2006-11-01 19:16:44 +01:00
|
|
|
void
|
2006-11-30 23:23:01 +01:00
|
|
|
dump_distinct_digest_count(int severity)
|
2006-11-14 01:06:45 +01:00
|
|
|
{
|
2006-11-30 23:23:01 +01:00
|
|
|
#ifdef COUNT_DISTINCT_DIGESTS
|
|
|
|
if (!verified_digests)
|
|
|
|
verified_digests = digestmap_new();
|
2013-02-01 21:43:37 +01:00
|
|
|
tor_log(severity, LD_GENERAL, "%d *distinct* router digests verified",
|
2006-11-30 23:23:01 +01:00
|
|
|
digestmap_size(verified_digests));
|
2017-09-15 22:24:44 +02:00
|
|
|
#else /* !(defined(COUNT_DISTINCT_DIGESTS)) */
|
2006-11-30 23:23:01 +01:00
|
|
|
(void)severity; /* suppress "unused parameter" warning */
|
2017-09-15 22:24:44 +02:00
|
|
|
#endif /* defined(COUNT_DISTINCT_DIGESTS) */
|
2006-11-01 19:16:44 +01:00
|
|
|
}
|
|
|
|
|
2012-08-14 14:03:58 +02:00
|
|
|
/** Try to find an IPv6 OR port in <b>list</b> of directory_token_t's
|
|
|
|
* with at least one argument (use GE(1) in setup). If found, store
|
|
|
|
* address and port number to <b>addr_out</b> and
|
|
|
|
* <b>port_out</b>. Return number of OR ports found. */
|
|
|
|
static int
|
|
|
|
find_single_ipv6_orport(const smartlist_t *list,
|
|
|
|
tor_addr_t *addr_out,
|
|
|
|
uint16_t *port_out)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
tor_assert(list != NULL);
|
|
|
|
tor_assert(addr_out != NULL);
|
|
|
|
tor_assert(port_out != NULL);
|
|
|
|
|
|
|
|
SMARTLIST_FOREACH_BEGIN(list, directory_token_t *, t) {
|
|
|
|
tor_addr_t a;
|
|
|
|
maskbits_t bits;
|
|
|
|
uint16_t port_min, port_max;
|
|
|
|
tor_assert(t->n_args >= 1);
|
|
|
|
/* XXXX Prop186 the full spec allows much more than this. */
|
2012-10-24 18:33:18 +02:00
|
|
|
if (tor_addr_parse_mask_ports(t->args[0], 0,
|
|
|
|
&a, &bits, &port_min,
|
2012-08-14 14:03:58 +02:00
|
|
|
&port_max) == AF_INET6 &&
|
|
|
|
bits == 128 &&
|
|
|
|
port_min == port_max) {
|
|
|
|
/* Okay, this is one we can understand. Use it and ignore
|
|
|
|
any potential more addresses in list. */
|
|
|
|
tor_addr_copy(addr_out, &a);
|
|
|
|
*port_out = port_min;
|
|
|
|
ret = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(t);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
/** Helper function: reads a single router entry from *<b>s</b> ...
|
|
|
|
* *<b>end</b>. Mallocs a new router and returns it if all goes well, else
|
2007-01-22 08:51:06 +01:00
|
|
|
* returns NULL. If <b>cache_copy</b> is true, duplicate the contents of
|
|
|
|
* s through end into the signed_descriptor_body of the resulting
|
|
|
|
* routerinfo_t.
|
2007-10-04 18:21:58 +02:00
|
|
|
*
|
2012-03-30 16:58:32 +02:00
|
|
|
* If <b>end</b> is NULL, <b>s</b> must be properly NUL-terminated.
|
2010-02-25 10:31:36 +01:00
|
|
|
*
|
2007-10-04 18:21:58 +02:00
|
|
|
* If <b>allow_annotations</b>, it's okay to encounter annotations in <b>s</b>
|
|
|
|
* before the router; if it's false, reject the router if it's annotated. If
|
|
|
|
* <b>prepend_annotations</b> is set, it should contain some annotations:
|
|
|
|
* append them to the front of the router before parsing it, and keep them
|
|
|
|
* around when caching the router.
|
|
|
|
*
|
|
|
|
* Only one of allow_annotations and prepend_annotations may be set.
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
*
|
|
|
|
* If <b>can_dl_again_out</b> is provided, set *<b>can_dl_again_out</b> to 1
|
|
|
|
* if it's okay to try to download a descriptor with this same digest again,
|
|
|
|
* and 0 if it isn't. (It might not be okay to download it again if part of
|
|
|
|
* the part covered by the digest is invalid.)
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
routerinfo_t *
|
2006-06-22 09:01:54 +02:00
|
|
|
router_parse_entry_from_string(const char *s, const char *end,
|
2007-09-27 18:08:10 +02:00
|
|
|
int cache_copy, int allow_annotations,
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
const char *prepend_annotations,
|
|
|
|
int *can_dl_again_out)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-05-10 19:30:51 +02:00
|
|
|
routerinfo_t *router = NULL;
|
|
|
|
char digest[128];
|
|
|
|
smartlist_t *tokens = NULL, *exit_policy_tokens = NULL;
|
|
|
|
directory_token_t *tok;
|
2005-08-16 04:52:27 +02:00
|
|
|
struct in_addr in;
|
2009-08-20 18:45:03 +02:00
|
|
|
const char *start_of_annotations, *cp, *s_dup = s;
|
2007-09-27 22:46:30 +02:00
|
|
|
size_t prepend_len = prepend_annotations ? strlen(prepend_annotations) : 0;
|
2008-02-06 01:54:47 +01:00
|
|
|
int ok = 1;
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_t *area = NULL;
|
2014-10-01 17:54:07 +02:00
|
|
|
tor_cert_t *ntor_cc_cert = NULL;
|
2014-10-13 20:22:52 +02:00
|
|
|
/* Do not set this to '1' until we have parsed everything that we intend to
|
|
|
|
* parse that's covered by the hash. */
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
int can_dl_again = 0;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2007-09-27 18:08:10 +02:00
|
|
|
tor_assert(!allow_annotations || !prepend_annotations);
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
if (!end) {
|
|
|
|
end = s + strlen(s);
|
|
|
|
}
|
|
|
|
|
2005-10-07 20:33:30 +02:00
|
|
|
/* point 'end' to a point immediately after the final newline. */
|
|
|
|
while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n')
|
|
|
|
--end;
|
|
|
|
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2012-01-18 21:53:30 +01:00
|
|
|
tokens = smartlist_new();
|
2007-09-27 18:08:10 +02:00
|
|
|
if (prepend_annotations) {
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area,prepend_annotations,NULL,tokens,
|
2007-09-27 18:08:10 +02:00
|
|
|
routerdesc_token_table,TS_NOCHECK)) {
|
2007-12-19 04:11:02 +01:00
|
|
|
log_warn(LD_DIR, "Error tokenizing router descriptor (annotations).");
|
2007-09-27 18:08:10 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-09-26 18:19:44 +02:00
|
|
|
start_of_annotations = s;
|
|
|
|
cp = tor_memstr(s, end-s, "\nrouter ");
|
|
|
|
if (!cp) {
|
|
|
|
if (end-s < 7 || strcmpstart(s, "router ")) {
|
|
|
|
log_warn(LD_DIR, "No router keyword found.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
s = cp+1;
|
|
|
|
}
|
|
|
|
|
2010-11-02 16:45:14 +01:00
|
|
|
if (start_of_annotations != s) { /* We have annotations */
|
|
|
|
if (allow_annotations) {
|
|
|
|
if (tokenize_string(area,start_of_annotations,s,tokens,
|
|
|
|
routerdesc_token_table,TS_NOCHECK)) {
|
|
|
|
log_warn(LD_DIR, "Error tokenizing router descriptor (annotations).");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_warn(LD_DIR, "Found unexpected annotations on router descriptor not "
|
|
|
|
"loaded from disk. Dropping it.");
|
2007-12-19 04:11:02 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-02-25 10:31:36 +01:00
|
|
|
if (router_get_router_hash(s, end - s, digest) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Couldn't compute router hash.");
|
2008-01-16 06:27:19 +01:00
|
|
|
goto err;
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2007-09-27 18:08:10 +02:00
|
|
|
{
|
|
|
|
int flags = 0;
|
|
|
|
if (allow_annotations)
|
|
|
|
flags |= TS_ANNOTATIONS_OK;
|
|
|
|
if (prepend_annotations)
|
|
|
|
flags |= TS_ANNOTATIONS_OK|TS_NO_NEW_ANNOTATIONS;
|
|
|
|
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area,s,end,tokens,routerdesc_token_table, flags)) {
|
2007-09-27 18:08:10 +02:00
|
|
|
log_warn(LD_DIR, "Error tokenizing router descriptor.");
|
|
|
|
goto err;
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (smartlist_len(tokens) < 2) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Impossibly short router descriptor.");
|
2004-05-10 19:30:51 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_ROUTER);
|
2014-10-08 15:13:09 +02:00
|
|
|
const int router_token_pos = smartlist_pos(tokens, tok);
|
2007-04-16 18:28:06 +02:00
|
|
|
tor_assert(tok->n_args >= 5);
|
2004-05-10 19:30:51 +02:00
|
|
|
|
|
|
|
router = tor_malloc_zero(sizeof(routerinfo_t));
|
2014-10-01 17:54:07 +02:00
|
|
|
router->cert_expiration_time = TIME_MAX;
|
2007-11-07 18:11:23 +01:00
|
|
|
router->cache_info.routerlist_index = -1;
|
2007-09-27 22:46:30 +02:00
|
|
|
router->cache_info.annotations_len = s-start_of_annotations + prepend_len;
|
2005-11-05 21:15:27 +01:00
|
|
|
router->cache_info.signed_descriptor_len = end-s;
|
2007-09-27 18:08:10 +02:00
|
|
|
if (cache_copy) {
|
|
|
|
size_t len = router->cache_info.signed_descriptor_len +
|
2008-02-20 00:29:45 +01:00
|
|
|
router->cache_info.annotations_len;
|
2016-07-05 20:19:31 +02:00
|
|
|
char *signed_body =
|
2007-09-27 18:08:10 +02:00
|
|
|
router->cache_info.signed_descriptor_body = tor_malloc(len+1);
|
|
|
|
if (prepend_annotations) {
|
2016-07-05 20:19:31 +02:00
|
|
|
memcpy(signed_body, prepend_annotations, prepend_len);
|
|
|
|
signed_body += prepend_len;
|
2007-09-27 18:08:10 +02:00
|
|
|
}
|
2008-02-20 00:57:06 +01:00
|
|
|
/* This assertion will always succeed.
|
|
|
|
* len == signed_desc_len + annotations_len
|
|
|
|
* == end-s + s-start_of_annotations + prepend_len
|
|
|
|
* == end-start_of_annotations + prepend_len
|
|
|
|
* We already wrote prepend_len bytes into the buffer; now we're
|
|
|
|
* writing end-start_of_annotations -NM. */
|
2016-07-05 20:19:31 +02:00
|
|
|
tor_assert(signed_body+(end-start_of_annotations) ==
|
2008-02-20 00:29:45 +01:00
|
|
|
router->cache_info.signed_descriptor_body+len);
|
2016-07-05 20:19:31 +02:00
|
|
|
memcpy(signed_body, start_of_annotations, end-start_of_annotations);
|
2007-09-27 22:46:30 +02:00
|
|
|
router->cache_info.signed_descriptor_body[len] = '\0';
|
|
|
|
tor_assert(strlen(router->cache_info.signed_descriptor_body) == len);
|
2007-09-27 18:08:10 +02:00
|
|
|
}
|
2005-11-05 21:15:27 +01:00
|
|
|
memcpy(router->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2007-04-16 18:28:06 +02:00
|
|
|
router->nickname = tor_strdup(tok->args[0]);
|
|
|
|
if (!is_legal_nickname(router->nickname)) {
|
|
|
|
log_warn(LD_DIR,"Router nickname is invalid");
|
2004-05-10 19:30:51 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2013-02-10 04:07:22 +01:00
|
|
|
if (!tor_inet_aton(tok->args[1], &in)) {
|
2007-12-19 04:11:02 +01:00
|
|
|
log_warn(LD_DIR,"Router address is not an IP address.");
|
2004-05-10 19:30:51 +02:00
|
|
|
goto err;
|
2007-04-16 18:28:06 +02:00
|
|
|
}
|
|
|
|
router->addr = ntohl(in.s_addr);
|
2007-02-24 20:29:42 +01:00
|
|
|
|
2007-04-16 18:28:06 +02:00
|
|
|
router->or_port =
|
2008-02-06 01:54:47 +01:00
|
|
|
(uint16_t) tor_parse_long(tok->args[2],10,0,65535,&ok,NULL);
|
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR,"Invalid OR port %s", escaped(tok->args[2]));
|
|
|
|
goto err;
|
|
|
|
}
|
2007-04-16 18:28:06 +02:00
|
|
|
router->dir_port =
|
2008-02-06 01:54:47 +01:00
|
|
|
(uint16_t) tor_parse_long(tok->args[4],10,0,65535,&ok,NULL);
|
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR,"Invalid dir port %s", escaped(tok->args[4]));
|
|
|
|
goto err;
|
|
|
|
}
|
2007-02-24 20:29:42 +01:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_BANDWIDTH);
|
|
|
|
tor_assert(tok->n_args >= 3);
|
2008-02-22 20:09:45 +01:00
|
|
|
router->bandwidthrate = (int)
|
2008-02-06 01:54:47 +01:00
|
|
|
tor_parse_long(tok->args[0],10,1,INT_MAX,&ok,NULL);
|
2007-02-24 20:29:42 +01:00
|
|
|
|
2008-02-06 01:54:47 +01:00
|
|
|
if (!ok) {
|
2007-04-16 18:28:06 +02:00
|
|
|
log_warn(LD_DIR, "bandwidthrate %s unreadable or 0. Failing.",
|
|
|
|
escaped(tok->args[0]));
|
|
|
|
goto err;
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2008-02-22 20:09:45 +01:00
|
|
|
router->bandwidthburst =
|
|
|
|
(int) tor_parse_long(tok->args[1],10,0,INT_MAX,&ok,NULL);
|
2008-02-06 01:54:47 +01:00
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Invalid bandwidthburst %s", escaped(tok->args[1]));
|
|
|
|
goto err;
|
|
|
|
}
|
2008-02-22 20:09:45 +01:00
|
|
|
router->bandwidthcapacity = (int)
|
2008-02-06 01:54:47 +01:00
|
|
|
tor_parse_long(tok->args[2],10,0,INT_MAX,&ok,NULL);
|
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Invalid bandwidthcapacity %s", escaped(tok->args[1]));
|
|
|
|
goto err;
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, A_PURPOSE))) {
|
2008-12-11 21:28:50 +01:00
|
|
|
tor_assert(tok->n_args);
|
2007-09-27 22:46:30 +02:00
|
|
|
router->purpose = router_purpose_from_string(tok->args[0]);
|
|
|
|
} else {
|
|
|
|
router->purpose = ROUTER_PURPOSE_GENERAL;
|
|
|
|
}
|
2008-01-10 18:48:40 +01:00
|
|
|
router->cache_info.send_unencrypted =
|
|
|
|
(router->purpose == ROUTER_PURPOSE_GENERAL) ? 1 : 0;
|
2007-09-27 22:46:30 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_UPTIME))) {
|
2007-04-16 18:28:06 +02:00
|
|
|
tor_assert(tok->n_args >= 1);
|
2008-02-06 01:54:47 +01:00
|
|
|
router->uptime = tor_parse_long(tok->args[0],10,0,LONG_MAX,&ok,NULL);
|
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Invalid uptime %s", escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
2004-08-17 07:29:41 +02:00
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_HIBERNATING))) {
|
2007-04-16 18:28:06 +02:00
|
|
|
tor_assert(tok->n_args >= 1);
|
|
|
|
router->is_hibernating
|
|
|
|
= (tor_parse_long(tok->args[0],10,0,LONG_MAX,NULL,NULL) != 0);
|
2005-08-30 17:04:24 +02:00
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_PUBLISHED);
|
2004-05-10 19:30:51 +02:00
|
|
|
tor_assert(tok->n_args == 1);
|
2005-11-05 21:15:27 +01:00
|
|
|
if (parse_iso_time(tok->args[0], &router->cache_info.published_on) < 0)
|
2004-10-17 00:14:52 +02:00
|
|
|
goto err;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_ONION_KEY);
|
2011-05-16 20:44:23 +02:00
|
|
|
if (!crypto_pk_public_exponent_ok(tok->key)) {
|
2011-04-28 23:35:03 +02:00
|
|
|
log_warn(LD_DIR,
|
|
|
|
"Relay's onion key had invalid exponent.");
|
|
|
|
goto err;
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
router->onion_pkey = tok->key;
|
|
|
|
tok->key = NULL; /* Prevent free */
|
|
|
|
|
2012-12-04 21:58:18 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
|
2013-01-06 04:53:32 +01:00
|
|
|
curve25519_public_key_t k;
|
2012-12-04 21:58:18 +01:00
|
|
|
tor_assert(tok->n_args >= 1);
|
2013-01-06 04:53:32 +01:00
|
|
|
if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
|
|
|
|
log_warn(LD_DIR, "Bogus ntor-onion-key in routerinfo");
|
2012-12-04 21:58:18 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
router->onion_curve25519_pkey =
|
2013-01-06 04:53:32 +01:00
|
|
|
tor_memdup(&k, sizeof(curve25519_public_key_t));
|
2012-12-04 21:58:18 +01:00
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_SIGNING_KEY);
|
2004-05-10 19:30:51 +02:00
|
|
|
router->identity_pkey = tok->key;
|
|
|
|
tok->key = NULL; /* Prevent free */
|
2005-11-05 21:15:27 +01:00
|
|
|
if (crypto_pk_get_digest(router->identity_pkey,
|
|
|
|
router->cache_info.identity_digest)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Couldn't calculate key digest"); goto err;
|
2004-07-01 03:16:59 +02:00
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2014-10-01 05:36:47 +02:00
|
|
|
{
|
2015-06-01 17:24:55 +02:00
|
|
|
directory_token_t *ed_sig_tok, *ed_cert_tok, *cc_tap_tok, *cc_ntor_tok,
|
|
|
|
*master_key_tok;
|
2014-10-01 05:36:47 +02:00
|
|
|
ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519);
|
|
|
|
ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519);
|
2015-06-01 17:24:55 +02:00
|
|
|
master_key_tok = find_opt_by_keyword(tokens, K_MASTER_KEY_ED25519);
|
2014-10-01 17:54:07 +02:00
|
|
|
cc_tap_tok = find_opt_by_keyword(tokens, K_ONION_KEY_CROSSCERT);
|
|
|
|
cc_ntor_tok = find_opt_by_keyword(tokens, K_NTOR_ONION_KEY_CROSSCERT);
|
|
|
|
int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok +
|
|
|
|
!!cc_tap_tok + !!cc_ntor_tok;
|
|
|
|
if ((n_ed_toks != 0 && n_ed_toks != 4) ||
|
|
|
|
(n_ed_toks == 4 && !router->onion_curve25519_pkey)) {
|
|
|
|
log_warn(LD_DIR, "Router descriptor with only partial ed25519/"
|
|
|
|
"cross-certification support");
|
2014-10-01 05:36:47 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2015-06-01 17:24:55 +02:00
|
|
|
if (master_key_tok && !ed_sig_tok) {
|
|
|
|
log_warn(LD_DIR, "Router descriptor has ed25519 master key but no "
|
|
|
|
"certificate");
|
|
|
|
goto err;
|
|
|
|
}
|
2014-10-01 05:36:47 +02:00
|
|
|
if (ed_sig_tok) {
|
2014-10-01 17:54:07 +02:00
|
|
|
tor_assert(ed_cert_tok && cc_tap_tok && cc_ntor_tok);
|
2014-10-08 15:13:09 +02:00
|
|
|
const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok);
|
|
|
|
if (ed_cert_token_pos == -1 || router_token_pos == -1 ||
|
|
|
|
(ed_cert_token_pos != router_token_pos + 1 &&
|
|
|
|
ed_cert_token_pos != router_token_pos - 1)) {
|
2014-10-01 05:36:47 +02:00
|
|
|
log_warn(LD_DIR, "Ed25519 certificate in wrong position");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) {
|
|
|
|
log_warn(LD_DIR, "Ed25519 signature in wrong position");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) {
|
|
|
|
log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor");
|
|
|
|
goto err;
|
|
|
|
}
|
2014-10-01 17:54:07 +02:00
|
|
|
if (strcmp(cc_ntor_tok->object_type, "ED25519 CERT")) {
|
|
|
|
log_warn(LD_DIR, "Wrong object type on ntor-onion-key-crosscert "
|
|
|
|
"in decriptor");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (strcmp(cc_tap_tok->object_type, "CROSSCERT")) {
|
|
|
|
log_warn(LD_DIR, "Wrong object type on onion-key-crosscert "
|
|
|
|
"in decriptor");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (strcmp(cc_ntor_tok->args[0], "0") &&
|
|
|
|
strcmp(cc_ntor_tok->args[0], "1")) {
|
|
|
|
log_warn(LD_DIR, "Bad sign bit on ntor-onion-key-crosscert");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
int ntor_cc_sign_bit = !strcmp(cc_ntor_tok->args[0], "1");
|
2014-10-01 05:36:47 +02:00
|
|
|
|
|
|
|
uint8_t d256[DIGEST256_LEN];
|
|
|
|
const char *signed_start, *signed_end;
|
|
|
|
tor_cert_t *cert = tor_cert_parse(
|
|
|
|
(const uint8_t*)ed_cert_tok->object_body,
|
|
|
|
ed_cert_tok->object_size);
|
|
|
|
if (! cert) {
|
|
|
|
log_warn(LD_DIR, "Couldn't parse ed25519 cert");
|
|
|
|
goto err;
|
|
|
|
}
|
2016-05-18 02:08:03 +02:00
|
|
|
/* makes sure it gets freed. */
|
|
|
|
router->cache_info.signing_key_cert = cert;
|
2015-06-01 17:24:55 +02:00
|
|
|
|
2014-10-01 05:36:47 +02:00
|
|
|
if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
|
|
|
|
! cert->signing_key_included) {
|
|
|
|
log_warn(LD_DIR, "Invalid form for ed25519 cert");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2015-06-01 17:24:55 +02:00
|
|
|
if (master_key_tok) {
|
|
|
|
/* This token is optional, but if it's present, it must match
|
|
|
|
* the signature in the signing cert, or supplant it. */
|
|
|
|
tor_assert(master_key_tok->n_args >= 1);
|
|
|
|
ed25519_public_key_t pkey;
|
|
|
|
if (ed25519_public_from_base64(&pkey, master_key_tok->args[0])<0) {
|
|
|
|
log_warn(LD_DIR, "Can't parse ed25519 master key");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fast_memneq(&cert->signing_key.pubkey,
|
|
|
|
pkey.pubkey, ED25519_PUBKEY_LEN)) {
|
|
|
|
log_warn(LD_DIR, "Ed25519 master key does not match "
|
|
|
|
"key in certificate");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2014-10-01 17:54:07 +02:00
|
|
|
ntor_cc_cert = tor_cert_parse((const uint8_t*)cc_ntor_tok->object_body,
|
|
|
|
cc_ntor_tok->object_size);
|
2015-05-28 16:42:22 +02:00
|
|
|
if (!ntor_cc_cert) {
|
2014-10-01 17:54:07 +02:00
|
|
|
log_warn(LD_DIR, "Couldn't parse ntor-onion-key-crosscert cert");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (ntor_cc_cert->cert_type != CERT_TYPE_ONION_ID ||
|
|
|
|
! ed25519_pubkey_eq(&ntor_cc_cert->signed_key, &cert->signing_key)) {
|
|
|
|
log_warn(LD_DIR, "Invalid contents for ntor-onion-key-crosscert cert");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
ed25519_public_key_t ntor_cc_pk;
|
|
|
|
if (ed25519_public_key_from_curve25519_public_key(&ntor_cc_pk,
|
|
|
|
router->onion_curve25519_pkey,
|
|
|
|
ntor_cc_sign_bit)<0) {
|
|
|
|
log_warn(LD_DIR, "Error converting onion key to ed25519");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-10-01 05:36:47 +02:00
|
|
|
if (router_get_hash_impl_helper(s, end-s, "router ",
|
|
|
|
"\nrouter-sig-ed25519",
|
2017-05-03 17:48:08 +02:00
|
|
|
' ', LOG_WARN,
|
|
|
|
&signed_start, &signed_end) < 0) {
|
2014-10-01 05:36:47 +02:00
|
|
|
log_warn(LD_DIR, "Can't find ed25519-signed portion of descriptor");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256);
|
|
|
|
crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX,
|
|
|
|
strlen(ED_DESC_SIGNATURE_PREFIX));
|
|
|
|
crypto_digest_add_bytes(d, signed_start, signed_end-signed_start);
|
|
|
|
crypto_digest_get_digest(d, (char*)d256, sizeof(d256));
|
|
|
|
crypto_digest_free(d);
|
|
|
|
|
2014-10-01 17:54:07 +02:00
|
|
|
ed25519_checkable_t check[3];
|
|
|
|
int check_ok[3];
|
2016-08-30 14:48:50 +02:00
|
|
|
time_t expires = TIME_MAX;
|
|
|
|
if (tor_cert_get_checkable_sig(&check[0], cert, NULL, &expires) < 0) {
|
2014-10-01 05:36:47 +02:00
|
|
|
log_err(LD_BUG, "Couldn't create 'checkable' for cert.");
|
|
|
|
goto err;
|
|
|
|
}
|
2014-10-01 17:54:07 +02:00
|
|
|
if (tor_cert_get_checkable_sig(&check[1],
|
2016-08-30 14:48:50 +02:00
|
|
|
ntor_cc_cert, &ntor_cc_pk, &expires) < 0) {
|
2014-10-01 17:54:07 +02:00
|
|
|
log_err(LD_BUG, "Couldn't create 'checkable' for ntor_cc_cert.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ed25519_signature_from_base64(&check[2].signature,
|
2014-10-01 05:36:47 +02:00
|
|
|
ed_sig_tok->args[0])<0) {
|
|
|
|
log_warn(LD_DIR, "Couldn't decode ed25519 signature");
|
|
|
|
goto err;
|
|
|
|
}
|
2014-10-01 17:54:07 +02:00
|
|
|
check[2].pubkey = &cert->signed_key;
|
|
|
|
check[2].msg = d256;
|
|
|
|
check[2].len = DIGEST256_LEN;
|
2014-10-01 05:36:47 +02:00
|
|
|
|
2014-10-01 17:54:07 +02:00
|
|
|
if (ed25519_checksig_batch(check_ok, check, 3) < 0) {
|
|
|
|
log_warn(LD_DIR, "Incorrect ed25519 signature(s)");
|
2014-10-01 05:36:47 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2014-10-01 17:54:07 +02:00
|
|
|
|
|
|
|
if (check_tap_onion_key_crosscert(
|
|
|
|
(const uint8_t*)cc_tap_tok->object_body,
|
|
|
|
(int)cc_tap_tok->object_size,
|
|
|
|
router->onion_pkey,
|
|
|
|
&cert->signing_key,
|
|
|
|
(const uint8_t*)router->cache_info.identity_digest)<0) {
|
|
|
|
log_warn(LD_DIR, "Incorrect TAP cross-verification");
|
2014-10-01 05:36:47 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-10-01 17:54:07 +02:00
|
|
|
/* We check this before adding it to the routerlist. */
|
2016-08-30 14:48:50 +02:00
|
|
|
router->cert_expiration_time = expires;
|
2014-10-01 17:54:07 +02:00
|
|
|
}
|
2004-07-01 03:16:59 +02:00
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_FINGERPRINT))) {
|
2005-11-15 22:24:32 +01:00
|
|
|
/* If there's a fingerprint line, it must match the identity digest. */
|
|
|
|
char d[DIGEST_LEN];
|
2007-04-16 18:28:06 +02:00
|
|
|
tor_assert(tok->n_args == 1);
|
2005-11-15 22:24:32 +01:00
|
|
|
tor_strstrip(tok->args[0], " ");
|
2016-06-17 16:41:45 +02:00
|
|
|
if (base16_decode(d, DIGEST_LEN,
|
|
|
|
tok->args[0], strlen(tok->args[0])) != DIGEST_LEN) {
|
2006-09-30 00:33:40 +02:00
|
|
|
log_warn(LD_DIR, "Couldn't decode router fingerprint %s",
|
2006-03-05 10:50:26 +01:00
|
|
|
escaped(tok->args[0]));
|
2005-11-15 22:24:32 +01:00
|
|
|
goto err;
|
|
|
|
}
|
2011-05-10 22:58:38 +02:00
|
|
|
if (tor_memneq(d,router->cache_info.identity_digest, DIGEST_LEN)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Fingerprint '%s' does not match identity digest.",
|
|
|
|
tok->args[0]);
|
2005-11-15 22:24:32 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-24 19:49:15 +01:00
|
|
|
{
|
|
|
|
const char *version = NULL, *protocols = NULL;
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_PLATFORM))) {
|
|
|
|
router->platform = tor_strdup(tok->args[0]);
|
|
|
|
version = tok->args[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
|
|
|
|
router->protocol_list = tor_strdup(tok->args[0]);
|
|
|
|
protocols = tok->args[0];
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2018-01-24 19:49:15 +01:00
|
|
|
summarize_protover_flags(&router->pv, protocols, version);
|
2016-08-19 20:10:20 +02:00
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_CONTACT))) {
|
2005-05-02 23:22:31 +02:00
|
|
|
router->contact_info = tor_strdup(tok->args[0]);
|
|
|
|
}
|
|
|
|
|
2011-03-06 19:31:06 +01:00
|
|
|
if (find_opt_by_keyword(tokens, K_REJECT6) ||
|
|
|
|
find_opt_by_keyword(tokens, K_ACCEPT6)) {
|
|
|
|
log_warn(LD_DIR, "Rejecting router with reject6/accept6 line: they crash "
|
|
|
|
"older Tors.");
|
|
|
|
goto err;
|
|
|
|
}
|
2011-11-08 22:51:30 +01:00
|
|
|
{
|
|
|
|
smartlist_t *or_addresses = find_all_by_keyword(tokens, K_OR_ADDRESS);
|
|
|
|
if (or_addresses) {
|
2012-08-14 14:03:58 +02:00
|
|
|
find_single_ipv6_orport(or_addresses, &router->ipv6_addr,
|
|
|
|
&router->ipv6_orport);
|
2011-11-08 22:51:30 +01:00
|
|
|
smartlist_free(or_addresses);
|
|
|
|
}
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
exit_policy_tokens = find_all_exitpolicy(tokens);
|
2008-03-17 21:10:57 +01:00
|
|
|
if (!smartlist_len(exit_policy_tokens)) {
|
|
|
|
log_warn(LD_DIR, "No exit policy tokens in descriptor.");
|
|
|
|
goto err;
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
SMARTLIST_FOREACH(exit_policy_tokens, directory_token_t *, t,
|
|
|
|
if (router_add_exit_policy(router,t)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR,"Error in exit policy");
|
2005-05-02 23:48:25 +02:00
|
|
|
goto err;
|
|
|
|
});
|
2008-01-02 05:43:44 +01:00
|
|
|
policy_expand_private(&router->exit_policy);
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2012-10-25 03:59:55 +02:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_IPV6_POLICY)) && tok->n_args) {
|
|
|
|
router->ipv6_exit_policy = parse_short_policy(tok->args[0]);
|
|
|
|
if (! router->ipv6_exit_policy) {
|
|
|
|
log_warn(LD_DIR , "Error in ipv6-policy %s", escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-31 20:05:56 +01:00
|
|
|
if (policy_is_reject_star(router->exit_policy, AF_INET, 1) &&
|
2012-11-15 02:51:41 +01:00
|
|
|
(!router->ipv6_exit_policy ||
|
|
|
|
short_policy_is_reject_star(router->ipv6_exit_policy)))
|
|
|
|
router->policy_is_reject_star = 1;
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_FAMILY)) && tok->n_args) {
|
2004-10-15 03:58:11 +02:00
|
|
|
int i;
|
2012-01-18 21:53:30 +01:00
|
|
|
router->declared_family = smartlist_new();
|
2004-10-15 03:58:11 +02:00
|
|
|
for (i=0;i<tok->n_args;++i) {
|
|
|
|
if (!is_legal_nickname_or_hexdigest(tok->args[i])) {
|
2006-03-05 10:50:26 +01:00
|
|
|
log_warn(LD_DIR, "Illegal nickname %s in family line",
|
|
|
|
escaped(tok->args[i]));
|
2004-10-15 03:58:11 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(router->declared_family, tok->args[i]);
|
2004-10-15 03:58:11 +02:00
|
|
|
}
|
|
|
|
}
|
2004-11-09 21:04:00 +01:00
|
|
|
|
2011-06-08 21:16:11 +02:00
|
|
|
if (find_opt_by_keyword(tokens, K_CACHES_EXTRA_INFO))
|
2007-04-16 06:17:58 +02:00
|
|
|
router->caches_extra_info = 1;
|
|
|
|
|
2011-06-08 21:16:11 +02:00
|
|
|
if (find_opt_by_keyword(tokens, K_ALLOW_SINGLE_HOP_EXITS))
|
2008-09-26 20:58:45 +02:00
|
|
|
router->allow_single_hop_exits = 1;
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_EXTRA_INFO_DIGEST))) {
|
2007-04-16 18:28:06 +02:00
|
|
|
tor_assert(tok->n_args >= 1);
|
2007-04-16 06:17:58 +02:00
|
|
|
if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
|
2016-06-17 16:41:45 +02:00
|
|
|
if (base16_decode(router->cache_info.extra_info_digest, DIGEST_LEN,
|
|
|
|
tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN) {
|
|
|
|
log_warn(LD_DIR,"Invalid extra info digest");
|
|
|
|
}
|
2007-04-16 06:17:58 +02:00
|
|
|
} else {
|
|
|
|
log_warn(LD_DIR, "Invalid extra info digest %s", escaped(tok->args[0]));
|
|
|
|
}
|
2015-05-28 16:42:22 +02:00
|
|
|
|
|
|
|
if (tok->n_args >= 2) {
|
2016-05-18 02:08:03 +02:00
|
|
|
if (digest256_from_base64(router->cache_info.extra_info_digest256,
|
|
|
|
tok->args[1]) < 0) {
|
2015-05-28 16:42:22 +02:00
|
|
|
log_warn(LD_DIR, "Invalid extra info digest256 %s",
|
|
|
|
escaped(tok->args[1]));
|
|
|
|
}
|
|
|
|
}
|
2007-04-16 06:17:58 +02:00
|
|
|
}
|
|
|
|
|
2011-06-08 21:16:11 +02:00
|
|
|
if (find_opt_by_keyword(tokens, K_HIDDEN_SERVICE_DIR)) {
|
2007-10-29 20:10:42 +01:00
|
|
|
router->wants_to_be_hs_dir = 1;
|
|
|
|
}
|
|
|
|
|
2014-10-28 18:12:52 +01:00
|
|
|
/* This router accepts tunnelled directory requests via begindir if it has
|
|
|
|
* an open dirport or it included "tunnelled-dir-server". */
|
|
|
|
if (find_opt_by_keyword(tokens, K_DIR_TUNNELLED) || router->dir_port > 0) {
|
|
|
|
router->supports_tunnelled_dir_requests = 1;
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE);
|
2006-11-30 23:23:01 +01:00
|
|
|
#ifdef COUNT_DISTINCT_DIGESTS
|
|
|
|
if (!verified_digests)
|
|
|
|
verified_digests = digestmap_new();
|
|
|
|
digestmap_set(verified_digests, signed_digest, (void*)(uintptr_t)1);
|
|
|
|
#endif
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!router->or_port) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR,"or_port unreadable or 0. Failing.");
|
2004-05-10 19:30:51 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2007-02-24 20:29:42 +01:00
|
|
|
|
2014-10-13 20:22:52 +02:00
|
|
|
/* We've checked everything that's covered by the hash. */
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
can_dl_again = 1;
|
|
|
|
if (check_signature_token(digest, DIGEST_LEN, tok, router->identity_pkey, 0,
|
|
|
|
"router descriptor") < 0)
|
|
|
|
goto err;
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
if (!router->platform) {
|
|
|
|
router->platform = tor_strdup("<unknown>");
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
err:
|
2009-08-20 18:45:03 +02:00
|
|
|
dump_desc(s_dup, "router descriptor");
|
2004-05-10 19:30:51 +02:00
|
|
|
routerinfo_free(router);
|
|
|
|
router = NULL;
|
|
|
|
done:
|
2014-10-01 17:54:07 +02:00
|
|
|
tor_cert_free(ntor_cc_cert);
|
2004-05-10 19:30:51 +02:00
|
|
|
if (tokens) {
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2004-05-10 19:30:51 +02:00
|
|
|
smartlist_free(tokens);
|
|
|
|
}
|
2009-12-12 08:07:59 +01:00
|
|
|
smartlist_free(exit_policy_tokens);
|
2008-03-26 18:50:27 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "routerinfo");
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_drop_all(area);
|
2008-03-26 18:50:27 +01:00
|
|
|
}
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
if (can_dl_again_out)
|
|
|
|
*can_dl_again_out = can_dl_again;
|
2004-05-10 19:30:51 +02:00
|
|
|
return router;
|
|
|
|
}
|
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/** Parse a single extrainfo entry from the string <b>s</b>, ending at
|
|
|
|
* <b>end</b>. (If <b>end</b> is NULL, parse up to the end of <b>s</b>.) If
|
|
|
|
* <b>cache_copy</b> is true, make a copy of the extra-info document in the
|
|
|
|
* cache_info fields of the result. If <b>routermap</b> is provided, use it
|
|
|
|
* as a map from router identity to routerinfo_t when looking up signing keys.
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
*
|
|
|
|
* If <b>can_dl_again_out</b> is provided, set *<b>can_dl_again_out</b> to 1
|
|
|
|
* if it's okay to try to download an extrainfo with this same digest again,
|
|
|
|
* and 0 if it isn't. (It might not be okay to download it again if part of
|
|
|
|
* the part covered by the digest is invalid.)
|
2007-05-15 00:51:05 +02:00
|
|
|
*/
|
2007-04-16 06:17:58 +02:00
|
|
|
extrainfo_t *
|
|
|
|
extrainfo_parse_entry_from_string(const char *s, const char *end,
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
int cache_copy, struct digest_ri_map_t *routermap,
|
|
|
|
int *can_dl_again_out)
|
2007-04-16 06:17:58 +02:00
|
|
|
{
|
|
|
|
extrainfo_t *extrainfo = NULL;
|
|
|
|
char digest[128];
|
|
|
|
smartlist_t *tokens = NULL;
|
|
|
|
directory_token_t *tok;
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_pk_t *key = NULL;
|
2008-01-10 18:48:40 +01:00
|
|
|
routerinfo_t *router = NULL;
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_t *area = NULL;
|
2009-08-20 18:45:03 +02:00
|
|
|
const char *s_dup = s;
|
2014-10-13 20:22:52 +02:00
|
|
|
/* Do not set this to '1' until we have parsed everything that we intend to
|
|
|
|
* parse that's covered by the hash. */
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
int can_dl_again = 0;
|
2007-04-16 06:17:58 +02:00
|
|
|
|
2017-03-06 17:31:11 +01:00
|
|
|
if (BUG(s == NULL))
|
|
|
|
return NULL;
|
|
|
|
|
2007-04-16 06:17:58 +02:00
|
|
|
if (!end) {
|
|
|
|
end = s + strlen(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* point 'end' to a point immediately after the final newline. */
|
|
|
|
while (end > s+2 && *(end-1) == '\n' && *(end-2) == '\n')
|
|
|
|
--end;
|
|
|
|
|
2012-05-10 23:27:16 +02:00
|
|
|
if (router_get_extrainfo_hash(s, end-s, digest) < 0) {
|
2007-04-16 06:17:58 +02:00
|
|
|
log_warn(LD_DIR, "Couldn't compute router hash.");
|
2009-01-13 15:43:51 +01:00
|
|
|
goto err;
|
2007-04-16 06:17:58 +02:00
|
|
|
}
|
2012-01-18 21:53:30 +01:00
|
|
|
tokens = smartlist_new();
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area,s,end,tokens,extrainfo_token_table,0)) {
|
2007-09-26 18:19:44 +02:00
|
|
|
log_warn(LD_DIR, "Error tokenizing extra-info document.");
|
2007-04-16 06:17:58 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (smartlist_len(tokens) < 2) {
|
2007-09-26 18:19:44 +02:00
|
|
|
log_warn(LD_DIR, "Impossibly short extra-info document.");
|
2007-04-16 06:17:58 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2015-05-28 16:42:22 +02:00
|
|
|
/* XXXX Accept this in position 1 too, and ed identity in position 0. */
|
2007-04-16 06:17:58 +02:00
|
|
|
tok = smartlist_get(tokens,0);
|
|
|
|
if (tok->tp != K_EXTRA_INFO) {
|
|
|
|
log_warn(LD_DIR,"Entry does not start with \"extra-info\"");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
extrainfo = tor_malloc_zero(sizeof(extrainfo_t));
|
2007-05-20 07:15:53 +02:00
|
|
|
extrainfo->cache_info.is_extrainfo = 1;
|
2007-04-16 06:17:58 +02:00
|
|
|
if (cache_copy)
|
2013-04-18 16:30:14 +02:00
|
|
|
extrainfo->cache_info.signed_descriptor_body = tor_memdup_nulterm(s,end-s);
|
2007-04-16 06:17:58 +02:00
|
|
|
extrainfo->cache_info.signed_descriptor_len = end-s;
|
|
|
|
memcpy(extrainfo->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
|
2015-05-28 16:42:22 +02:00
|
|
|
crypto_digest256((char*)extrainfo->digest256, s, end-s, DIGEST_SHA256);
|
2007-04-16 19:55:08 +02:00
|
|
|
|
2007-04-16 18:28:06 +02:00
|
|
|
tor_assert(tok->n_args >= 2);
|
2007-04-16 06:17:58 +02:00
|
|
|
if (!is_legal_nickname(tok->args[0])) {
|
|
|
|
log_warn(LD_DIR,"Bad nickname %s on \"extra-info\"",escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
strlcpy(extrainfo->nickname, tok->args[0], sizeof(extrainfo->nickname));
|
|
|
|
if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
|
|
|
|
base16_decode(extrainfo->cache_info.identity_digest, DIGEST_LEN,
|
2016-06-17 16:41:45 +02:00
|
|
|
tok->args[1], HEX_DIGEST_LEN) != DIGEST_LEN) {
|
2007-04-16 06:17:58 +02:00
|
|
|
log_warn(LD_DIR,"Invalid fingerprint %s on \"extra-info\"",
|
|
|
|
escaped(tok->args[1]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_PUBLISHED);
|
2007-04-16 06:17:58 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &extrainfo->cache_info.published_on)) {
|
|
|
|
log_warn(LD_DIR,"Invalid published time %s on \"extra-info\"",
|
|
|
|
escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2015-05-28 16:42:22 +02:00
|
|
|
{
|
|
|
|
directory_token_t *ed_sig_tok, *ed_cert_tok;
|
|
|
|
ed_sig_tok = find_opt_by_keyword(tokens, K_ROUTER_SIG_ED25519);
|
|
|
|
ed_cert_tok = find_opt_by_keyword(tokens, K_IDENTITY_ED25519);
|
|
|
|
int n_ed_toks = !!ed_sig_tok + !!ed_cert_tok;
|
|
|
|
if (n_ed_toks != 0 && n_ed_toks != 2) {
|
|
|
|
log_warn(LD_DIR, "Router descriptor with only partial ed25519/"
|
|
|
|
"cross-certification support");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (ed_sig_tok) {
|
|
|
|
tor_assert(ed_cert_tok);
|
|
|
|
const int ed_cert_token_pos = smartlist_pos(tokens, ed_cert_tok);
|
|
|
|
if (ed_cert_token_pos != 1) {
|
|
|
|
/* Accept this in position 0 XXXX */
|
|
|
|
log_warn(LD_DIR, "Ed25519 certificate in wrong position");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (ed_sig_tok != smartlist_get(tokens, smartlist_len(tokens)-2)) {
|
|
|
|
log_warn(LD_DIR, "Ed25519 signature in wrong position");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (strcmp(ed_cert_tok->object_type, "ED25519 CERT")) {
|
|
|
|
log_warn(LD_DIR, "Wrong object type on identity-ed25519 in decriptor");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t d256[DIGEST256_LEN];
|
|
|
|
const char *signed_start, *signed_end;
|
|
|
|
tor_cert_t *cert = tor_cert_parse(
|
|
|
|
(const uint8_t*)ed_cert_tok->object_body,
|
|
|
|
ed_cert_tok->object_size);
|
|
|
|
if (! cert) {
|
|
|
|
log_warn(LD_DIR, "Couldn't parse ed25519 cert");
|
|
|
|
goto err;
|
|
|
|
}
|
2016-05-18 02:04:16 +02:00
|
|
|
/* makes sure it gets freed. */
|
|
|
|
extrainfo->cache_info.signing_key_cert = cert;
|
|
|
|
|
2015-05-28 16:42:22 +02:00
|
|
|
if (cert->cert_type != CERT_TYPE_ID_SIGNING ||
|
|
|
|
! cert->signing_key_included) {
|
|
|
|
log_warn(LD_DIR, "Invalid form for ed25519 cert");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (router_get_hash_impl_helper(s, end-s, "extra-info ",
|
|
|
|
"\nrouter-sig-ed25519",
|
2017-05-03 17:48:08 +02:00
|
|
|
' ', LOG_WARN,
|
|
|
|
&signed_start, &signed_end) < 0) {
|
2015-05-28 16:42:22 +02:00
|
|
|
log_warn(LD_DIR, "Can't find ed25519-signed portion of extrainfo");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
crypto_digest_t *d = crypto_digest256_new(DIGEST_SHA256);
|
|
|
|
crypto_digest_add_bytes(d, ED_DESC_SIGNATURE_PREFIX,
|
|
|
|
strlen(ED_DESC_SIGNATURE_PREFIX));
|
|
|
|
crypto_digest_add_bytes(d, signed_start, signed_end-signed_start);
|
|
|
|
crypto_digest_get_digest(d, (char*)d256, sizeof(d256));
|
|
|
|
crypto_digest_free(d);
|
|
|
|
|
|
|
|
ed25519_checkable_t check[2];
|
|
|
|
int check_ok[2];
|
2016-08-30 14:48:50 +02:00
|
|
|
if (tor_cert_get_checkable_sig(&check[0], cert, NULL, NULL) < 0) {
|
2015-05-28 16:42:22 +02:00
|
|
|
log_err(LD_BUG, "Couldn't create 'checkable' for cert.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ed25519_signature_from_base64(&check[1].signature,
|
|
|
|
ed_sig_tok->args[0])<0) {
|
|
|
|
log_warn(LD_DIR, "Couldn't decode ed25519 signature");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
check[1].pubkey = &cert->signed_key;
|
|
|
|
check[1].msg = d256;
|
|
|
|
check[1].len = DIGEST256_LEN;
|
|
|
|
|
|
|
|
if (ed25519_checksig_batch(check_ok, check, 2) < 0) {
|
|
|
|
log_warn(LD_DIR, "Incorrect ed25519 signature(s)");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* We don't check the certificate expiration time: checking that it
|
|
|
|
* matches the cert in the router descriptor is adequate. */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-13 20:22:52 +02:00
|
|
|
/* We've checked everything that's covered by the hash. */
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
can_dl_again = 1;
|
|
|
|
|
2007-04-16 06:17:58 +02:00
|
|
|
if (routermap &&
|
2007-08-14 04:23:57 +02:00
|
|
|
(router = digestmap_get((digestmap_t*)routermap,
|
2007-04-16 06:17:58 +02:00
|
|
|
extrainfo->cache_info.identity_digest))) {
|
|
|
|
key = router->identity_pkey;
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_ROUTER_SIGNATURE);
|
2007-05-15 00:51:05 +02:00
|
|
|
if (strcmp(tok->object_type, "SIGNATURE") ||
|
|
|
|
tok->object_size < 128 || tok->object_size > 512) {
|
|
|
|
log_warn(LD_DIR, "Bad object type or length on extra-info signature");
|
2007-04-16 06:17:58 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (key) {
|
2009-09-14 18:02:00 +02:00
|
|
|
if (check_signature_token(digest, DIGEST_LEN, tok, key, 0,
|
|
|
|
"extra-info") < 0)
|
2007-04-16 06:17:58 +02:00
|
|
|
goto err;
|
2007-05-15 00:51:05 +02:00
|
|
|
|
2008-01-10 18:48:40 +01:00
|
|
|
if (router)
|
|
|
|
extrainfo->cache_info.send_unencrypted =
|
|
|
|
router->cache_info.send_unencrypted;
|
2007-04-16 06:17:58 +02:00
|
|
|
} else {
|
2007-05-15 00:51:05 +02:00
|
|
|
extrainfo->pending_sig = tor_memdup(tok->object_body,
|
|
|
|
tok->object_size);
|
|
|
|
extrainfo->pending_sig_len = tok->object_size;
|
2007-04-16 06:17:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
goto done;
|
|
|
|
err:
|
2009-08-20 18:45:03 +02:00
|
|
|
dump_desc(s_dup, "extra-info descriptor");
|
2009-12-12 08:07:59 +01:00
|
|
|
extrainfo_free(extrainfo);
|
2007-04-16 06:17:58 +02:00
|
|
|
extrainfo = NULL;
|
|
|
|
done:
|
|
|
|
if (tokens) {
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2007-04-16 06:17:58 +02:00
|
|
|
smartlist_free(tokens);
|
|
|
|
}
|
2008-03-26 18:50:27 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "extrainfo");
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_drop_all(area);
|
2008-03-26 18:50:27 +01:00
|
|
|
}
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
if (can_dl_again_out)
|
|
|
|
*can_dl_again_out = can_dl_again;
|
2007-04-16 06:17:58 +02:00
|
|
|
return extrainfo;
|
|
|
|
}
|
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/** Parse a key certificate from <b>s</b>; point <b>end-of-string</b> to
|
|
|
|
* the first character after the certificate. */
|
2007-05-02 23:37:55 +02:00
|
|
|
authority_cert_t *
|
2007-05-22 19:58:25 +02:00
|
|
|
authority_cert_parse_from_string(const char *s, const char **end_of_string)
|
2007-05-02 23:37:55 +02:00
|
|
|
{
|
2011-01-19 19:25:17 +01:00
|
|
|
/** Reject any certificate at least this big; it is probably an overflow, an
|
|
|
|
* attack, a bug, or some other nonsense. */
|
|
|
|
#define MAX_CERT_SIZE (128*1024)
|
|
|
|
|
2008-01-27 00:18:30 +01:00
|
|
|
authority_cert_t *cert = NULL, *old_cert;
|
2007-05-02 23:37:55 +02:00
|
|
|
smartlist_t *tokens = NULL;
|
|
|
|
char digest[DIGEST_LEN];
|
|
|
|
directory_token_t *tok;
|
|
|
|
char fp_declared[DIGEST_LEN];
|
2007-05-22 19:58:25 +02:00
|
|
|
char *eos;
|
2007-05-02 23:37:55 +02:00
|
|
|
size_t len;
|
2007-07-26 22:49:04 +02:00
|
|
|
int found;
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_t *area = NULL;
|
2009-08-20 18:45:03 +02:00
|
|
|
const char *s_dup = s;
|
2007-05-22 19:58:25 +02:00
|
|
|
|
|
|
|
s = eat_whitespace(s);
|
2008-12-03 04:42:19 +01:00
|
|
|
eos = strstr(s, "\ndir-key-certification");
|
|
|
|
if (! eos) {
|
|
|
|
log_warn(LD_DIR, "No signature found on key certificate");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
eos = strstr(eos, "\n-----END SIGNATURE-----\n");
|
2007-05-02 23:37:55 +02:00
|
|
|
if (! eos) {
|
|
|
|
log_warn(LD_DIR, "No end-of-signature found on key certificate");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
eos = strchr(eos+2, '\n');
|
|
|
|
tor_assert(eos);
|
|
|
|
++eos;
|
|
|
|
len = eos - s;
|
|
|
|
|
2011-01-19 19:25:17 +01:00
|
|
|
if (len > MAX_CERT_SIZE) {
|
|
|
|
log_warn(LD_DIR, "Certificate is far too big (at %lu bytes long); "
|
|
|
|
"rejecting", (unsigned long)len);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-01-18 21:53:30 +01:00
|
|
|
tokens = smartlist_new();
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area,s, eos, tokens, dir_key_certificate_table, 0) < 0) {
|
2007-05-02 23:37:55 +02:00
|
|
|
log_warn(LD_DIR, "Error tokenizing key certificate");
|
|
|
|
goto err;
|
|
|
|
}
|
2010-02-25 10:31:36 +01:00
|
|
|
if (router_get_hash_impl(s, strlen(s), digest, "dir-key-certificate-version",
|
2009-08-24 18:51:33 +02:00
|
|
|
"\ndir-key-certification", '\n', DIGEST_SHA1) < 0)
|
2007-05-02 23:37:55 +02:00
|
|
|
goto err;
|
|
|
|
tok = smartlist_get(tokens, 0);
|
|
|
|
if (tok->tp != K_DIR_KEY_CERTIFICATE_VERSION || strcmp(tok->args[0], "3")) {
|
|
|
|
log_warn(LD_DIR,
|
|
|
|
"Key certificate does not begin with a recognized version (3).");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
cert = tor_malloc_zero(sizeof(authority_cert_t));
|
2007-05-22 19:58:25 +02:00
|
|
|
memcpy(cert->cache_info.signed_descriptor_digest, digest, DIGEST_LEN);
|
2007-05-02 23:37:55 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_DIR_SIGNING_KEY);
|
|
|
|
tor_assert(tok->key);
|
2007-05-02 23:37:55 +02:00
|
|
|
cert->signing_key = tok->key;
|
|
|
|
tok->key = NULL;
|
2007-07-26 22:49:04 +02:00
|
|
|
if (crypto_pk_get_digest(cert->signing_key, cert->signing_key_digest))
|
|
|
|
goto err;
|
2007-05-02 23:37:55 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_DIR_IDENTITY_KEY);
|
|
|
|
tor_assert(tok->key);
|
2007-05-02 23:37:55 +02:00
|
|
|
cert->identity_key = tok->key;
|
|
|
|
tok->key = NULL;
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_FINGERPRINT);
|
2008-12-11 21:28:50 +01:00
|
|
|
tor_assert(tok->n_args);
|
2007-05-02 23:37:55 +02:00
|
|
|
if (base16_decode(fp_declared, DIGEST_LEN, tok->args[0],
|
2016-06-17 16:41:45 +02:00
|
|
|
strlen(tok->args[0])) != DIGEST_LEN) {
|
2007-05-02 23:37:55 +02:00
|
|
|
log_warn(LD_DIR, "Couldn't decode key certificate fingerprint %s",
|
|
|
|
escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (crypto_pk_get_digest(cert->identity_key,
|
|
|
|
cert->cache_info.identity_digest))
|
|
|
|
goto err;
|
|
|
|
|
2011-05-10 22:58:38 +02:00
|
|
|
if (tor_memneq(cert->cache_info.identity_digest, fp_declared, DIGEST_LEN)) {
|
2007-05-02 23:37:55 +02:00
|
|
|
log_warn(LD_DIR, "Digest of certificate key didn't match declared "
|
|
|
|
"fingerprint");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_opt_by_keyword(tokens, K_DIR_ADDRESS);
|
2007-10-19 18:28:10 +02:00
|
|
|
if (tok) {
|
2009-02-09 04:13:14 +01:00
|
|
|
struct in_addr in;
|
|
|
|
char *address = NULL;
|
2008-12-11 21:28:50 +01:00
|
|
|
tor_assert(tok->n_args);
|
2016-05-30 22:18:16 +02:00
|
|
|
/* XXX++ use some tor_addr parse function below instead. -RD */
|
2011-10-11 18:02:19 +02:00
|
|
|
if (tor_addr_port_split(LOG_WARN, tok->args[0], &address,
|
|
|
|
&cert->dir_port) < 0 ||
|
2009-02-09 04:13:14 +01:00
|
|
|
tor_inet_aton(address, &in) == 0) {
|
2007-10-19 18:28:10 +02:00
|
|
|
log_warn(LD_DIR, "Couldn't parse dir-address in certificate");
|
2009-02-09 04:13:14 +01:00
|
|
|
tor_free(address);
|
2007-10-19 18:28:10 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2009-02-09 04:13:14 +01:00
|
|
|
cert->addr = ntohl(in.s_addr);
|
|
|
|
tor_free(address);
|
2007-10-19 18:28:10 +02:00
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_DIR_KEY_PUBLISHED);
|
2007-05-02 23:37:55 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &cert->cache_info.published_on) < 0) {
|
|
|
|
goto err;
|
|
|
|
}
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_DIR_KEY_EXPIRES);
|
2007-05-02 23:37:55 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &cert->expires) < 0) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
tok = smartlist_get(tokens, smartlist_len(tokens)-1);
|
|
|
|
if (tok->tp != K_DIR_KEY_CERTIFICATION) {
|
|
|
|
log_warn(LD_DIR, "Certificate didn't end with dir-key-certification.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2007-05-22 19:58:25 +02:00
|
|
|
/* If we already have this cert, don't bother checking the signature. */
|
2008-01-27 00:18:30 +01:00
|
|
|
old_cert = authority_cert_get_by_digests(
|
|
|
|
cert->cache_info.identity_digest,
|
|
|
|
cert->signing_key_digest);
|
2007-07-26 22:49:04 +02:00
|
|
|
found = 0;
|
2008-01-27 00:18:30 +01:00
|
|
|
if (old_cert) {
|
2008-02-06 01:54:47 +01:00
|
|
|
/* XXXX We could just compare signed_descriptor_digest, but that wouldn't
|
|
|
|
* buy us much. */
|
2008-01-27 00:18:30 +01:00
|
|
|
if (old_cert->cache_info.signed_descriptor_len == len &&
|
|
|
|
old_cert->cache_info.signed_descriptor_body &&
|
2011-05-10 22:23:43 +02:00
|
|
|
tor_memeq(s, old_cert->cache_info.signed_descriptor_body, len)) {
|
2008-01-27 00:18:30 +01:00
|
|
|
log_debug(LD_DIR, "We already checked the signature on this "
|
|
|
|
"certificate; no need to do so again.");
|
|
|
|
found = 1;
|
|
|
|
}
|
2007-07-26 22:49:04 +02:00
|
|
|
}
|
|
|
|
if (!found) {
|
2009-09-14 18:02:00 +02:00
|
|
|
if (check_signature_token(digest, DIGEST_LEN, tok, cert->identity_key, 0,
|
2007-05-22 19:58:25 +02:00
|
|
|
"key certificate")) {
|
|
|
|
goto err;
|
|
|
|
}
|
2008-12-12 19:31:39 +01:00
|
|
|
|
2013-11-14 15:37:41 +01:00
|
|
|
tok = find_by_keyword(tokens, K_DIR_KEY_CROSSCERT);
|
|
|
|
if (check_signature_token(cert->cache_info.identity_digest,
|
|
|
|
DIGEST_LEN,
|
|
|
|
tok,
|
|
|
|
cert->signing_key,
|
|
|
|
CST_NO_CHECK_OBJTYPE,
|
|
|
|
"key cross-certification")) {
|
|
|
|
goto err;
|
2008-12-12 19:31:39 +01:00
|
|
|
}
|
2007-05-02 23:37:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
cert->cache_info.signed_descriptor_len = len;
|
|
|
|
cert->cache_info.signed_descriptor_body = tor_malloc(len+1);
|
|
|
|
memcpy(cert->cache_info.signed_descriptor_body, s, len);
|
|
|
|
cert->cache_info.signed_descriptor_body[len] = 0;
|
|
|
|
cert->cache_info.saved_location = SAVED_NOWHERE;
|
2007-05-22 19:58:25 +02:00
|
|
|
|
|
|
|
if (end_of_string) {
|
|
|
|
*end_of_string = eat_whitespace(eos);
|
|
|
|
}
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2007-10-25 15:18:37 +02:00
|
|
|
smartlist_free(tokens);
|
2008-03-26 22:08:39 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "authority cert");
|
|
|
|
memarea_drop_all(area);
|
|
|
|
}
|
2007-05-02 23:37:55 +02:00
|
|
|
return cert;
|
|
|
|
err:
|
2009-08-20 18:45:03 +02:00
|
|
|
dump_desc(s_dup, "authority cert");
|
2007-05-02 23:37:55 +02:00
|
|
|
authority_cert_free(cert);
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2008-03-26 22:08:39 +01:00
|
|
|
smartlist_free(tokens);
|
2008-03-26 18:50:27 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "authority cert");
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_drop_all(area);
|
2008-03-26 18:50:27 +01:00
|
|
|
}
|
2007-05-02 23:37:55 +02:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-09-02 22:37:31 +02:00
|
|
|
/** Helper: given a string <b>s</b>, return the start of the next router-status
|
|
|
|
* object (starting with "r " at the start of a line). If none is found,
|
2010-02-15 02:49:08 +01:00
|
|
|
* return the start of the directory footer, or the next directory signature.
|
|
|
|
* If none is found, return the end of the string. */
|
2015-12-10 16:19:43 +01:00
|
|
|
static inline const char *
|
2005-09-02 22:37:31 +02:00
|
|
|
find_start_of_next_routerstatus(const char *s)
|
|
|
|
{
|
2010-02-27 22:33:46 +01:00
|
|
|
const char *eos, *footer, *sig;
|
|
|
|
if ((eos = strstr(s, "\nr ")))
|
|
|
|
++eos;
|
|
|
|
else
|
|
|
|
eos = s + strlen(s);
|
|
|
|
|
|
|
|
footer = tor_memstr(s, eos-s, "\ndirectory-footer");
|
|
|
|
sig = tor_memstr(s, eos-s, "\ndirectory-signature");
|
|
|
|
|
|
|
|
if (footer && sig)
|
|
|
|
return MIN(footer, sig) + 1;
|
|
|
|
else if (footer)
|
|
|
|
return footer+1;
|
|
|
|
else if (sig)
|
|
|
|
return sig+1;
|
|
|
|
else
|
|
|
|
return eos;
|
2005-09-02 22:37:31 +02:00
|
|
|
}
|
|
|
|
|
2015-01-29 15:57:00 +01:00
|
|
|
/** Parse the GuardFraction string from a consensus or vote.
|
|
|
|
*
|
|
|
|
* If <b>vote</b> or <b>vote_rs</b> are set the document getting
|
|
|
|
* parsed is a vote routerstatus. Otherwise it's a consensus. This is
|
|
|
|
* the same semantic as in routerstatus_parse_entry_from_string(). */
|
|
|
|
STATIC int
|
|
|
|
routerstatus_parse_guardfraction(const char *guardfraction_str,
|
|
|
|
networkstatus_t *vote,
|
|
|
|
vote_routerstatus_t *vote_rs,
|
|
|
|
routerstatus_t *rs)
|
|
|
|
{
|
|
|
|
int ok;
|
|
|
|
const char *end_of_header = NULL;
|
|
|
|
int is_consensus = !vote_rs;
|
|
|
|
uint32_t guardfraction;
|
|
|
|
|
|
|
|
tor_assert(bool_eq(vote, vote_rs));
|
|
|
|
|
|
|
|
/* If this info comes from a consensus, but we should't apply
|
|
|
|
guardfraction, just exit. */
|
|
|
|
if (is_consensus && !should_apply_guardfraction(NULL)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
end_of_header = strchr(guardfraction_str, '=');
|
|
|
|
if (!end_of_header) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
guardfraction = (uint32_t)tor_parse_ulong(end_of_header+1,
|
2015-02-19 01:27:02 +01:00
|
|
|
10, 0, 100, &ok, NULL);
|
2015-01-29 15:57:00 +01:00
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Invalid GuardFraction %s", escaped(guardfraction_str));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2015-01-29 16:49:23 +01:00
|
|
|
log_debug(LD_GENERAL, "[*] Parsed %s guardfraction '%s' for '%s'.",
|
|
|
|
is_consensus ? "consensus" : "vote",
|
|
|
|
guardfraction_str, rs->nickname);
|
2015-01-29 15:57:00 +01:00
|
|
|
|
|
|
|
if (!is_consensus) { /* We are parsing a vote */
|
|
|
|
vote_rs->status.guardfraction_percentage = guardfraction;
|
|
|
|
vote_rs->status.has_guardfraction = 1;
|
|
|
|
} else {
|
|
|
|
/* We are parsing a consensus. Only apply guardfraction to guards. */
|
|
|
|
if (rs->is_possible_guard) {
|
|
|
|
rs->guardfraction_percentage = guardfraction;
|
|
|
|
rs->has_guardfraction = 1;
|
|
|
|
} else {
|
|
|
|
log_warn(LD_BUG, "Got GuardFraction for non-guard %s. "
|
|
|
|
"This is not supposed to happen. Not applying. ", rs->nickname);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-01-24 19:38:01 +01:00
|
|
|
/** Summarize the protocols listed in <b>protocols</b> into <b>out</b>,
|
|
|
|
* falling back or correcting them based on <b>version</b> as appropriate.
|
|
|
|
*/
|
2018-01-31 20:06:37 +01:00
|
|
|
STATIC void
|
2018-01-24 19:38:01 +01:00
|
|
|
summarize_protover_flags(protover_summary_flags_t *out,
|
|
|
|
const char *protocols,
|
|
|
|
const char *version)
|
|
|
|
{
|
|
|
|
tor_assert(out);
|
|
|
|
memset(out, 0, sizeof(*out));
|
|
|
|
if (protocols) {
|
|
|
|
out->protocols_known = 1;
|
|
|
|
out->supports_extend2_cells =
|
|
|
|
protocol_list_supports_protocol(protocols, PRT_RELAY, 2);
|
|
|
|
out->supports_ed25519_link_handshake_compat =
|
|
|
|
protocol_list_supports_protocol(protocols, PRT_LINKAUTH, 3);
|
|
|
|
out->supports_ed25519_link_handshake_any =
|
|
|
|
protocol_list_supports_protocol_or_later(protocols, PRT_LINKAUTH, 3);
|
|
|
|
out->supports_ed25519_hs_intro =
|
|
|
|
protocol_list_supports_protocol(protocols, PRT_HSINTRO, 4);
|
|
|
|
out->supports_v3_hsdir =
|
|
|
|
protocol_list_supports_protocol(protocols, PRT_HSDIR,
|
|
|
|
PROTOVER_HSDIR_V3);
|
|
|
|
out->supports_v3_rendezvous_point =
|
2018-01-31 20:01:49 +01:00
|
|
|
protocol_list_supports_protocol(protocols, PRT_HSREND,
|
2018-01-24 19:38:01 +01:00
|
|
|
PROTOVER_HS_RENDEZVOUS_POINT_V3);
|
|
|
|
}
|
|
|
|
if (version && !strcmpstart(version, "Tor ")) {
|
|
|
|
if (!out->protocols_known) {
|
|
|
|
/* The version is a "Tor" version, and where there is no
|
|
|
|
* list of protocol versions that we should be looking at instead. */
|
|
|
|
|
|
|
|
out->supports_extend2_cells =
|
|
|
|
tor_version_as_new_as(version, "0.2.4.8-alpha");
|
|
|
|
out->protocols_known = 1;
|
|
|
|
} else {
|
|
|
|
/* Bug #22447 forces us to filter on this version. */
|
|
|
|
if (!tor_version_as_new_as(version, "0.3.0.8")) {
|
|
|
|
out->supports_v3_hsdir = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-09-02 22:37:31 +02:00
|
|
|
/** Given a string at *<b>s</b>, containing a routerstatus object, and an
|
|
|
|
* empty smartlist at <b>tokens</b>, parse and return the first router status
|
|
|
|
* object in the string, and advance *<b>s</b> to just after the end of the
|
2007-10-04 18:21:58 +02:00
|
|
|
* router status. Return NULL and advance *<b>s</b> on error.
|
|
|
|
*
|
|
|
|
* If <b>vote</b> and <b>vote_rs</b> are provided, don't allocate a fresh
|
2007-10-19 20:56:24 +02:00
|
|
|
* routerstatus but use <b>vote_rs</b> instead.
|
|
|
|
*
|
|
|
|
* If <b>consensus_method</b> is nonzero, this routerstatus is part of a
|
|
|
|
* consensus, and we should parse it according to the method used to
|
|
|
|
* make that consensus.
|
2009-09-23 21:23:04 +02:00
|
|
|
*
|
2009-10-15 22:06:00 +02:00
|
|
|
* Parse according to the syntax used by the consensus flavor <b>flav</b>.
|
2007-10-04 18:21:58 +02:00
|
|
|
**/
|
2016-09-13 15:45:55 +02:00
|
|
|
STATIC routerstatus_t *
|
2008-03-26 17:33:33 +01:00
|
|
|
routerstatus_parse_entry_from_string(memarea_t *area,
|
|
|
|
const char **s, smartlist_t *tokens,
|
2008-02-05 22:39:29 +01:00
|
|
|
networkstatus_t *vote,
|
2007-10-19 20:56:24 +02:00
|
|
|
vote_routerstatus_t *vote_rs,
|
2009-09-23 21:23:04 +02:00
|
|
|
int consensus_method,
|
|
|
|
consensus_flavor_t flav)
|
2005-09-02 22:37:31 +02:00
|
|
|
{
|
2009-08-20 18:45:03 +02:00
|
|
|
const char *eos, *s_dup = *s;
|
2005-09-02 22:37:31 +02:00
|
|
|
routerstatus_t *rs = NULL;
|
|
|
|
directory_token_t *tok;
|
|
|
|
char timebuf[ISO_TIME_LEN+1];
|
|
|
|
struct in_addr in;
|
2009-09-23 21:23:04 +02:00
|
|
|
int offset = 0;
|
2005-09-02 22:37:31 +02:00
|
|
|
tor_assert(tokens);
|
2007-06-03 05:05:10 +02:00
|
|
|
tor_assert(bool_eq(vote, vote_rs));
|
2005-09-02 22:37:31 +02:00
|
|
|
|
2009-09-23 21:23:04 +02:00
|
|
|
if (!consensus_method)
|
|
|
|
flav = FLAV_NS;
|
2010-09-16 18:55:48 +02:00
|
|
|
tor_assert(flav == FLAV_NS || flav == FLAV_MICRODESC);
|
2009-09-23 21:23:04 +02:00
|
|
|
|
2005-09-02 22:37:31 +02:00
|
|
|
eos = find_start_of_next_routerstatus(*s);
|
|
|
|
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area,*s, eos, tokens, rtrstatus_token_table,0)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Error tokenizing router status");
|
2005-09-02 22:37:31 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (smartlist_len(tokens) < 1) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Impossibly short router status");
|
2005-09-02 22:37:31 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_R);
|
2010-09-16 18:55:48 +02:00
|
|
|
tor_assert(tok->n_args >= 7); /* guaranteed by GE(7) in K_R setup */
|
2009-09-23 21:23:04 +02:00
|
|
|
if (flav == FLAV_NS) {
|
|
|
|
if (tok->n_args < 8) {
|
|
|
|
log_warn(LD_DIR, "Too few arguments to r");
|
|
|
|
goto err;
|
|
|
|
}
|
2010-09-16 18:55:48 +02:00
|
|
|
} else if (flav == FLAV_MICRODESC) {
|
2017-05-10 17:16:07 +02:00
|
|
|
offset = -1; /* There is no descriptor digest in an md consensus r line */
|
2009-09-23 21:23:04 +02:00
|
|
|
}
|
2010-09-16 18:55:48 +02:00
|
|
|
|
2007-06-03 05:05:10 +02:00
|
|
|
if (vote_rs) {
|
|
|
|
rs = &vote_rs->status;
|
|
|
|
} else {
|
|
|
|
rs = tor_malloc_zero(sizeof(routerstatus_t));
|
|
|
|
}
|
2005-09-02 22:37:31 +02:00
|
|
|
|
|
|
|
if (!is_legal_nickname(tok->args[0])) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR,
|
2006-03-05 10:50:26 +01:00
|
|
|
"Invalid nickname %s in router status; skipping.",
|
|
|
|
escaped(tok->args[0]));
|
2005-09-02 22:37:31 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
strlcpy(rs->nickname, tok->args[0], sizeof(rs->nickname));
|
|
|
|
|
2005-09-18 04:22:21 +02:00
|
|
|
if (digest_from_base64(rs->identity_digest, tok->args[1])) {
|
2006-09-30 00:51:47 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding identity digest %s",
|
|
|
|
escaped(tok->args[1]));
|
2005-09-02 22:37:31 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-09-23 21:23:04 +02:00
|
|
|
if (flav == FLAV_NS) {
|
|
|
|
if (digest_from_base64(rs->descriptor_digest, tok->args[2])) {
|
|
|
|
log_warn(LD_DIR, "Error decoding descriptor digest %s",
|
|
|
|
escaped(tok->args[2]));
|
|
|
|
goto err;
|
|
|
|
}
|
2005-09-02 22:37:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tor_snprintf(timebuf, sizeof(timebuf), "%s %s",
|
2009-09-23 21:23:04 +02:00
|
|
|
tok->args[3+offset], tok->args[4+offset]) < 0 ||
|
2005-09-02 22:37:31 +02:00
|
|
|
parse_iso_time(timebuf, &rs->published_on)<0) {
|
2009-09-23 21:23:04 +02:00
|
|
|
log_warn(LD_DIR, "Error parsing time '%s %s' [%d %d]",
|
|
|
|
tok->args[3+offset], tok->args[4+offset],
|
|
|
|
offset, (int)flav);
|
2005-09-02 22:37:31 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-09-23 21:23:04 +02:00
|
|
|
if (tor_inet_aton(tok->args[5+offset], &in) == 0) {
|
2006-09-30 00:33:40 +02:00
|
|
|
log_warn(LD_DIR, "Error parsing router address in network-status %s",
|
2009-09-23 21:23:04 +02:00
|
|
|
escaped(tok->args[5+offset]));
|
2005-09-02 22:37:31 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
rs->addr = ntohl(in.s_addr);
|
|
|
|
|
2009-09-23 21:23:04 +02:00
|
|
|
rs->or_port = (uint16_t) tor_parse_long(tok->args[6+offset],
|
|
|
|
10,0,65535,NULL,NULL);
|
|
|
|
rs->dir_port = (uint16_t) tor_parse_long(tok->args[7+offset],
|
|
|
|
10,0,65535,NULL,NULL);
|
2005-09-02 22:37:31 +02:00
|
|
|
|
2012-08-14 14:03:58 +02:00
|
|
|
{
|
|
|
|
smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
|
|
|
|
if (a_lines) {
|
|
|
|
find_single_ipv6_orport(a_lines, &rs->ipv6_addr, &rs->ipv6_orport);
|
|
|
|
smartlist_free(a_lines);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_opt_by_keyword(tokens, K_S);
|
2007-05-31 21:03:44 +02:00
|
|
|
if (tok && vote) {
|
2007-06-08 20:41:09 +02:00
|
|
|
int i;
|
2007-06-03 05:05:10 +02:00
|
|
|
vote_rs->flags = 0;
|
2007-05-31 21:03:44 +02:00
|
|
|
for (i=0; i < tok->n_args; ++i) {
|
2007-06-08 20:41:09 +02:00
|
|
|
int p = smartlist_string_pos(vote->known_flags, tok->args[i]);
|
|
|
|
if (p >= 0) {
|
2012-09-17 16:24:52 +02:00
|
|
|
vote_rs->flags |= (U64_LITERAL(1)<<p);
|
2007-06-08 20:41:12 +02:00
|
|
|
} else {
|
|
|
|
log_warn(LD_DIR, "Flags line had a flag %s not listed in known_flags.",
|
|
|
|
escaped(tok->args[i]));
|
|
|
|
goto err;
|
2007-05-31 21:03:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (tok) {
|
2016-08-26 20:07:53 +02:00
|
|
|
/* This is a consensus, not a vote. */
|
2005-09-02 22:37:31 +02:00
|
|
|
int i;
|
|
|
|
for (i=0; i < tok->n_args; ++i) {
|
|
|
|
if (!strcmp(tok->args[i], "Exit"))
|
|
|
|
rs->is_exit = 1;
|
|
|
|
else if (!strcmp(tok->args[i], "Stable"))
|
|
|
|
rs->is_stable = 1;
|
|
|
|
else if (!strcmp(tok->args[i], "Fast"))
|
|
|
|
rs->is_fast = 1;
|
|
|
|
else if (!strcmp(tok->args[i], "Running"))
|
2010-09-30 20:58:27 +02:00
|
|
|
rs->is_flagged_running = 1;
|
2005-09-02 22:37:31 +02:00
|
|
|
else if (!strcmp(tok->args[i], "Named"))
|
|
|
|
rs->is_named = 1;
|
|
|
|
else if (!strcmp(tok->args[i], "Valid"))
|
|
|
|
rs->is_valid = 1;
|
2006-01-24 01:31:16 +01:00
|
|
|
else if (!strcmp(tok->args[i], "Guard"))
|
|
|
|
rs->is_possible_guard = 1;
|
2006-10-12 00:06:01 +02:00
|
|
|
else if (!strcmp(tok->args[i], "BadExit"))
|
|
|
|
rs->is_bad_exit = 1;
|
2007-06-13 21:06:23 +02:00
|
|
|
else if (!strcmp(tok->args[i], "Authority"))
|
|
|
|
rs->is_authority = 1;
|
2007-10-19 20:56:24 +02:00
|
|
|
else if (!strcmp(tok->args[i], "Unnamed") &&
|
|
|
|
consensus_method >= 2) {
|
|
|
|
/* Unnamed is computed right by consensus method 2 and later. */
|
|
|
|
rs->is_unnamed = 1;
|
2007-10-29 20:10:42 +01:00
|
|
|
} else if (!strcmp(tok->args[i], "HSDir")) {
|
|
|
|
rs->is_hs_dir = 1;
|
2014-10-28 18:12:52 +01:00
|
|
|
} else if (!strcmp(tok->args[i], "V2Dir")) {
|
|
|
|
rs->is_v2_dir = 1;
|
2007-10-19 20:56:24 +02:00
|
|
|
}
|
2006-12-24 05:09:48 +01:00
|
|
|
}
|
2016-08-26 20:07:53 +02:00
|
|
|
/* These are implied true by having been included in a consensus made
|
|
|
|
* with a given method */
|
|
|
|
rs->is_flagged_running = 1; /* Starting with consensus method 4. */
|
2018-04-09 21:07:51 +02:00
|
|
|
rs->is_valid = 1; /* Starting with consensus method 24. */
|
2006-12-24 05:09:48 +01:00
|
|
|
}
|
2018-01-24 19:38:01 +01:00
|
|
|
{
|
|
|
|
const char *protocols = NULL, *version = NULL;
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_PROTO))) {
|
|
|
|
tor_assert(tok->n_args == 1);
|
|
|
|
protocols = tok->args[0];
|
2005-09-02 22:37:31 +02:00
|
|
|
}
|
2018-01-24 19:38:01 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_V))) {
|
|
|
|
tor_assert(tok->n_args == 1);
|
|
|
|
version = tok->args[0];
|
|
|
|
if (vote_rs) {
|
|
|
|
vote_rs->version = tor_strdup(tok->args[0]);
|
2017-08-03 15:08:17 +02:00
|
|
|
}
|
2017-07-14 22:37:13 +02:00
|
|
|
}
|
2018-01-24 19:38:01 +01:00
|
|
|
|
|
|
|
summarize_protover_flags(&rs->pv, protocols, version);
|
2005-09-02 22:37:31 +02:00
|
|
|
}
|
|
|
|
|
2008-08-15 01:00:44 +02:00
|
|
|
/* handle weighting/bandwidth info */
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_W))) {
|
2008-08-15 01:00:44 +02:00
|
|
|
int i;
|
|
|
|
for (i=0; i < tok->n_args; ++i) {
|
|
|
|
if (!strcmpstart(tok->args[i], "Bandwidth=")) {
|
|
|
|
int ok;
|
2013-04-11 17:43:40 +02:00
|
|
|
rs->bandwidth_kb =
|
|
|
|
(uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
|
|
|
|
10, 0, UINT32_MAX,
|
|
|
|
&ok, NULL);
|
2008-08-15 01:00:44 +02:00
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Invalid Bandwidth %s", escaped(tok->args[i]));
|
|
|
|
goto err;
|
|
|
|
}
|
2008-08-15 01:00:57 +02:00
|
|
|
rs->has_bandwidth = 1;
|
Refactor storing of measured_bw versus Unmeasured=1.
This patch moves the measured_bw field and the has_measured_bw field
into vote_routerstatus_t, since only votes have 'Measured=XX' set on
their weight line.
I also added a new bw_is_unmeasured flag to routerstatus_t to
represent the Unmeasured=1 flag on a w line. Previously, I was using
has_measured_bw for this, which was quite incorrect: has_measured_bw
means that the measured_bw field is set, and it's probably a mistake
to have it serve double duty as meaning that 'baandwidth' represents a
measured value.
While making this change,I also found a harmless but stupid bug in
dirserv_read_measured_bandwidths: It assumes that it's getting a
smartlist of routerstatus_t, when really it's getting a smartlist of
vote_routerstatus_t. C's struct layout rules mean that we could never
actually get an error because of that, but it's still quite incorrect.
I fixed that, and in the process needed to add two more sorting and
searching helpers.
Finally, I made the Unmeasured=1 flag get parsed. We don't use it for
anything yet, but someday we might.
This isn't complete yet -- the new 2286 unit test doesn't build.
2013-02-19 16:39:27 +01:00
|
|
|
} else if (!strcmpstart(tok->args[i], "Measured=") && vote_rs) {
|
2009-07-31 06:33:53 +02:00
|
|
|
int ok;
|
2013-04-11 17:43:40 +02:00
|
|
|
vote_rs->measured_bw_kb =
|
2009-09-01 05:23:47 +02:00
|
|
|
(uint32_t)tor_parse_ulong(strchr(tok->args[i], '=')+1,
|
|
|
|
10, 0, UINT32_MAX, &ok, NULL);
|
2009-07-31 06:33:53 +02:00
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Invalid Measured Bandwidth %s",
|
|
|
|
escaped(tok->args[i]));
|
|
|
|
goto err;
|
|
|
|
}
|
Refactor storing of measured_bw versus Unmeasured=1.
This patch moves the measured_bw field and the has_measured_bw field
into vote_routerstatus_t, since only votes have 'Measured=XX' set on
their weight line.
I also added a new bw_is_unmeasured flag to routerstatus_t to
represent the Unmeasured=1 flag on a w line. Previously, I was using
has_measured_bw for this, which was quite incorrect: has_measured_bw
means that the measured_bw field is set, and it's probably a mistake
to have it serve double duty as meaning that 'baandwidth' represents a
measured value.
While making this change,I also found a harmless but stupid bug in
dirserv_read_measured_bandwidths: It assumes that it's getting a
smartlist of routerstatus_t, when really it's getting a smartlist of
vote_routerstatus_t. C's struct layout rules mean that we could never
actually get an error because of that, but it's still quite incorrect.
I fixed that, and in the process needed to add two more sorting and
searching helpers.
Finally, I made the Unmeasured=1 flag get parsed. We don't use it for
anything yet, but someday we might.
This isn't complete yet -- the new 2286 unit test doesn't build.
2013-02-19 16:39:27 +01:00
|
|
|
vote_rs->has_measured_bw = 1;
|
2012-05-22 15:48:10 +02:00
|
|
|
vote->has_measured_bws = 1;
|
Refactor storing of measured_bw versus Unmeasured=1.
This patch moves the measured_bw field and the has_measured_bw field
into vote_routerstatus_t, since only votes have 'Measured=XX' set on
their weight line.
I also added a new bw_is_unmeasured flag to routerstatus_t to
represent the Unmeasured=1 flag on a w line. Previously, I was using
has_measured_bw for this, which was quite incorrect: has_measured_bw
means that the measured_bw field is set, and it's probably a mistake
to have it serve double duty as meaning that 'baandwidth' represents a
measured value.
While making this change,I also found a harmless but stupid bug in
dirserv_read_measured_bandwidths: It assumes that it's getting a
smartlist of routerstatus_t, when really it's getting a smartlist of
vote_routerstatus_t. C's struct layout rules mean that we could never
actually get an error because of that, but it's still quite incorrect.
I fixed that, and in the process needed to add two more sorting and
searching helpers.
Finally, I made the Unmeasured=1 flag get parsed. We don't use it for
anything yet, but someday we might.
This isn't complete yet -- the new 2286 unit test doesn't build.
2013-02-19 16:39:27 +01:00
|
|
|
} else if (!strcmpstart(tok->args[i], "Unmeasured=1")) {
|
|
|
|
rs->bw_is_unmeasured = 1;
|
2015-01-29 15:57:00 +01:00
|
|
|
} else if (!strcmpstart(tok->args[i], "GuardFraction=")) {
|
|
|
|
if (routerstatus_parse_guardfraction(tok->args[i],
|
|
|
|
vote, vote_rs, rs) < 0) {
|
|
|
|
goto err;
|
|
|
|
}
|
2008-08-15 01:00:44 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse exit policy summaries */
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_P))) {
|
2008-08-15 01:01:21 +02:00
|
|
|
tor_assert(tok->n_args == 1);
|
|
|
|
if (strcmpstart(tok->args[0], "accept ") &&
|
|
|
|
strcmpstart(tok->args[0], "reject ")) {
|
2009-04-09 21:58:16 +02:00
|
|
|
log_warn(LD_DIR, "Unknown exit policy summary type %s.",
|
2008-08-15 01:00:44 +02:00
|
|
|
escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
2008-08-15 01:01:21 +02:00
|
|
|
/* XXX weasel: parse this into ports and represent them somehow smart,
|
|
|
|
* maybe not here but somewhere on if we need it for the client.
|
|
|
|
* we should still parse it here to check it's valid tho.
|
|
|
|
*/
|
|
|
|
rs->exitsummary = tor_strdup(tok->args[0]);
|
2008-08-15 01:00:57 +02:00
|
|
|
rs->has_exitsummary = 1;
|
2008-08-15 01:00:44 +02:00
|
|
|
}
|
|
|
|
|
2009-08-20 17:39:22 +02:00
|
|
|
if (vote_rs) {
|
|
|
|
SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, t) {
|
|
|
|
if (t->tp == K_M && t->n_args) {
|
|
|
|
vote_microdesc_hash_t *line =
|
|
|
|
tor_malloc(sizeof(vote_microdesc_hash_t));
|
|
|
|
line->next = vote_rs->microdesc;
|
|
|
|
line->microdesc_hash_line = tor_strdup(t->args[0]);
|
|
|
|
vote_rs->microdesc = line;
|
|
|
|
}
|
2014-10-29 18:36:21 +01:00
|
|
|
if (t->tp == K_ID) {
|
|
|
|
tor_assert(t->n_args >= 2);
|
|
|
|
if (!strcmp(t->args[0], "ed25519")) {
|
|
|
|
vote_rs->has_ed25519_listing = 1;
|
|
|
|
if (strcmp(t->args[1], "none") &&
|
2015-06-01 14:59:14 +02:00
|
|
|
digest256_from_base64((char*)vote_rs->ed25519_id,
|
|
|
|
t->args[1])<0) {
|
2014-10-29 18:36:21 +01:00
|
|
|
log_warn(LD_DIR, "Bogus ed25519 key in networkstatus vote");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-08-26 18:33:23 +02:00
|
|
|
if (t->tp == K_PROTO) {
|
|
|
|
tor_assert(t->n_args == 1);
|
|
|
|
vote_rs->protocols = tor_strdup(t->args[0]);
|
|
|
|
}
|
2009-08-20 17:39:22 +02:00
|
|
|
} SMARTLIST_FOREACH_END(t);
|
2010-09-16 18:55:48 +02:00
|
|
|
} else if (flav == FLAV_MICRODESC) {
|
|
|
|
tok = find_opt_by_keyword(tokens, K_M);
|
|
|
|
if (tok) {
|
|
|
|
tor_assert(tok->n_args);
|
|
|
|
if (digest256_from_base64(rs->descriptor_digest, tok->args[0])) {
|
|
|
|
log_warn(LD_DIR, "Error decoding microdescriptor digest %s",
|
|
|
|
escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
2012-07-31 17:00:18 +02:00
|
|
|
} else {
|
|
|
|
log_info(LD_BUG, "Found an entry in networkstatus with no "
|
2013-03-27 02:49:41 +01:00
|
|
|
"microdescriptor digest. (Router %s ($%s) at %s:%d.)",
|
2012-07-31 17:00:18 +02:00
|
|
|
rs->nickname, hex_str(rs->identity_digest, DIGEST_LEN),
|
|
|
|
fmt_addr32(rs->addr), rs->or_port);
|
2010-09-16 18:55:48 +02:00
|
|
|
}
|
2009-08-20 17:39:22 +02:00
|
|
|
}
|
|
|
|
|
2006-09-29 06:51:28 +02:00
|
|
|
if (!strcasecmp(rs->nickname, UNNAMED_ROUTER_NICKNAME))
|
|
|
|
rs->is_named = 0;
|
|
|
|
|
2005-09-02 22:37:31 +02:00
|
|
|
goto done;
|
|
|
|
err:
|
2009-08-20 18:45:03 +02:00
|
|
|
dump_desc(s_dup, "routerstatus entry");
|
2007-06-03 05:05:10 +02:00
|
|
|
if (rs && !vote_rs)
|
2005-09-02 22:37:31 +02:00
|
|
|
routerstatus_free(rs);
|
|
|
|
rs = NULL;
|
|
|
|
done:
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2005-09-02 22:37:31 +02:00
|
|
|
smartlist_clear(tokens);
|
2008-03-26 18:50:27 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "routerstatus entry");
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_clear(area);
|
2008-03-26 18:50:27 +01:00
|
|
|
}
|
2005-09-02 22:37:31 +02:00
|
|
|
*s = eos;
|
|
|
|
|
|
|
|
return rs;
|
|
|
|
}
|
|
|
|
|
Refactor storing of measured_bw versus Unmeasured=1.
This patch moves the measured_bw field and the has_measured_bw field
into vote_routerstatus_t, since only votes have 'Measured=XX' set on
their weight line.
I also added a new bw_is_unmeasured flag to routerstatus_t to
represent the Unmeasured=1 flag on a w line. Previously, I was using
has_measured_bw for this, which was quite incorrect: has_measured_bw
means that the measured_bw field is set, and it's probably a mistake
to have it serve double duty as meaning that 'baandwidth' represents a
measured value.
While making this change,I also found a harmless but stupid bug in
dirserv_read_measured_bandwidths: It assumes that it's getting a
smartlist of routerstatus_t, when really it's getting a smartlist of
vote_routerstatus_t. C's struct layout rules mean that we could never
actually get an error because of that, but it's still quite incorrect.
I fixed that, and in the process needed to add two more sorting and
searching helpers.
Finally, I made the Unmeasured=1 flag get parsed. We don't use it for
anything yet, but someday we might.
This isn't complete yet -- the new 2286 unit test doesn't build.
2013-02-19 16:39:27 +01:00
|
|
|
int
|
|
|
|
compare_vote_routerstatus_entries(const void **_a, const void **_b)
|
|
|
|
{
|
|
|
|
const vote_routerstatus_t *a = *_a, *b = *_b;
|
|
|
|
return fast_memcmp(a->status.identity_digest, b->status.identity_digest,
|
|
|
|
DIGEST_LEN);
|
|
|
|
}
|
|
|
|
|
2010-01-30 00:40:40 +01:00
|
|
|
/** Verify the bandwidth weights of a network status document */
|
|
|
|
int
|
2013-03-26 22:15:58 +01:00
|
|
|
networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
|
2010-01-30 00:40:40 +01:00
|
|
|
{
|
|
|
|
int64_t G=0, M=0, E=0, D=0, T=0;
|
|
|
|
double Wgg, Wgm, Wgd, Wmg, Wmm, Wme, Wmd, Weg, Wem, Wee, Wed;
|
|
|
|
double Gtotal=0, Mtotal=0, Etotal=0;
|
|
|
|
const char *casename = NULL;
|
|
|
|
int valid = 1;
|
2014-08-16 00:11:26 +02:00
|
|
|
(void) consensus_method;
|
2010-01-30 00:40:40 +01:00
|
|
|
|
2017-07-28 16:17:14 +02:00
|
|
|
const int64_t weight_scale = networkstatus_get_weight_scale_param(ns);
|
|
|
|
tor_assert(weight_scale >= 1);
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg = networkstatus_get_bw_weight(ns, "Wgg", -1);
|
|
|
|
Wgm = networkstatus_get_bw_weight(ns, "Wgm", -1);
|
|
|
|
Wgd = networkstatus_get_bw_weight(ns, "Wgd", -1);
|
|
|
|
Wmg = networkstatus_get_bw_weight(ns, "Wmg", -1);
|
|
|
|
Wmm = networkstatus_get_bw_weight(ns, "Wmm", -1);
|
|
|
|
Wme = networkstatus_get_bw_weight(ns, "Wme", -1);
|
|
|
|
Wmd = networkstatus_get_bw_weight(ns, "Wmd", -1);
|
|
|
|
Weg = networkstatus_get_bw_weight(ns, "Weg", -1);
|
|
|
|
Wem = networkstatus_get_bw_weight(ns, "Wem", -1);
|
|
|
|
Wee = networkstatus_get_bw_weight(ns, "Wee", -1);
|
|
|
|
Wed = networkstatus_get_bw_weight(ns, "Wed", -1);
|
|
|
|
|
|
|
|
if (Wgg<0 || Wgm<0 || Wgd<0 || Wmg<0 || Wmm<0 || Wme<0 || Wmd<0 || Weg<0
|
|
|
|
|| Wem<0 || Wee<0 || Wed<0) {
|
|
|
|
log_warn(LD_BUG, "No bandwidth weights produced in consensus!");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First, sanity check basic summing properties that hold for all cases
|
|
|
|
// We use > 1 as the check for these because they are computed as integers.
|
|
|
|
// Sometimes there are rounding errors.
|
|
|
|
if (fabs(Wmm - weight_scale) > 1) {
|
2011-08-31 02:42:51 +02:00
|
|
|
log_warn(LD_BUG, "Wmm=%f != "I64_FORMAT,
|
2010-02-27 22:33:22 +01:00
|
|
|
Wmm, I64_PRINTF_ARG(weight_scale));
|
2010-01-30 00:40:40 +01:00
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(Wem - Wee) > 1) {
|
2011-08-31 02:42:51 +02:00
|
|
|
log_warn(LD_BUG, "Wem=%f != Wee=%f", Wem, Wee);
|
2010-01-30 00:40:40 +01:00
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(Wgm - Wgg) > 1) {
|
2011-08-31 02:42:51 +02:00
|
|
|
log_warn(LD_BUG, "Wgm=%f != Wgg=%f", Wgm, Wgg);
|
2010-01-30 00:40:40 +01:00
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(Weg - Wed) > 1) {
|
2011-08-31 02:42:51 +02:00
|
|
|
log_warn(LD_BUG, "Wed=%f != Weg=%f", Wed, Weg);
|
2010-01-30 00:40:40 +01:00
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(Wgg + Wmg - weight_scale) > 0.001*weight_scale) {
|
2011-08-31 02:42:51 +02:00
|
|
|
log_warn(LD_BUG, "Wgg=%f != "I64_FORMAT" - Wmg=%f", Wgg,
|
2010-02-25 22:22:40 +01:00
|
|
|
I64_PRINTF_ARG(weight_scale), Wmg);
|
2010-01-30 00:40:40 +01:00
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(Wee + Wme - weight_scale) > 0.001*weight_scale) {
|
2011-08-31 02:42:51 +02:00
|
|
|
log_warn(LD_BUG, "Wee=%f != "I64_FORMAT" - Wme=%f", Wee,
|
2010-02-25 22:22:40 +01:00
|
|
|
I64_PRINTF_ARG(weight_scale), Wme);
|
2010-01-30 00:40:40 +01:00
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fabs(Wgd + Wmd + Wed - weight_scale) > 0.001*weight_scale) {
|
2011-08-31 02:42:51 +02:00
|
|
|
log_warn(LD_BUG, "Wgd=%f + Wmd=%f + Wed=%f != "I64_FORMAT,
|
2010-02-25 22:22:40 +01:00
|
|
|
Wgd, Wmd, Wed, I64_PRINTF_ARG(weight_scale));
|
2010-01-30 00:40:40 +01:00
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Wgg /= weight_scale;
|
2017-09-12 23:20:09 +02:00
|
|
|
Wgm /= weight_scale; (void) Wgm; // unused from here on.
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgd /= weight_scale;
|
|
|
|
|
|
|
|
Wmg /= weight_scale;
|
|
|
|
Wmm /= weight_scale;
|
|
|
|
Wme /= weight_scale;
|
|
|
|
Wmd /= weight_scale;
|
|
|
|
|
2017-09-12 23:20:09 +02:00
|
|
|
Weg /= weight_scale; (void) Weg; // unused from here on.
|
|
|
|
Wem /= weight_scale; (void) Wem; // unused from here on.
|
2010-01-30 00:40:40 +01:00
|
|
|
Wee /= weight_scale;
|
|
|
|
Wed /= weight_scale;
|
|
|
|
|
|
|
|
// Then, gather G, M, E, D, T to determine case
|
|
|
|
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, routerstatus_t *, rs) {
|
2013-03-26 22:15:58 +01:00
|
|
|
int is_exit = 0;
|
2014-08-16 00:11:26 +02:00
|
|
|
/* Bug #2203: Don't count bad exits as exits for balancing */
|
|
|
|
is_exit = rs->is_exit && !rs->is_bad_exit;
|
2010-01-30 00:40:40 +01:00
|
|
|
if (rs->has_bandwidth) {
|
2013-04-11 17:43:40 +02:00
|
|
|
T += rs->bandwidth_kb;
|
2013-03-26 22:15:58 +01:00
|
|
|
if (is_exit && rs->is_possible_guard) {
|
2013-04-11 17:43:40 +02:00
|
|
|
D += rs->bandwidth_kb;
|
|
|
|
Gtotal += Wgd*rs->bandwidth_kb;
|
|
|
|
Mtotal += Wmd*rs->bandwidth_kb;
|
|
|
|
Etotal += Wed*rs->bandwidth_kb;
|
2013-03-26 22:15:58 +01:00
|
|
|
} else if (is_exit) {
|
2013-04-11 17:43:40 +02:00
|
|
|
E += rs->bandwidth_kb;
|
|
|
|
Mtotal += Wme*rs->bandwidth_kb;
|
|
|
|
Etotal += Wee*rs->bandwidth_kb;
|
2010-01-30 00:40:40 +01:00
|
|
|
} else if (rs->is_possible_guard) {
|
2013-04-11 17:43:40 +02:00
|
|
|
G += rs->bandwidth_kb;
|
|
|
|
Gtotal += Wgg*rs->bandwidth_kb;
|
|
|
|
Mtotal += Wmg*rs->bandwidth_kb;
|
2010-01-30 00:40:40 +01:00
|
|
|
} else {
|
2013-04-11 17:43:40 +02:00
|
|
|
M += rs->bandwidth_kb;
|
|
|
|
Mtotal += Wmm*rs->bandwidth_kb;
|
2010-01-30 00:40:40 +01:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
log_warn(LD_BUG, "Missing consensus bandwidth for router %s",
|
2011-05-16 03:58:46 +02:00
|
|
|
routerstatus_describe(rs));
|
2010-01-30 00:40:40 +01:00
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(rs);
|
|
|
|
|
|
|
|
// Finally, check equality conditions depending upon case 1, 2 or 3
|
|
|
|
// Full equality cases: 1, 3b
|
|
|
|
// Partial equality cases: 2b (E=G), 3a (M=E)
|
|
|
|
// Fully unknown: 2a
|
|
|
|
if (3*E >= T && 3*G >= T) {
|
|
|
|
// Case 1: Neither are scarce
|
|
|
|
casename = "Case 1";
|
|
|
|
if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Etotal, Mtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Etotal, Gtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Mtotal, Gtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
} else if (3*E < T && 3*G < T) {
|
|
|
|
int64_t R = MIN(E, G);
|
|
|
|
int64_t S = MAX(E, G);
|
|
|
|
/*
|
|
|
|
* Case 2: Both Guards and Exits are scarce
|
|
|
|
* Balance D between E and G, depending upon
|
|
|
|
* D capacity and scarcity. Devote no extra
|
|
|
|
* bandwidth to middle nodes.
|
|
|
|
*/
|
|
|
|
if (R+D < S) { // Subcase a
|
|
|
|
double Rtotal, Stotal;
|
|
|
|
if (E < G) {
|
|
|
|
Rtotal = Etotal;
|
|
|
|
Stotal = Gtotal;
|
|
|
|
} else {
|
|
|
|
Rtotal = Gtotal;
|
|
|
|
Stotal = Etotal;
|
|
|
|
}
|
|
|
|
casename = "Case 2a";
|
|
|
|
// Rtotal < Stotal
|
|
|
|
if (Rtotal > Stotal) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Rtotal %f > Stotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Rtotal, Stotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
// Rtotal < T/3
|
|
|
|
if (3*Rtotal > T) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: 3*Rtotal %f > T "
|
2010-01-30 00:40:40 +01:00
|
|
|
I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
|
|
|
|
" D="I64_FORMAT" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Rtotal*3, I64_PRINTF_ARG(T),
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
// Stotal < T/3
|
|
|
|
if (3*Stotal > T) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: 3*Stotal %f > T "
|
2010-01-30 00:40:40 +01:00
|
|
|
I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
|
|
|
|
" D="I64_FORMAT" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Stotal*3, I64_PRINTF_ARG(T),
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
// Mtotal > T/3
|
|
|
|
if (3*Mtotal < T) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: 3*Mtotal %f < T "
|
2010-01-30 00:40:40 +01:00
|
|
|
I64_FORMAT". "
|
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Mtotal*3, I64_PRINTF_ARG(T),
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
} else { // Subcase b: R+D > S
|
|
|
|
casename = "Case 2b";
|
|
|
|
|
|
|
|
/* Check the rare-M redirect case. */
|
|
|
|
if (D != 0 && 3*M < T) {
|
|
|
|
casename = "Case 2b (balanced)";
|
|
|
|
if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Etotal, Mtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Etotal, Gtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Mtotal, Gtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Etotal, Gtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // if (E < T/3 || G < T/3) {
|
|
|
|
int64_t S = MIN(E, G);
|
|
|
|
int64_t NS = MAX(E, G);
|
|
|
|
if (3*(S+D) < T) { // Subcase a:
|
|
|
|
double Stotal;
|
|
|
|
double NStotal;
|
|
|
|
if (G < E) {
|
|
|
|
casename = "Case 3a (G scarce)";
|
|
|
|
Stotal = Gtotal;
|
|
|
|
NStotal = Etotal;
|
|
|
|
} else { // if (G >= E) {
|
|
|
|
casename = "Case 3a (E scarce)";
|
|
|
|
NStotal = Gtotal;
|
|
|
|
Stotal = Etotal;
|
|
|
|
}
|
|
|
|
// Stotal < T/3
|
|
|
|
if (3*Stotal > T) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: 3*Stotal %f > T "
|
2010-01-30 00:40:40 +01:00
|
|
|
I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT
|
|
|
|
" D="I64_FORMAT" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Stotal*3, I64_PRINTF_ARG(T),
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
if (NS >= M) {
|
|
|
|
if (fabs(NStotal-Mtotal) > 0.01*MAX(NStotal,Mtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: NStotal %f != Mtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, NStotal, Mtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// if NS < M, NStotal > T/3 because only one of G or E is scarce
|
|
|
|
if (3*NStotal < T) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: 3*NStotal %f < T "
|
2010-01-30 00:40:40 +01:00
|
|
|
I64_FORMAT". G="I64_FORMAT" M="I64_FORMAT
|
|
|
|
" E="I64_FORMAT" D="I64_FORMAT" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, NStotal*3, I64_PRINTF_ARG(T),
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else { // Subcase b: S+D >= T/3
|
|
|
|
casename = "Case 3b";
|
|
|
|
if (fabs(Etotal-Mtotal) > 0.01*MAX(Etotal,Mtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Etotal %f != Mtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Etotal, Mtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
if (fabs(Etotal-Gtotal) > 0.01*MAX(Etotal,Gtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Etotal %f != Gtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Etotal, Gtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
if (fabs(Gtotal-Mtotal) > 0.01*MAX(Gtotal,Mtotal)) {
|
|
|
|
log_warn(LD_DIR,
|
2011-08-31 02:42:51 +02:00
|
|
|
"Bw Weight Failure for %s: Mtotal %f != Gtotal %f. "
|
2010-01-30 00:40:40 +01:00
|
|
|
"G="I64_FORMAT" M="I64_FORMAT" E="I64_FORMAT" D="I64_FORMAT
|
|
|
|
" T="I64_FORMAT". "
|
2011-08-31 02:42:51 +02:00
|
|
|
"Wgg=%f Wgd=%f Wmg=%f Wme=%f Wmd=%f Wee=%f Wed=%f",
|
2010-02-25 22:22:40 +01:00
|
|
|
casename, Mtotal, Gtotal,
|
|
|
|
I64_PRINTF_ARG(G), I64_PRINTF_ARG(M), I64_PRINTF_ARG(E),
|
|
|
|
I64_PRINTF_ARG(D), I64_PRINTF_ARG(T),
|
2010-01-30 00:40:40 +01:00
|
|
|
Wgg, Wgd, Wmg, Wme, Wmd, Wee, Wed);
|
|
|
|
valid = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (valid)
|
|
|
|
log_notice(LD_DIR, "Bandwidth-weight %s is verified and valid.",
|
|
|
|
casename);
|
|
|
|
|
|
|
|
return valid;
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:36:09 +02:00
|
|
|
/** Check if a shared random value of type <b>srv_type</b> is in
|
|
|
|
* <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
|
|
|
|
* -1 on failure, 0 on success. The resulting srv is allocated on the heap and
|
|
|
|
* it's the responsibility of the caller to free it. */
|
|
|
|
static int
|
|
|
|
extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
|
|
|
|
sr_srv_t **srv_out)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
directory_token_t *tok;
|
|
|
|
sr_srv_t *srv = NULL;
|
|
|
|
smartlist_t *chunks;
|
|
|
|
|
|
|
|
tor_assert(tokens);
|
|
|
|
|
|
|
|
chunks = smartlist_new();
|
|
|
|
tok = find_opt_by_keyword(tokens, srv_type);
|
|
|
|
if (!tok) {
|
|
|
|
/* That's fine, no SRV is allowed. */
|
|
|
|
ret = 0;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
for (int i = 0; i < tok->n_args; i++) {
|
|
|
|
smartlist_add(chunks, tok->args[i]);
|
|
|
|
}
|
|
|
|
srv = sr_parse_srv(chunks);
|
|
|
|
if (srv == NULL) {
|
|
|
|
log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
/* All is good. */
|
|
|
|
*srv_out = srv;
|
|
|
|
ret = 0;
|
|
|
|
end:
|
|
|
|
smartlist_free(chunks);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Extract any shared random values found in <b>tokens</b> and place them in
|
|
|
|
* the networkstatus <b>ns</b>. */
|
|
|
|
static void
|
|
|
|
extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
|
|
|
|
{
|
|
|
|
const char *voter_identity;
|
|
|
|
networkstatus_voter_info_t *voter;
|
|
|
|
|
|
|
|
tor_assert(ns);
|
|
|
|
tor_assert(tokens);
|
|
|
|
/* Can be only one of them else code flow. */
|
|
|
|
tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
|
|
|
|
|
|
|
|
if (ns->type == NS_TYPE_VOTE) {
|
|
|
|
voter = smartlist_get(ns->voters, 0);
|
|
|
|
tor_assert(voter);
|
|
|
|
voter_identity = hex_str(voter->identity_digest,
|
|
|
|
sizeof(voter->identity_digest));
|
|
|
|
} else {
|
|
|
|
/* Consensus has multiple voters so no specific voter. */
|
|
|
|
voter_identity = "consensus";
|
|
|
|
}
|
|
|
|
|
2017-07-01 23:56:06 +02:00
|
|
|
/* We extract both, and on error everything is stopped because it means
|
|
|
|
* the vote is malformed for the shared random value(s). */
|
2016-05-03 17:36:09 +02:00
|
|
|
if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
|
|
|
|
log_warn(LD_DIR, "SR: Unable to parse previous SRV from %s",
|
|
|
|
voter_identity);
|
|
|
|
/* Maybe we have a chance with the current SRV so let's try it anyway. */
|
|
|
|
}
|
|
|
|
if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
|
|
|
|
log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
|
|
|
|
voter_identity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-24 11:22:27 +02:00
|
|
|
/** Parse a v3 networkstatus vote, opinion, or consensus (depending on
|
|
|
|
* ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
|
2008-02-05 22:39:29 +01:00
|
|
|
networkstatus_t *
|
2007-09-22 08:06:05 +02:00
|
|
|
networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
2008-07-24 11:22:27 +02:00
|
|
|
networkstatus_type_t ns_type)
|
2007-06-03 05:05:10 +02:00
|
|
|
{
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *tokens = smartlist_new();
|
2007-06-03 05:05:10 +02:00
|
|
|
smartlist_t *rs_tokens = NULL, *footer_tokens = NULL;
|
2007-06-05 00:29:00 +02:00
|
|
|
networkstatus_voter_info_t *voter = NULL;
|
2008-02-05 22:39:29 +01:00
|
|
|
networkstatus_t *ns = NULL;
|
2016-02-10 21:28:19 +01:00
|
|
|
common_digests_t ns_digests;
|
2017-05-04 14:49:02 +02:00
|
|
|
uint8_t sha3_as_signed[DIGEST256_LEN];
|
2009-08-20 18:45:03 +02:00
|
|
|
const char *cert, *end_of_header, *end_of_footer, *s_dup = s;
|
2007-06-03 05:05:10 +02:00
|
|
|
directory_token_t *tok;
|
|
|
|
struct in_addr in;
|
2007-10-11 20:01:12 +02:00
|
|
|
int i, inorder, n_signatures = 0;
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_t *area = NULL, *rs_area = NULL;
|
2009-09-23 21:23:04 +02:00
|
|
|
consensus_flavor_t flav = FLAV_NS;
|
2012-05-07 18:38:28 +02:00
|
|
|
char *last_kwd=NULL;
|
2009-09-23 21:23:04 +02:00
|
|
|
|
2008-08-14 14:37:14 +02:00
|
|
|
tor_assert(s);
|
2007-06-03 05:05:10 +02:00
|
|
|
|
2009-01-31 19:27:38 +01:00
|
|
|
if (eos_out)
|
|
|
|
*eos_out = NULL;
|
|
|
|
|
2017-05-03 16:17:37 +02:00
|
|
|
if (router_get_networkstatus_v3_hashes(s, &ns_digests) ||
|
2017-05-04 14:49:02 +02:00
|
|
|
router_get_networkstatus_v3_sha3_as_signed(sha3_as_signed, s)<0) {
|
2007-06-03 05:05:10 +02:00
|
|
|
log_warn(LD_DIR, "Unable to compute digest of network-status");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2007-06-03 05:05:10 +02:00
|
|
|
end_of_header = find_start_of_next_routerstatus(s);
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area, s, end_of_header, tokens,
|
2008-07-24 11:22:27 +02:00
|
|
|
(ns_type == NS_TYPE_CONSENSUS) ?
|
|
|
|
networkstatus_consensus_token_table :
|
|
|
|
networkstatus_token_table, 0)) {
|
2016-02-22 16:59:27 +01:00
|
|
|
log_warn(LD_DIR, "Error tokenizing network-status header");
|
2007-06-03 05:05:10 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-02-05 22:39:29 +01:00
|
|
|
ns = tor_malloc_zero(sizeof(networkstatus_t));
|
2009-09-16 23:01:01 +02:00
|
|
|
memcpy(&ns->digests, &ns_digests, sizeof(ns_digests));
|
2017-05-04 14:49:02 +02:00
|
|
|
memcpy(&ns->digest_sha3_as_signed, sha3_as_signed, sizeof(sha3_as_signed));
|
2007-06-05 00:29:00 +02:00
|
|
|
|
2009-09-23 21:23:04 +02:00
|
|
|
tok = find_by_keyword(tokens, K_NETWORK_STATUS_VERSION);
|
|
|
|
tor_assert(tok);
|
|
|
|
if (tok->n_args > 1) {
|
|
|
|
int flavor = networkstatus_parse_flavor_name(tok->args[1]);
|
|
|
|
if (flavor < 0) {
|
|
|
|
log_warn(LD_DIR, "Can't parse document with unknown flavor %s",
|
2012-08-03 16:53:00 +02:00
|
|
|
escaped(tok->args[1]));
|
2009-09-23 21:23:04 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
ns->flavor = flav = flavor;
|
|
|
|
}
|
|
|
|
if (flav != FLAV_NS && ns_type != NS_TYPE_CONSENSUS) {
|
2011-03-15 17:48:26 +01:00
|
|
|
log_warn(LD_DIR, "Flavor found on non-consensus networkstatus.");
|
2009-09-23 21:23:04 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns_type != NS_TYPE_CONSENSUS) {
|
2007-06-05 00:29:00 +02:00
|
|
|
const char *end_of_cert = NULL;
|
|
|
|
if (!(cert = strstr(s, "\ndir-key-certificate-version")))
|
|
|
|
goto err;
|
|
|
|
++cert;
|
|
|
|
ns->cert = authority_cert_parse_from_string(cert, &end_of_cert);
|
|
|
|
if (!ns->cert || !end_of_cert || end_of_cert > end_of_header)
|
|
|
|
goto err;
|
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_VOTE_STATUS);
|
2008-12-11 21:28:50 +01:00
|
|
|
tor_assert(tok->n_args);
|
2007-06-05 00:29:00 +02:00
|
|
|
if (!strcmp(tok->args[0], "vote")) {
|
2008-07-24 11:22:27 +02:00
|
|
|
ns->type = NS_TYPE_VOTE;
|
2007-06-05 00:29:00 +02:00
|
|
|
} else if (!strcmp(tok->args[0], "consensus")) {
|
2008-07-24 11:22:27 +02:00
|
|
|
ns->type = NS_TYPE_CONSENSUS;
|
|
|
|
} else if (!strcmp(tok->args[0], "opinion")) {
|
|
|
|
ns->type = NS_TYPE_OPINION;
|
2007-06-05 00:29:00 +02:00
|
|
|
} else {
|
|
|
|
log_warn(LD_DIR, "Unrecognized vote status %s in network-status",
|
2007-06-03 05:05:10 +02:00
|
|
|
escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns_type != ns->type) {
|
2007-06-05 00:29:00 +02:00
|
|
|
log_warn(LD_DIR, "Got the wrong kind of v3 networkstatus.");
|
2007-06-03 05:05:10 +02:00
|
|
|
goto err;
|
2007-06-05 00:29:00 +02:00
|
|
|
}
|
|
|
|
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_OPINION) {
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_PUBLISHED);
|
2007-06-05 00:29:00 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &ns->published))
|
|
|
|
goto err;
|
2007-10-19 20:56:24 +02:00
|
|
|
|
2012-01-18 21:53:30 +01:00
|
|
|
ns->supported_methods = smartlist_new();
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHODS);
|
2007-10-19 20:56:24 +02:00
|
|
|
if (tok) {
|
|
|
|
for (i=0; i < tok->n_args; ++i)
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(ns->supported_methods, tok->args[i]);
|
2007-10-19 20:56:24 +02:00
|
|
|
} else {
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(ns->supported_methods, "1");
|
2007-10-19 20:56:24 +02:00
|
|
|
}
|
|
|
|
} else {
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_opt_by_keyword(tokens, K_CONSENSUS_METHOD);
|
2007-10-19 20:56:24 +02:00
|
|
|
if (tok) {
|
2016-07-05 20:19:31 +02:00
|
|
|
int num_ok;
|
2007-10-19 20:56:24 +02:00
|
|
|
ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX,
|
2016-07-05 20:19:31 +02:00
|
|
|
&num_ok, NULL);
|
|
|
|
if (!num_ok)
|
2007-10-19 20:56:24 +02:00
|
|
|
goto err;
|
|
|
|
} else {
|
|
|
|
ns->consensus_method = 1;
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
|
2016-08-19 23:31:49 +02:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_CLIENT_PROTOCOLS)))
|
|
|
|
ns->recommended_client_protocols = tor_strdup(tok->args[0]);
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_RECOMMENDED_RELAY_PROTOCOLS)))
|
|
|
|
ns->recommended_relay_protocols = tor_strdup(tok->args[0]);
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_CLIENT_PROTOCOLS)))
|
|
|
|
ns->required_client_protocols = tor_strdup(tok->args[0]);
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_REQUIRED_RELAY_PROTOCOLS)))
|
|
|
|
ns->required_relay_protocols = tor_strdup(tok->args[0]);
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_VALID_AFTER);
|
2007-06-03 05:05:10 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &ns->valid_after))
|
|
|
|
goto err;
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_FRESH_UNTIL);
|
2007-06-03 05:05:10 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &ns->fresh_until))
|
|
|
|
goto err;
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_VALID_UNTIL);
|
2007-06-03 05:05:10 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &ns->valid_until))
|
|
|
|
goto err;
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_VOTING_DELAY);
|
2007-06-03 05:05:10 +02:00
|
|
|
tor_assert(tok->n_args >= 2);
|
2016-07-05 20:19:31 +02:00
|
|
|
{
|
|
|
|
int ok;
|
|
|
|
ns->vote_seconds =
|
|
|
|
(int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
|
|
|
|
if (!ok)
|
|
|
|
goto err;
|
|
|
|
ns->dist_seconds =
|
|
|
|
(int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
|
|
|
|
if (!ok)
|
|
|
|
goto err;
|
|
|
|
}
|
2014-12-20 11:53:00 +01:00
|
|
|
if (ns->valid_after +
|
|
|
|
(get_options()->TestingTorNetwork ?
|
|
|
|
MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL) > ns->fresh_until) {
|
2007-08-15 17:38:58 +02:00
|
|
|
log_warn(LD_DIR, "Vote/consensus freshness interval is too short");
|
|
|
|
goto err;
|
|
|
|
}
|
2014-12-20 11:53:00 +01:00
|
|
|
if (ns->valid_after +
|
|
|
|
(get_options()->TestingTorNetwork ?
|
|
|
|
MIN_VOTE_INTERVAL_TESTING : MIN_VOTE_INTERVAL)*2 > ns->valid_until) {
|
2007-08-15 17:38:58 +02:00
|
|
|
log_warn(LD_DIR, "Vote/consensus liveness interval is too short");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (ns->vote_seconds < MIN_VOTE_SECONDS) {
|
|
|
|
log_warn(LD_DIR, "Vote seconds is too short");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (ns->dist_seconds < MIN_DIST_SECONDS) {
|
|
|
|
log_warn(LD_DIR, "Dist seconds is too short");
|
|
|
|
goto err;
|
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_CLIENT_VERSIONS))) {
|
2008-03-26 17:33:33 +01:00
|
|
|
ns->client_versions = tor_strdup(tok->args[0]);
|
2007-06-03 05:05:10 +02:00
|
|
|
}
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_SERVER_VERSIONS))) {
|
2008-03-26 17:33:33 +01:00
|
|
|
ns->server_versions = tor_strdup(tok->args[0]);
|
2007-06-03 05:05:10 +02:00
|
|
|
}
|
|
|
|
|
2015-01-09 17:36:47 +01:00
|
|
|
{
|
|
|
|
smartlist_t *package_lst = find_all_by_keyword(tokens, K_PACKAGE);
|
2015-01-29 20:14:59 +01:00
|
|
|
ns->package_lines = smartlist_new();
|
2015-01-09 17:36:47 +01:00
|
|
|
if (package_lst) {
|
|
|
|
SMARTLIST_FOREACH(package_lst, directory_token_t *, t,
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(ns->package_lines, t->args[0]));
|
2015-01-09 17:36:47 +01:00
|
|
|
}
|
|
|
|
smartlist_free(package_lst);
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_KNOWN_FLAGS);
|
2012-01-18 21:53:30 +01:00
|
|
|
ns->known_flags = smartlist_new();
|
2007-06-08 20:41:12 +02:00
|
|
|
inorder = 1;
|
|
|
|
for (i = 0; i < tok->n_args; ++i) {
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(ns->known_flags, tok->args[i]);
|
2007-06-13 20:16:05 +02:00
|
|
|
if (i>0 && strcmp(tok->args[i-1], tok->args[i])>= 0) {
|
|
|
|
log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
|
2007-06-08 20:41:12 +02:00
|
|
|
inorder = 0;
|
2007-06-13 20:16:05 +02:00
|
|
|
}
|
2007-06-08 20:41:12 +02:00
|
|
|
}
|
|
|
|
if (!inorder) {
|
|
|
|
log_warn(LD_DIR, "known-flags not in order");
|
|
|
|
goto err;
|
2012-09-13 17:45:05 +02:00
|
|
|
}
|
|
|
|
if (ns->type != NS_TYPE_CONSENSUS &&
|
|
|
|
smartlist_len(ns->known_flags) > MAX_KNOWN_FLAGS_IN_VOTE) {
|
2012-09-14 16:10:16 +02:00
|
|
|
/* If we allowed more than 64 flags in votes, then parsing them would make
|
|
|
|
* us invoke undefined behavior whenever we used 1<<flagnum to do a
|
|
|
|
* bit-shift. This is only for votes and opinions: consensus users don't
|
|
|
|
* care about flags they don't recognize, and so don't build a bitfield
|
|
|
|
* for them. */
|
2012-09-13 17:45:05 +02:00
|
|
|
log_warn(LD_DIR, "Too many known-flags in consensus vote or opinion");
|
|
|
|
goto err;
|
2007-06-08 20:41:12 +02:00
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
|
2009-09-15 04:15:57 +02:00
|
|
|
tok = find_opt_by_keyword(tokens, K_PARAMS);
|
|
|
|
if (tok) {
|
2012-05-07 18:38:28 +02:00
|
|
|
int any_dups = 0;
|
2009-09-15 04:15:57 +02:00
|
|
|
inorder = 1;
|
2012-01-18 21:53:30 +01:00
|
|
|
ns->net_params = smartlist_new();
|
2009-09-15 04:15:57 +02:00
|
|
|
for (i = 0; i < tok->n_args; ++i) {
|
|
|
|
int ok=0;
|
|
|
|
char *eq = strchr(tok->args[i], '=');
|
2012-05-07 18:38:28 +02:00
|
|
|
size_t eq_pos;
|
2009-09-15 04:15:57 +02:00
|
|
|
if (!eq) {
|
|
|
|
log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
|
|
|
|
goto err;
|
|
|
|
}
|
2012-05-07 18:38:28 +02:00
|
|
|
eq_pos = eq-tok->args[i];
|
2009-09-15 04:15:57 +02:00
|
|
|
tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
|
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (i > 0 && strcmp(tok->args[i-1], tok->args[i]) >= 0) {
|
|
|
|
log_warn(LD_DIR, "%s >= %s", tok->args[i-1], tok->args[i]);
|
|
|
|
inorder = 0;
|
|
|
|
}
|
2012-05-07 18:38:28 +02:00
|
|
|
if (last_kwd && eq_pos == strlen(last_kwd) &&
|
|
|
|
fast_memeq(last_kwd, tok->args[i], eq_pos)) {
|
|
|
|
log_warn(LD_DIR, "Duplicate value for %s parameter",
|
|
|
|
escaped(tok->args[i]));
|
|
|
|
any_dups = 1;
|
|
|
|
}
|
|
|
|
tor_free(last_kwd);
|
|
|
|
last_kwd = tor_strndup(tok->args[i], eq_pos);
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(ns->net_params, tok->args[i]);
|
2009-09-15 04:15:57 +02:00
|
|
|
}
|
|
|
|
if (!inorder) {
|
|
|
|
log_warn(LD_DIR, "params not in order");
|
|
|
|
goto err;
|
|
|
|
}
|
2012-05-07 18:38:28 +02:00
|
|
|
if (any_dups) {
|
|
|
|
log_warn(LD_DIR, "Duplicate in parameters");
|
|
|
|
goto err;
|
|
|
|
}
|
2009-09-15 04:15:57 +02:00
|
|
|
}
|
|
|
|
|
2012-01-18 21:53:30 +01:00
|
|
|
ns->voters = smartlist_new();
|
2007-06-05 00:29:00 +02:00
|
|
|
|
2009-04-09 21:58:16 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
|
2007-06-05 00:29:00 +02:00
|
|
|
tok = _tok;
|
|
|
|
if (tok->tp == K_DIR_SOURCE) {
|
|
|
|
tor_assert(tok->n_args >= 6);
|
|
|
|
|
|
|
|
if (voter)
|
|
|
|
smartlist_add(ns->voters, voter);
|
|
|
|
voter = tor_malloc_zero(sizeof(networkstatus_voter_info_t));
|
2012-01-18 21:53:30 +01:00
|
|
|
voter->sigs = smartlist_new();
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS)
|
2009-09-16 23:01:01 +02:00
|
|
|
memcpy(voter->vote_digest, ns_digests.d[DIGEST_SHA1], DIGEST_LEN);
|
2007-06-05 00:29:00 +02:00
|
|
|
|
|
|
|
voter->nickname = tor_strdup(tok->args[0]);
|
|
|
|
if (strlen(tok->args[1]) != HEX_DIGEST_LEN ||
|
|
|
|
base16_decode(voter->identity_digest, sizeof(voter->identity_digest),
|
2016-06-17 16:41:45 +02:00
|
|
|
tok->args[1], HEX_DIGEST_LEN)
|
|
|
|
!= sizeof(voter->identity_digest)) {
|
2007-06-05 00:29:00 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding identity digest %s in "
|
2016-02-22 16:59:27 +01:00
|
|
|
"network-status document.", escaped(tok->args[1]));
|
2007-06-05 00:29:00 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS &&
|
2011-05-10 22:58:38 +02:00
|
|
|
tor_memneq(ns->cert->cache_info.identity_digest,
|
2008-07-24 11:22:27 +02:00
|
|
|
voter->identity_digest, DIGEST_LEN)) {
|
2007-10-11 20:01:12 +02:00
|
|
|
log_warn(LD_DIR,"Mismatch between identities in certificate and vote");
|
|
|
|
goto err;
|
|
|
|
}
|
2014-04-11 16:22:14 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS) {
|
|
|
|
if (authority_cert_is_blacklisted(ns->cert)) {
|
|
|
|
log_warn(LD_DIR, "Rejecting vote signature made with blacklisted "
|
|
|
|
"signing key %s",
|
|
|
|
hex_str(ns->cert->signing_key_digest, DIGEST_LEN));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
voter->address = tor_strdup(tok->args[2]);
|
|
|
|
if (!tor_inet_aton(tok->args[3], &in)) {
|
|
|
|
log_warn(LD_DIR, "Error decoding IP address %s in network-status.",
|
|
|
|
escaped(tok->args[3]));
|
|
|
|
goto err;
|
|
|
|
}
|
2007-06-13 20:16:05 +02:00
|
|
|
voter->addr = ntohl(in.s_addr);
|
2016-07-05 20:19:31 +02:00
|
|
|
int ok;
|
2008-03-18 15:30:46 +01:00
|
|
|
voter->dir_port = (uint16_t)
|
|
|
|
tor_parse_long(tok->args[4], 10, 0, 65535, &ok, NULL);
|
2007-06-05 00:29:00 +02:00
|
|
|
if (!ok)
|
|
|
|
goto err;
|
2008-03-18 15:30:46 +01:00
|
|
|
voter->or_port = (uint16_t)
|
|
|
|
tor_parse_long(tok->args[5], 10, 0, 65535, &ok, NULL);
|
2007-06-05 00:29:00 +02:00
|
|
|
if (!ok)
|
|
|
|
goto err;
|
|
|
|
} else if (tok->tp == K_CONTACT) {
|
|
|
|
if (!voter || voter->contact) {
|
|
|
|
log_warn(LD_DIR, "contact element is out of place.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
voter->contact = tor_strdup(tok->args[0]);
|
|
|
|
} else if (tok->tp == K_VOTE_DIGEST) {
|
2008-07-24 11:22:27 +02:00
|
|
|
tor_assert(ns->type == NS_TYPE_CONSENSUS);
|
2007-06-05 00:29:00 +02:00
|
|
|
tor_assert(tok->n_args >= 1);
|
|
|
|
if (!voter || ! tor_digest_is_zero(voter->vote_digest)) {
|
|
|
|
log_warn(LD_DIR, "vote-digest element is out of place.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (strlen(tok->args[0]) != HEX_DIGEST_LEN ||
|
|
|
|
base16_decode(voter->vote_digest, sizeof(voter->vote_digest),
|
2016-06-17 16:41:45 +02:00
|
|
|
tok->args[0], HEX_DIGEST_LEN)
|
|
|
|
!= sizeof(voter->vote_digest)) {
|
2007-06-05 00:29:00 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding vote digest %s in "
|
2009-04-09 21:58:16 +02:00
|
|
|
"network-status consensus.", escaped(tok->args[0]));
|
2007-06-05 00:29:00 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2009-04-09 21:58:16 +02:00
|
|
|
} SMARTLIST_FOREACH_END(_tok);
|
2007-06-05 00:29:00 +02:00
|
|
|
if (voter) {
|
|
|
|
smartlist_add(ns->voters, voter);
|
|
|
|
voter = NULL;
|
2007-06-03 05:05:10 +02:00
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
if (smartlist_len(ns->voters) == 0) {
|
2016-02-22 16:59:27 +01:00
|
|
|
log_warn(LD_DIR, "Missing dir-source elements in a networkstatus.");
|
2007-06-03 05:05:10 +02:00
|
|
|
goto err;
|
2008-07-24 11:22:27 +02:00
|
|
|
} else if (ns->type != NS_TYPE_CONSENSUS && smartlist_len(ns->voters) != 1) {
|
2007-06-05 00:29:00 +02:00
|
|
|
log_warn(LD_DIR, "Too many dir-source elements in a vote networkstatus.");
|
2007-06-03 05:05:10 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS &&
|
2008-12-11 20:40:58 +01:00
|
|
|
(tok = find_opt_by_keyword(tokens, K_LEGACY_DIR_KEY))) {
|
2008-05-12 04:14:01 +02:00
|
|
|
int bad = 1;
|
|
|
|
if (strlen(tok->args[0]) == HEX_DIGEST_LEN) {
|
2016-07-05 20:19:31 +02:00
|
|
|
networkstatus_voter_info_t *voter_0 = smartlist_get(ns->voters, 0);
|
|
|
|
if (base16_decode(voter_0->legacy_id_digest, DIGEST_LEN,
|
2016-06-17 16:41:45 +02:00
|
|
|
tok->args[0], HEX_DIGEST_LEN) != DIGEST_LEN)
|
2008-05-12 04:14:01 +02:00
|
|
|
bad = 1;
|
|
|
|
else
|
|
|
|
bad = 0;
|
|
|
|
}
|
|
|
|
if (bad) {
|
|
|
|
log_warn(LD_DIR, "Invalid legacy key digest %s on vote.",
|
|
|
|
escaped(tok->args[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-03 17:36:09 +02:00
|
|
|
/* If this is a vote document, check if information about the shared
|
|
|
|
randomness protocol is included, and extract it. */
|
|
|
|
if (ns->type == NS_TYPE_VOTE) {
|
2018-04-25 16:42:56 +02:00
|
|
|
dirvote_parse_sr_commits(ns, tokens);
|
2016-05-03 17:36:09 +02:00
|
|
|
}
|
|
|
|
/* For both a vote and consensus, extract the shared random values. */
|
|
|
|
if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
|
|
|
|
extract_shared_random_srvs(ns, tokens);
|
|
|
|
}
|
|
|
|
|
2007-06-03 05:05:10 +02:00
|
|
|
/* Parse routerstatus lines. */
|
2012-01-18 21:53:30 +01:00
|
|
|
rs_tokens = smartlist_new();
|
2008-11-05 21:34:22 +01:00
|
|
|
rs_area = memarea_new();
|
2007-06-03 05:05:10 +02:00
|
|
|
s = end_of_header;
|
2012-01-18 21:53:30 +01:00
|
|
|
ns->routerstatus_list = smartlist_new();
|
2007-06-05 00:29:00 +02:00
|
|
|
|
2007-06-03 05:05:10 +02:00
|
|
|
while (!strcmpstart(s, "r ")) {
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS) {
|
2007-06-05 00:29:00 +02:00
|
|
|
vote_routerstatus_t *rs = tor_malloc_zero(sizeof(vote_routerstatus_t));
|
2008-03-26 17:33:33 +01:00
|
|
|
if (routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens, ns,
|
2017-01-09 20:25:48 +01:00
|
|
|
rs, 0, 0)) {
|
2007-06-05 00:29:00 +02:00
|
|
|
smartlist_add(ns->routerstatus_list, rs);
|
2017-01-09 20:25:48 +01:00
|
|
|
} else {
|
|
|
|
vote_routerstatus_free(rs);
|
2007-06-05 00:29:00 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
routerstatus_t *rs;
|
2008-03-26 17:33:33 +01:00
|
|
|
if ((rs = routerstatus_parse_entry_from_string(rs_area, &s, rs_tokens,
|
|
|
|
NULL, NULL,
|
2009-09-23 21:23:04 +02:00
|
|
|
ns->consensus_method,
|
2016-06-27 19:19:49 +02:00
|
|
|
flav))) {
|
2016-06-12 23:27:22 +02:00
|
|
|
/* Use exponential-backoff scheduling when downloading microdescs */
|
2007-06-05 00:29:00 +02:00
|
|
|
smartlist_add(ns->routerstatus_list, rs);
|
2016-06-27 19:19:49 +02:00
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
}
|
|
|
|
}
|
2007-06-04 21:19:01 +02:00
|
|
|
for (i = 1; i < smartlist_len(ns->routerstatus_list); ++i) {
|
2007-06-05 00:29:00 +02:00
|
|
|
routerstatus_t *rs1, *rs2;
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS) {
|
2007-06-05 00:29:00 +02:00
|
|
|
vote_routerstatus_t *a = smartlist_get(ns->routerstatus_list, i-1);
|
|
|
|
vote_routerstatus_t *b = smartlist_get(ns->routerstatus_list, i);
|
|
|
|
rs1 = &a->status; rs2 = &b->status;
|
|
|
|
} else {
|
|
|
|
rs1 = smartlist_get(ns->routerstatus_list, i-1);
|
|
|
|
rs2 = smartlist_get(ns->routerstatus_list, i);
|
|
|
|
}
|
2011-05-10 22:58:38 +02:00
|
|
|
if (fast_memcmp(rs1->identity_digest, rs2->identity_digest, DIGEST_LEN)
|
|
|
|
>= 0) {
|
2016-02-22 16:59:27 +01:00
|
|
|
log_warn(LD_DIR, "Networkstatus entries not sorted by identity digest");
|
2007-06-04 21:19:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
2014-10-29 18:36:21 +01:00
|
|
|
if (ns_type != NS_TYPE_CONSENSUS) {
|
|
|
|
digest256map_t *ed_id_map = digest256map_new();
|
2015-06-01 14:59:14 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(ns->routerstatus_list, vote_routerstatus_t *,
|
|
|
|
vrs) {
|
2014-10-29 18:36:21 +01:00
|
|
|
if (! vrs->has_ed25519_listing ||
|
|
|
|
tor_mem_is_zero((const char *)vrs->ed25519_id, DIGEST256_LEN))
|
|
|
|
continue;
|
|
|
|
if (digest256map_get(ed_id_map, vrs->ed25519_id) != NULL) {
|
|
|
|
log_warn(LD_DIR, "Vote networkstatus ed25519 identities were not "
|
|
|
|
"unique");
|
2015-05-28 18:49:39 +02:00
|
|
|
digest256map_free(ed_id_map, NULL);
|
2014-10-29 18:36:21 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
digest256map_set(ed_id_map, vrs->ed25519_id, (void*)1);
|
|
|
|
} SMARTLIST_FOREACH_END(vrs);
|
|
|
|
digest256map_free(ed_id_map, NULL);
|
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
|
|
|
|
/* Parse footer; check signature. */
|
2012-01-18 21:53:30 +01:00
|
|
|
footer_tokens = smartlist_new();
|
2007-09-22 08:06:05 +02:00
|
|
|
if ((end_of_footer = strstr(s, "\nnetwork-status-version ")))
|
|
|
|
++end_of_footer;
|
|
|
|
else
|
|
|
|
end_of_footer = s + strlen(s);
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area,s, end_of_footer, footer_tokens,
|
2007-09-26 18:19:44 +02:00
|
|
|
networkstatus_vote_footer_token_table, 0)) {
|
2007-06-03 05:05:10 +02:00
|
|
|
log_warn(LD_DIR, "Error tokenizing network-status vote footer.");
|
|
|
|
goto err;
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
|
2010-02-26 07:02:20 +01:00
|
|
|
{
|
|
|
|
int found_sig = 0;
|
|
|
|
SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
|
2010-02-26 08:00:56 +01:00
|
|
|
tok = _tok;
|
2010-02-26 07:02:20 +01:00
|
|
|
if (tok->tp == K_DIRECTORY_SIGNATURE)
|
|
|
|
found_sig = 1;
|
|
|
|
else if (found_sig) {
|
|
|
|
log_warn(LD_DIR, "Extraneous token after first directory-signature");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(_tok);
|
|
|
|
}
|
|
|
|
|
2010-02-27 22:33:46 +01:00
|
|
|
if ((tok = find_opt_by_keyword(footer_tokens, K_DIRECTORY_FOOTER))) {
|
|
|
|
if (tok != smartlist_get(footer_tokens, 0)) {
|
|
|
|
log_warn(LD_DIR, "Misplaced directory-footer token");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-30 00:40:40 +01:00
|
|
|
tok = find_opt_by_keyword(footer_tokens, K_BW_WEIGHTS);
|
|
|
|
if (tok) {
|
2012-01-18 21:53:30 +01:00
|
|
|
ns->weight_params = smartlist_new();
|
2010-01-30 00:40:40 +01:00
|
|
|
for (i = 0; i < tok->n_args; ++i) {
|
|
|
|
int ok=0;
|
|
|
|
char *eq = strchr(tok->args[i], '=');
|
|
|
|
if (!eq) {
|
|
|
|
log_warn(LD_DIR, "Bad element '%s' in weight params",
|
|
|
|
escaped(tok->args[i]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
tor_parse_long(eq+1, 10, INT32_MIN, INT32_MAX, &ok, NULL);
|
|
|
|
if (!ok) {
|
|
|
|
log_warn(LD_DIR, "Bad element '%s' in params", escaped(tok->args[i]));
|
|
|
|
goto err;
|
|
|
|
}
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(ns->weight_params, tok->args[i]);
|
2010-01-30 00:40:40 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(footer_tokens, directory_token_t *, _tok) {
|
2007-06-05 00:29:00 +02:00
|
|
|
char declared_identity[DIGEST_LEN];
|
|
|
|
networkstatus_voter_info_t *v;
|
2009-09-16 23:01:01 +02:00
|
|
|
document_signature_t *sig;
|
2009-09-23 21:23:04 +02:00
|
|
|
const char *id_hexdigest = NULL;
|
|
|
|
const char *sk_hexdigest = NULL;
|
|
|
|
digest_algorithm_t alg = DIGEST_SHA1;
|
2007-06-05 00:29:00 +02:00
|
|
|
tok = _tok;
|
|
|
|
if (tok->tp != K_DIRECTORY_SIGNATURE)
|
|
|
|
continue;
|
|
|
|
tor_assert(tok->n_args >= 2);
|
2009-09-23 21:23:04 +02:00
|
|
|
if (tok->n_args == 2) {
|
|
|
|
id_hexdigest = tok->args[0];
|
|
|
|
sk_hexdigest = tok->args[1];
|
|
|
|
} else {
|
|
|
|
const char *algname = tok->args[0];
|
|
|
|
int a;
|
|
|
|
id_hexdigest = tok->args[1];
|
|
|
|
sk_hexdigest = tok->args[2];
|
|
|
|
a = crypto_digest_algorithm_parse_name(algname);
|
|
|
|
if (a<0) {
|
|
|
|
log_warn(LD_DIR, "Unknown digest algorithm %s; skipping",
|
|
|
|
escaped(algname));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
alg = a;
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
|
|
|
|
if (!tok->object_type ||
|
|
|
|
strcmp(tok->object_type, "SIGNATURE") ||
|
|
|
|
tok->object_size < 128 || tok->object_size > 512) {
|
|
|
|
log_warn(LD_DIR, "Bad object type or length on directory-signature");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-09-23 21:23:04 +02:00
|
|
|
if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
|
2007-06-05 00:29:00 +02:00
|
|
|
base16_decode(declared_identity, sizeof(declared_identity),
|
2016-06-17 16:41:45 +02:00
|
|
|
id_hexdigest, HEX_DIGEST_LEN)
|
|
|
|
!= sizeof(declared_identity)) {
|
2007-06-05 00:29:00 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding declared identity %s in "
|
2016-02-22 16:59:27 +01:00
|
|
|
"network-status document.", escaped(id_hexdigest));
|
2007-06-05 00:29:00 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
|
2016-02-22 16:59:27 +01:00
|
|
|
log_warn(LD_DIR, "ID on signature on network-status document does "
|
|
|
|
"not match any declared directory source.");
|
2007-06-05 00:29:00 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2009-09-16 23:01:01 +02:00
|
|
|
sig = tor_malloc_zero(sizeof(document_signature_t));
|
|
|
|
memcpy(sig->identity_digest, v->identity_digest, DIGEST_LEN);
|
2009-09-23 21:23:04 +02:00
|
|
|
sig->alg = alg;
|
|
|
|
if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
|
2009-09-16 23:01:01 +02:00
|
|
|
base16_decode(sig->signing_key_digest, sizeof(sig->signing_key_digest),
|
2016-06-17 16:41:45 +02:00
|
|
|
sk_hexdigest, HEX_DIGEST_LEN)
|
|
|
|
!= sizeof(sig->signing_key_digest)) {
|
2009-09-23 21:23:04 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
|
2016-02-22 16:59:27 +01:00
|
|
|
"network-status document.", escaped(sk_hexdigest));
|
2009-09-16 23:01:01 +02:00
|
|
|
tor_free(sig);
|
2007-06-05 00:29:00 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS) {
|
2011-05-10 22:58:38 +02:00
|
|
|
if (tor_memneq(declared_identity, ns->cert->cache_info.identity_digest,
|
2007-10-11 20:01:12 +02:00
|
|
|
DIGEST_LEN)) {
|
|
|
|
log_warn(LD_DIR, "Digest mismatch between declared and actual on "
|
|
|
|
"network-status vote.");
|
2009-09-16 23:01:01 +02:00
|
|
|
tor_free(sig);
|
2007-10-11 20:01:12 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
}
|
|
|
|
|
2018-05-01 16:59:10 +02:00
|
|
|
if (networkstatus_get_voter_sig_by_alg(v, sig->alg)) {
|
2009-09-16 23:01:01 +02:00
|
|
|
/* We already parsed a vote with this algorithm from this voter. Use the
|
|
|
|
first one. */
|
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_DIR, "We received a networkstatus "
|
2016-02-22 16:59:27 +01:00
|
|
|
"that contains two signatures from the same voter with the same "
|
|
|
|
"algorithm. Ignoring the second signature.");
|
2009-09-16 23:01:01 +02:00
|
|
|
tor_free(sig);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-07-24 11:22:27 +02:00
|
|
|
if (ns->type != NS_TYPE_CONSENSUS) {
|
2009-09-16 23:01:01 +02:00
|
|
|
if (check_signature_token(ns_digests.d[DIGEST_SHA1], DIGEST_LEN,
|
2009-09-14 18:02:00 +02:00
|
|
|
tok, ns->cert->signing_key, 0,
|
2016-02-22 16:59:27 +01:00
|
|
|
"network-status document")) {
|
2009-09-16 23:01:01 +02:00
|
|
|
tor_free(sig);
|
2007-06-05 00:29:00 +02:00
|
|
|
goto err;
|
2009-09-16 23:01:01 +02:00
|
|
|
}
|
|
|
|
sig->good_signature = 1;
|
2007-06-05 00:29:00 +02:00
|
|
|
} else {
|
2011-01-15 19:25:13 +01:00
|
|
|
if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
|
2009-09-16 23:01:01 +02:00
|
|
|
tor_free(sig);
|
2008-02-22 20:09:45 +01:00
|
|
|
goto err;
|
2009-09-13 21:47:55 +02:00
|
|
|
}
|
2009-09-16 23:01:01 +02:00
|
|
|
sig->signature = tor_memdup(tok->object_body, tok->object_size);
|
|
|
|
sig->signature_len = (int) tok->object_size;
|
2007-06-05 00:29:00 +02:00
|
|
|
}
|
2009-09-16 23:01:01 +02:00
|
|
|
smartlist_add(v->sigs, sig);
|
|
|
|
|
2007-10-11 20:01:12 +02:00
|
|
|
++n_signatures;
|
2009-09-16 23:01:01 +02:00
|
|
|
} SMARTLIST_FOREACH_END(_tok);
|
2007-10-11 20:01:12 +02:00
|
|
|
|
|
|
|
if (! n_signatures) {
|
2016-02-22 16:59:27 +01:00
|
|
|
log_warn(LD_DIR, "No signatures on networkstatus document.");
|
2007-10-11 20:01:12 +02:00
|
|
|
goto err;
|
2009-09-13 21:47:55 +02:00
|
|
|
} else if (ns->type == NS_TYPE_VOTE && n_signatures != 1) {
|
|
|
|
log_warn(LD_DIR, "Received more than one signature on a "
|
|
|
|
"network-status vote.");
|
|
|
|
goto err;
|
2007-10-11 20:01:12 +02:00
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
|
2007-09-22 08:06:05 +02:00
|
|
|
if (eos_out)
|
|
|
|
*eos_out = end_of_footer;
|
|
|
|
|
2007-06-03 05:05:10 +02:00
|
|
|
goto done;
|
|
|
|
err:
|
2009-08-20 18:45:03 +02:00
|
|
|
dump_desc(s_dup, "v3 networkstatus");
|
2009-12-12 08:07:59 +01:00
|
|
|
networkstatus_vote_free(ns);
|
2007-06-03 05:05:10 +02:00
|
|
|
ns = NULL;
|
|
|
|
done:
|
|
|
|
if (tokens) {
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2007-06-03 05:05:10 +02:00
|
|
|
smartlist_free(tokens);
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
if (voter) {
|
2009-09-16 23:01:01 +02:00
|
|
|
if (voter->sigs) {
|
|
|
|
SMARTLIST_FOREACH(voter->sigs, document_signature_t *, sig,
|
|
|
|
document_signature_free(sig));
|
|
|
|
smartlist_free(voter->sigs);
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
tor_free(voter->nickname);
|
|
|
|
tor_free(voter->address);
|
|
|
|
tor_free(voter->contact);
|
|
|
|
tor_free(voter);
|
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
if (rs_tokens) {
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(rs_tokens, directory_token_t *, t, token_clear(t));
|
2007-06-03 05:05:10 +02:00
|
|
|
smartlist_free(rs_tokens);
|
|
|
|
}
|
|
|
|
if (footer_tokens) {
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(footer_tokens, directory_token_t *, t, token_clear(t));
|
2007-06-03 05:05:10 +02:00
|
|
|
smartlist_free(footer_tokens);
|
|
|
|
}
|
2008-03-26 18:50:27 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "v3 networkstatus");
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_drop_all(area);
|
2008-03-26 18:50:27 +01:00
|
|
|
}
|
2008-03-26 17:33:33 +01:00
|
|
|
if (rs_area)
|
|
|
|
memarea_drop_all(rs_area);
|
2012-05-07 18:38:28 +02:00
|
|
|
tor_free(last_kwd);
|
2007-06-03 05:05:10 +02:00
|
|
|
|
|
|
|
return ns;
|
|
|
|
}
|
|
|
|
|
2016-02-10 21:28:19 +01:00
|
|
|
/** Return the common_digests_t that holds the digests of the
|
2009-10-15 22:06:00 +02:00
|
|
|
* <b>flavor_name</b>-flavored networkstatus according to the detached
|
2016-02-10 21:35:46 +01:00
|
|
|
* signatures document <b>sigs</b>, allocating a new common_digests_t as
|
2017-11-01 21:44:09 +01:00
|
|
|
* needed. */
|
2016-02-10 21:28:19 +01:00
|
|
|
static common_digests_t *
|
2009-09-16 18:34:44 +02:00
|
|
|
detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
|
|
|
|
{
|
2016-02-10 21:28:19 +01:00
|
|
|
common_digests_t *d = strmap_get(sigs->digests, flavor_name);
|
2009-09-16 18:34:44 +02:00
|
|
|
if (!d) {
|
2016-02-10 21:28:19 +01:00
|
|
|
d = tor_malloc_zero(sizeof(common_digests_t));
|
2009-09-16 18:34:44 +02:00
|
|
|
strmap_set(sigs->digests, flavor_name, d);
|
|
|
|
}
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Return the list of signatures of the <b>flavor_name</b>-flavored
|
|
|
|
* networkstatus according to the detached signatures document <b>sigs</b>,
|
2017-11-01 21:44:09 +01:00
|
|
|
* allocating a new common_digests_t as needed. */
|
2009-09-16 18:34:44 +02:00
|
|
|
static smartlist_t *
|
|
|
|
detached_get_signatures(ns_detached_signatures_t *sigs,
|
|
|
|
const char *flavor_name)
|
|
|
|
{
|
|
|
|
smartlist_t *sl = strmap_get(sigs->signatures, flavor_name);
|
|
|
|
if (!sl) {
|
2012-01-18 21:53:30 +01:00
|
|
|
sl = smartlist_new();
|
2009-09-16 18:34:44 +02:00
|
|
|
strmap_set(sigs->signatures, flavor_name, sl);
|
|
|
|
}
|
|
|
|
return sl;
|
|
|
|
}
|
|
|
|
|
2007-07-30 19:46:14 +02:00
|
|
|
/** Parse a detached v3 networkstatus signature document between <b>s</b> and
|
|
|
|
* <b>eos</b> and return the result. Return -1 on failure. */
|
2007-07-27 20:33:28 +02:00
|
|
|
ns_detached_signatures_t *
|
|
|
|
networkstatus_parse_detached_signatures(const char *s, const char *eos)
|
|
|
|
{
|
2008-12-29 20:57:04 +01:00
|
|
|
/* XXXX there is too much duplicate shared between this function and
|
|
|
|
* networkstatus_parse_vote_from_string(). */
|
2007-07-27 20:33:28 +02:00
|
|
|
directory_token_t *tok;
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_t *area = NULL;
|
2016-02-10 21:28:19 +01:00
|
|
|
common_digests_t *digests;
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *tokens = smartlist_new();
|
2007-07-27 20:33:28 +02:00
|
|
|
ns_detached_signatures_t *sigs =
|
|
|
|
tor_malloc_zero(sizeof(ns_detached_signatures_t));
|
2009-09-16 23:01:01 +02:00
|
|
|
sigs->digests = strmap_new();
|
|
|
|
sigs->signatures = strmap_new();
|
2007-07-27 20:33:28 +02:00
|
|
|
|
|
|
|
if (!eos)
|
|
|
|
eos = s + strlen(s);
|
|
|
|
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2008-03-26 17:33:33 +01:00
|
|
|
if (tokenize_string(area,s, eos, tokens,
|
2007-09-26 18:19:44 +02:00
|
|
|
networkstatus_detached_signature_token_table, 0)) {
|
2007-07-27 20:33:28 +02:00
|
|
|
log_warn(LD_DIR, "Error tokenizing detached networkstatus signatures");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2009-09-16 18:34:44 +02:00
|
|
|
/* Grab all the digest-like tokens. */
|
|
|
|
SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
|
|
|
|
const char *algname;
|
|
|
|
digest_algorithm_t alg;
|
|
|
|
const char *flavor;
|
|
|
|
const char *hexdigest;
|
2016-05-16 17:18:51 +02:00
|
|
|
size_t expected_length, digest_length;
|
2009-09-16 18:34:44 +02:00
|
|
|
|
|
|
|
tok = _tok;
|
|
|
|
|
|
|
|
if (tok->tp == K_CONSENSUS_DIGEST) {
|
|
|
|
algname = "sha1";
|
|
|
|
alg = DIGEST_SHA1;
|
|
|
|
flavor = "ns";
|
|
|
|
hexdigest = tok->args[0];
|
|
|
|
} else if (tok->tp == K_ADDITIONAL_DIGEST) {
|
|
|
|
int a = crypto_digest_algorithm_parse_name(tok->args[1]);
|
|
|
|
if (a<0) {
|
|
|
|
log_warn(LD_DIR, "Unrecognized algorithm name %s", tok->args[0]);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
alg = (digest_algorithm_t) a;
|
|
|
|
flavor = tok->args[0];
|
|
|
|
algname = tok->args[1];
|
|
|
|
hexdigest = tok->args[2];
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-05-23 16:58:27 +02:00
|
|
|
digest_length = crypto_digest_algorithm_get_length(alg);
|
|
|
|
expected_length = digest_length * 2; /* hex encoding */
|
2009-09-16 18:34:44 +02:00
|
|
|
|
|
|
|
if (strlen(hexdigest) != expected_length) {
|
|
|
|
log_warn(LD_DIR, "Wrong length on consensus-digest in detached "
|
|
|
|
"networkstatus signatures");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
digests = detached_get_digests(sigs, flavor);
|
|
|
|
tor_assert(digests);
|
2016-05-16 17:18:51 +02:00
|
|
|
if (!tor_mem_is_zero(digests->d[alg], digest_length)) {
|
2009-09-16 18:34:44 +02:00
|
|
|
log_warn(LD_DIR, "Multiple digests for %s with %s on detached "
|
|
|
|
"signatures document", flavor, algname);
|
|
|
|
continue;
|
|
|
|
}
|
2016-05-16 17:18:51 +02:00
|
|
|
if (base16_decode(digests->d[alg], digest_length,
|
2016-06-17 16:41:45 +02:00
|
|
|
hexdigest, strlen(hexdigest)) != (int) digest_length) {
|
2009-12-18 12:55:05 +01:00
|
|
|
log_warn(LD_DIR, "Bad encoding on consensus-digest in detached "
|
2009-09-16 18:34:44 +02:00
|
|
|
"networkstatus signatures");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(_tok);
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_VALID_AFTER);
|
2007-08-01 18:43:44 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &sigs->valid_after)) {
|
|
|
|
log_warn(LD_DIR, "Bad valid-after in detached networkstatus signatures");
|
2007-07-27 20:33:28 +02:00
|
|
|
goto err;
|
2007-08-01 18:43:44 +02:00
|
|
|
}
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_FRESH_UNTIL);
|
2007-08-01 18:43:44 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &sigs->fresh_until)) {
|
|
|
|
log_warn(LD_DIR, "Bad fresh-until in detached networkstatus signatures");
|
2007-07-27 20:33:28 +02:00
|
|
|
goto err;
|
2007-08-01 18:43:44 +02:00
|
|
|
}
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, K_VALID_UNTIL);
|
2007-08-01 18:43:44 +02:00
|
|
|
if (parse_iso_time(tok->args[0], &sigs->valid_until)) {
|
|
|
|
log_warn(LD_DIR, "Bad valid-until in detached networkstatus signatures");
|
2007-07-27 20:33:28 +02:00
|
|
|
goto err;
|
2007-08-01 18:43:44 +02:00
|
|
|
}
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
SMARTLIST_FOREACH_BEGIN(tokens, directory_token_t *, _tok) {
|
2009-09-16 18:34:44 +02:00
|
|
|
const char *id_hexdigest;
|
|
|
|
const char *sk_hexdigest;
|
|
|
|
const char *algname;
|
|
|
|
const char *flavor;
|
|
|
|
digest_algorithm_t alg;
|
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
char id_digest[DIGEST_LEN];
|
|
|
|
char sk_digest[DIGEST_LEN];
|
2009-09-16 18:34:44 +02:00
|
|
|
smartlist_t *siglist;
|
2009-09-16 23:01:01 +02:00
|
|
|
document_signature_t *sig;
|
2009-09-16 18:34:44 +02:00
|
|
|
int is_duplicate;
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
tok = _tok;
|
2009-09-16 18:34:44 +02:00
|
|
|
if (tok->tp == K_DIRECTORY_SIGNATURE) {
|
|
|
|
tor_assert(tok->n_args >= 2);
|
|
|
|
flavor = "ns";
|
|
|
|
algname = "sha1";
|
|
|
|
id_hexdigest = tok->args[0];
|
|
|
|
sk_hexdigest = tok->args[1];
|
|
|
|
} else if (tok->tp == K_ADDITIONAL_SIGNATURE) {
|
|
|
|
tor_assert(tok->n_args >= 4);
|
|
|
|
flavor = tok->args[0];
|
|
|
|
algname = tok->args[1];
|
|
|
|
id_hexdigest = tok->args[2];
|
|
|
|
sk_hexdigest = tok->args[3];
|
|
|
|
} else {
|
2009-09-16 23:01:01 +02:00
|
|
|
continue;
|
2009-09-16 18:34:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
int a = crypto_digest_algorithm_parse_name(algname);
|
|
|
|
if (a<0) {
|
|
|
|
log_warn(LD_DIR, "Unrecognized algorithm name %s", algname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
alg = (digest_algorithm_t) a;
|
|
|
|
}
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
if (!tok->object_type ||
|
|
|
|
strcmp(tok->object_type, "SIGNATURE") ||
|
|
|
|
tok->object_size < 128 || tok->object_size > 512) {
|
|
|
|
log_warn(LD_DIR, "Bad object type or length on directory-signature");
|
|
|
|
goto err;
|
|
|
|
}
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2009-09-16 18:34:44 +02:00
|
|
|
if (strlen(id_hexdigest) != HEX_DIGEST_LEN ||
|
2009-09-16 23:01:01 +02:00
|
|
|
base16_decode(id_digest, sizeof(id_digest),
|
2016-06-17 16:41:45 +02:00
|
|
|
id_hexdigest, HEX_DIGEST_LEN) != sizeof(id_digest)) {
|
2009-09-16 23:01:01 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding declared identity %s in "
|
2009-09-16 18:34:44 +02:00
|
|
|
"network-status vote.", escaped(id_hexdigest));
|
2009-09-16 23:01:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2009-09-23 21:23:04 +02:00
|
|
|
if (strlen(sk_hexdigest) != HEX_DIGEST_LEN ||
|
2009-09-16 23:01:01 +02:00
|
|
|
base16_decode(sk_digest, sizeof(sk_digest),
|
2016-06-17 16:41:45 +02:00
|
|
|
sk_hexdigest, HEX_DIGEST_LEN) != sizeof(sk_digest)) {
|
2009-09-23 21:23:04 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
|
2009-09-16 18:34:44 +02:00
|
|
|
"network-status vote.", escaped(sk_hexdigest));
|
2009-09-16 23:01:01 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2009-09-16 18:34:44 +02:00
|
|
|
siglist = detached_get_signatures(sigs, flavor);
|
|
|
|
is_duplicate = 0;
|
2011-07-01 17:33:07 +02:00
|
|
|
SMARTLIST_FOREACH(siglist, document_signature_t *, dsig, {
|
|
|
|
if (dsig->alg == alg &&
|
|
|
|
tor_memeq(id_digest, dsig->identity_digest, DIGEST_LEN) &&
|
|
|
|
tor_memeq(sk_digest, dsig->signing_key_digest, DIGEST_LEN)) {
|
2009-09-16 18:34:44 +02:00
|
|
|
is_duplicate = 1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
if (is_duplicate) {
|
|
|
|
log_warn(LD_DIR, "Two signatures with identical keys and algorithm "
|
|
|
|
"found.");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
sig = tor_malloc_zero(sizeof(document_signature_t));
|
2009-09-23 21:23:04 +02:00
|
|
|
sig->alg = alg;
|
2009-09-16 23:01:01 +02:00
|
|
|
memcpy(sig->identity_digest, id_digest, DIGEST_LEN);
|
|
|
|
memcpy(sig->signing_key_digest, sk_digest, DIGEST_LEN);
|
2011-01-15 19:25:13 +01:00
|
|
|
if (tok->object_size >= INT_MAX || tok->object_size >= SIZE_T_CEILING) {
|
2009-09-16 23:01:01 +02:00
|
|
|
tor_free(sig);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
sig->signature = tor_memdup(tok->object_body, tok->object_size);
|
|
|
|
sig->signature_len = (int) tok->object_size;
|
2007-07-27 20:33:28 +02:00
|
|
|
|
2009-09-16 18:34:44 +02:00
|
|
|
smartlist_add(siglist, sig);
|
2009-09-16 23:01:01 +02:00
|
|
|
} SMARTLIST_FOREACH_END(_tok);
|
2007-07-27 20:33:28 +02:00
|
|
|
|
|
|
|
goto done;
|
|
|
|
err:
|
|
|
|
ns_detached_signatures_free(sigs);
|
|
|
|
sigs = NULL;
|
|
|
|
done:
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2007-12-16 09:20:10 +01:00
|
|
|
smartlist_free(tokens);
|
2008-03-26 18:50:27 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "detached signatures");
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_drop_all(area);
|
2008-03-26 18:50:27 +01:00
|
|
|
}
|
2007-08-01 18:43:44 +02:00
|
|
|
return sigs;
|
2007-07-27 20:33:28 +02:00
|
|
|
}
|
|
|
|
|
2006-03-27 04:25:34 +02:00
|
|
|
/** Parse the addr policy in the string <b>s</b> and return it. If
|
2005-08-08 23:58:48 +02:00
|
|
|
* assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or
|
|
|
|
* ADDR_POLICY_REJECT) for items that specify no action.
|
2012-10-24 21:03:29 +02:00
|
|
|
*
|
2015-09-11 07:10:54 +02:00
|
|
|
* Returns NULL on policy errors.
|
|
|
|
*
|
2016-02-12 04:06:44 +01:00
|
|
|
* Set *<b>malformed_list</b> to true if the entire policy list should be
|
2015-12-16 14:49:32 +01:00
|
|
|
* discarded. Otherwise, set it to false, and only this item should be ignored
|
|
|
|
* on error - the rest of the policy list can continue to be processed and
|
|
|
|
* used.
|
2015-09-11 07:10:54 +02:00
|
|
|
*
|
2012-10-24 21:03:29 +02:00
|
|
|
* The addr_policy_t returned by this function can have its address set to
|
|
|
|
* AF_UNSPEC for '*'. Use policy_expand_unspec() to turn this into a pair
|
|
|
|
* of AF_INET and AF_INET6 items.
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
2014-08-27 12:41:25 +02:00
|
|
|
MOCK_IMPL(addr_policy_t *,
|
2015-09-11 07:10:54 +02:00
|
|
|
router_parse_addr_policy_item_from_string,(const char *s, int assume_action,
|
|
|
|
int *malformed_list))
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
|
|
|
directory_token_t *tok = NULL;
|
2007-09-18 17:38:00 +02:00
|
|
|
const char *cp, *eos;
|
2015-04-23 15:51:59 +02:00
|
|
|
/* Longest possible policy is
|
2015-12-15 22:49:06 +01:00
|
|
|
* "accept6 [ffff:ffff:..255]/128:10000-65535",
|
|
|
|
* which contains a max-length IPv6 address, plus 26 characters.
|
2007-09-18 17:38:00 +02:00
|
|
|
* But note that there can be an arbitrary amount of space between the
|
2015-09-09 15:48:33 +02:00
|
|
|
* accept and the address:mask/port element.
|
|
|
|
* We don't need to multiply TOR_ADDR_BUF_LEN by 2, as there is only one
|
|
|
|
* IPv6 address. But making the buffer shorter might cause valid long lines,
|
|
|
|
* which parsed in previous versions, to fail to parse in new versions.
|
|
|
|
* (These lines would have to have excessive amounts of whitespace.) */
|
2007-09-18 17:38:00 +02:00
|
|
|
char line[TOR_ADDR_BUF_LEN*2 + 32];
|
2004-12-04 02:14:36 +01:00
|
|
|
addr_policy_t *r;
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_t *area = NULL;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2015-09-11 07:10:54 +02:00
|
|
|
tor_assert(malformed_list);
|
2015-12-15 22:50:13 +01:00
|
|
|
*malformed_list = 0;
|
2015-09-11 07:10:54 +02:00
|
|
|
|
2007-09-18 17:38:00 +02:00
|
|
|
s = eat_whitespace(s);
|
2015-12-15 22:49:06 +01:00
|
|
|
/* We can only do assume_action on []-quoted IPv6, as "a" (accept)
|
|
|
|
* and ":" (port separator) are ambiguous */
|
|
|
|
if ((*s == '*' || *s == '[' || TOR_ISDIGIT(*s)) && assume_action >= 0) {
|
2007-09-18 17:38:00 +02:00
|
|
|
if (tor_snprintf(line, sizeof(line), "%s %s",
|
|
|
|
assume_action == ADDR_POLICY_ACCEPT?"accept":"reject", s)<0) {
|
|
|
|
log_warn(LD_DIR, "Policy %s is too long.", escaped(s));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
cp = line;
|
2007-10-11 01:44:10 +02:00
|
|
|
tor_strlower(line);
|
2007-09-18 17:38:00 +02:00
|
|
|
} else { /* assume an already well-formed address policy line */
|
|
|
|
cp = s;
|
2005-08-08 23:58:48 +02:00
|
|
|
}
|
2007-09-18 17:38:00 +02:00
|
|
|
|
2007-07-11 00:44:32 +02:00
|
|
|
eos = cp + strlen(cp);
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2008-03-26 17:33:33 +01:00
|
|
|
tok = get_next_token(area, &cp, eos, routerdesc_token_table);
|
2012-10-12 18:22:13 +02:00
|
|
|
if (tok->tp == ERR_) {
|
2006-03-27 04:25:34 +02:00
|
|
|
log_warn(LD_DIR, "Error reading address policy: %s", tok->error);
|
2004-05-10 19:30:51 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2008-07-24 15:44:04 +02:00
|
|
|
if (tok->tp != K_ACCEPT && tok->tp != K_ACCEPT6 &&
|
|
|
|
tok->tp != K_REJECT && tok->tp != K_REJECT6) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Expected 'accept' or 'reject'.");
|
2004-05-10 19:30:51 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2015-09-11 07:10:54 +02:00
|
|
|
/* Use the extended interpretation of accept/reject *,
|
|
|
|
* expanding it into an IPv4 wildcard and an IPv6 wildcard.
|
|
|
|
* Also permit *4 and *6 for IPv4 and IPv6 only wildcards. */
|
2012-10-24 21:03:29 +02:00
|
|
|
r = router_parse_addr_policy(tok, TAPMP_EXTENDED_STAR);
|
2015-09-11 07:10:54 +02:00
|
|
|
if (!r) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Ensure that accept6/reject6 fields are followed by IPv6 addresses.
|
|
|
|
* AF_UNSPEC addresses are only permitted on the accept/reject field type.
|
|
|
|
* Unlike descriptors, torrcs exit policy accept/reject can be followed by
|
|
|
|
* either an IPv4 or IPv6 address. */
|
|
|
|
if ((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
|
|
|
|
tor_addr_family(&r->addr) != AF_INET6) {
|
|
|
|
/* This is a non-fatal error, just ignore this one entry. */
|
|
|
|
*malformed_list = 0;
|
|
|
|
log_warn(LD_DIR, "IPv4 address '%s' with accept6/reject6 field type in "
|
|
|
|
"exit policy. Ignoring, but continuing to parse rules. (Use "
|
|
|
|
"accept/reject with IPv4 addresses.)",
|
|
|
|
tok->n_args == 1 ? tok->args[0] : "");
|
2015-09-22 15:55:05 +02:00
|
|
|
addr_policy_free(r);
|
|
|
|
r = NULL;
|
|
|
|
goto done;
|
2015-09-11 07:10:54 +02:00
|
|
|
}
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
goto done;
|
|
|
|
err:
|
2015-09-11 07:10:54 +02:00
|
|
|
*malformed_list = 1;
|
2004-05-20 04:42:50 +02:00
|
|
|
r = NULL;
|
2004-05-10 19:30:51 +02:00
|
|
|
done:
|
2009-09-28 16:37:01 +02:00
|
|
|
token_clear(tok);
|
2008-03-26 18:50:27 +01:00
|
|
|
if (area) {
|
|
|
|
DUMP_AREA(area, "policy item");
|
2008-03-26 17:33:33 +01:00
|
|
|
memarea_drop_all(area);
|
2008-03-26 18:50:27 +01:00
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2005-09-13 23:14:55 +02:00
|
|
|
/** Add an exit policy stored in the token <b>tok</b> to the router info in
|
|
|
|
* <b>router</b>. Return 0 on success, -1 on failure. */
|
2004-11-12 20:39:13 +01:00
|
|
|
static int
|
2005-09-13 23:14:55 +02:00
|
|
|
router_add_exit_policy(routerinfo_t *router, directory_token_t *tok)
|
2004-05-20 04:42:50 +02:00
|
|
|
{
|
2008-01-02 05:43:44 +01:00
|
|
|
addr_policy_t *newe;
|
2015-09-11 07:10:54 +02:00
|
|
|
/* Use the standard interpretation of accept/reject *, an IPv4 wildcard. */
|
2012-10-24 21:03:29 +02:00
|
|
|
newe = router_parse_addr_policy(tok, 0);
|
2004-05-20 04:42:50 +02:00
|
|
|
if (!newe)
|
|
|
|
return -1;
|
2008-01-02 05:43:44 +01:00
|
|
|
if (! router->exit_policy)
|
2012-01-18 21:53:30 +01:00
|
|
|
router->exit_policy = smartlist_new();
|
2008-01-02 05:43:44 +01:00
|
|
|
|
2015-09-11 07:10:54 +02:00
|
|
|
/* Ensure that in descriptors, accept/reject fields are followed by
|
|
|
|
* IPv4 addresses, and accept6/reject6 fields are followed by
|
|
|
|
* IPv6 addresses. Unlike torrcs, descriptor exit policies do not permit
|
|
|
|
* accept/reject followed by IPv6. */
|
2008-07-24 15:44:04 +02:00
|
|
|
if (((tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) &&
|
|
|
|
tor_addr_family(&newe->addr) == AF_INET)
|
|
|
|
||
|
|
|
|
((tok->tp == K_ACCEPT || tok->tp == K_REJECT) &&
|
|
|
|
tor_addr_family(&newe->addr) == AF_INET6)) {
|
2015-09-11 07:10:54 +02:00
|
|
|
/* There's nothing the user can do about other relays' descriptors,
|
|
|
|
* so we don't provide usage advice here. */
|
2008-07-24 15:44:04 +02:00
|
|
|
log_warn(LD_DIR, "Mismatch between field type and address type in exit "
|
2015-09-15 19:59:30 +02:00
|
|
|
"policy '%s'. Discarding entire router descriptor.",
|
|
|
|
tok->n_args == 1 ? tok->args[0] : "");
|
2008-07-24 15:44:04 +02:00
|
|
|
addr_policy_free(newe);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-01-02 05:43:44 +01:00
|
|
|
smartlist_add(router->exit_policy, newe);
|
2004-05-20 04:42:50 +02:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-09-15 19:58:06 +02:00
|
|
|
/** Given a K_ACCEPT[6] or K_REJECT[6] token and a router, create and return
|
|
|
|
* a new exit_policy_t corresponding to the token. If TAPMP_EXTENDED_STAR
|
|
|
|
* is set in fmt_flags, K_ACCEPT6 and K_REJECT6 tokens followed by *
|
|
|
|
* expand to IPv6-only policies, otherwise they expand to IPv4 and IPv6
|
|
|
|
* policies */
|
2004-12-04 02:14:36 +01:00
|
|
|
static addr_policy_t *
|
2012-10-24 21:03:29 +02:00
|
|
|
router_parse_addr_policy(directory_token_t *tok, unsigned fmt_flags)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2008-01-02 05:43:44 +01:00
|
|
|
addr_policy_t newe;
|
2005-02-22 09:18:36 +01:00
|
|
|
char *arg;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-07-24 15:44:04 +02:00
|
|
|
tor_assert(tok->tp == K_REJECT || tok->tp == K_REJECT6 ||
|
|
|
|
tok->tp == K_ACCEPT || tok->tp == K_ACCEPT6);
|
2004-05-10 19:30:51 +02:00
|
|
|
|
|
|
|
if (tok->n_args != 1)
|
2004-05-20 04:42:50 +02:00
|
|
|
return NULL;
|
2004-05-10 19:30:51 +02:00
|
|
|
arg = tok->args[0];
|
|
|
|
|
2006-03-05 06:27:59 +01:00
|
|
|
if (!strcmpstart(arg,"private"))
|
2006-12-29 21:50:24 +01:00
|
|
|
return router_parse_addr_policy_private(tok);
|
2006-03-05 06:27:59 +01:00
|
|
|
|
2008-01-02 05:43:44 +01:00
|
|
|
memset(&newe, 0, sizeof(newe));
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-07-24 15:44:04 +02:00
|
|
|
if (tok->tp == K_REJECT || tok->tp == K_REJECT6)
|
|
|
|
newe.policy_type = ADDR_POLICY_REJECT;
|
|
|
|
else
|
|
|
|
newe.policy_type = ADDR_POLICY_ACCEPT;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2015-09-14 03:46:58 +02:00
|
|
|
/* accept6/reject6 * produces an IPv6 wildcard address only.
|
|
|
|
* (accept/reject * produces rules for IPv4 and IPv6 wildcard addresses.) */
|
2015-09-15 19:58:06 +02:00
|
|
|
if ((fmt_flags & TAPMP_EXTENDED_STAR)
|
|
|
|
&& (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6)) {
|
2015-09-14 03:46:58 +02:00
|
|
|
fmt_flags |= TAPMP_STAR_IPV6_ONLY;
|
|
|
|
}
|
|
|
|
|
2012-10-24 21:03:29 +02:00
|
|
|
if (tor_addr_parse_mask_ports(arg, fmt_flags, &newe.addr, &newe.maskbits,
|
2008-07-24 15:44:04 +02:00
|
|
|
&newe.prt_min, &newe.prt_max) < 0) {
|
|
|
|
log_warn(LD_DIR,"Couldn't parse line %s. Dropping", escaped(arg));
|
|
|
|
return NULL;
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-01-02 05:43:44 +01:00
|
|
|
return addr_policy_get_canonical_entry(&newe);
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
2015-09-14 03:46:58 +02:00
|
|
|
/** Parse an exit policy line of the format "accept[6]/reject[6] private:...".
|
2006-03-05 06:27:59 +01:00
|
|
|
* This didn't exist until Tor 0.1.1.15, so nobody should generate it in
|
|
|
|
* router descriptors until earlier versions are obsolete.
|
2015-09-14 03:46:58 +02:00
|
|
|
*
|
|
|
|
* accept/reject and accept6/reject6 private all produce rules for both
|
|
|
|
* IPv4 and IPv6 addresses.
|
2006-03-05 06:27:59 +01:00
|
|
|
*/
|
|
|
|
static addr_policy_t *
|
2006-12-29 21:50:24 +01:00
|
|
|
router_parse_addr_policy_private(directory_token_t *tok)
|
2006-03-05 06:27:59 +01:00
|
|
|
{
|
2008-01-02 05:43:44 +01:00
|
|
|
const char *arg;
|
2006-03-05 06:27:59 +01:00
|
|
|
uint16_t port_min, port_max;
|
2008-01-02 05:43:44 +01:00
|
|
|
addr_policy_t result;
|
2006-03-05 06:27:59 +01:00
|
|
|
|
|
|
|
arg = tok->args[0];
|
|
|
|
if (strcmpstart(arg, "private"))
|
|
|
|
return NULL;
|
2008-01-02 05:43:44 +01:00
|
|
|
|
2006-03-05 06:27:59 +01:00
|
|
|
arg += strlen("private");
|
|
|
|
arg = (char*) eat_whitespace(arg);
|
|
|
|
if (!arg || *arg != ':')
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (parse_port_range(arg+1, &port_min, &port_max)<0)
|
|
|
|
return NULL;
|
|
|
|
|
2008-01-02 05:43:44 +01:00
|
|
|
memset(&result, 0, sizeof(result));
|
2008-07-24 15:44:04 +02:00
|
|
|
if (tok->tp == K_REJECT || tok->tp == K_REJECT6)
|
|
|
|
result.policy_type = ADDR_POLICY_REJECT;
|
|
|
|
else
|
|
|
|
result.policy_type = ADDR_POLICY_ACCEPT;
|
2008-01-02 05:43:44 +01:00
|
|
|
result.is_private = 1;
|
|
|
|
result.prt_min = port_min;
|
|
|
|
result.prt_max = port_max;
|
2006-03-05 06:27:59 +01:00
|
|
|
|
2015-09-11 07:10:54 +02:00
|
|
|
if (tok->tp == K_ACCEPT6 || tok->tp == K_REJECT6) {
|
|
|
|
log_warn(LD_GENERAL,
|
|
|
|
"'%s' expands into rules which apply to all private IPv4 and "
|
|
|
|
"IPv6 addresses. (Use accept/reject private:* for IPv4 and "
|
|
|
|
"IPv6.)", tok->n_args == 1 ? tok->args[0] : "");
|
|
|
|
}
|
|
|
|
|
2008-01-02 05:43:44 +01:00
|
|
|
return addr_policy_get_canonical_entry(&result);
|
2006-03-05 06:27:59 +01:00
|
|
|
}
|
|
|
|
|
2005-09-13 23:14:55 +02:00
|
|
|
/** Log and exit if <b>t</b> is malformed */
|
2004-12-04 02:14:36 +01:00
|
|
|
void
|
2008-01-02 05:43:44 +01:00
|
|
|
assert_addr_policy_ok(smartlist_t *lst)
|
2004-12-04 02:14:36 +01:00
|
|
|
{
|
2008-01-02 05:43:44 +01:00
|
|
|
if (!lst) return;
|
|
|
|
SMARTLIST_FOREACH(lst, addr_policy_t *, t, {
|
2004-12-04 02:14:36 +01:00
|
|
|
tor_assert(t->policy_type == ADDR_POLICY_REJECT ||
|
|
|
|
t->policy_type == ADDR_POLICY_ACCEPT);
|
|
|
|
tor_assert(t->prt_min <= t->prt_max);
|
2008-01-02 05:43:44 +01:00
|
|
|
});
|
2004-12-04 02:14:36 +01:00
|
|
|
}
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
/** Return a newly allocated smartlist of all accept or reject tokens in
|
|
|
|
* <b>s</b>.
|
|
|
|
*/
|
|
|
|
static smartlist_t *
|
|
|
|
find_all_exitpolicy(smartlist_t *s)
|
|
|
|
{
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *out = smartlist_new();
|
2004-05-10 19:30:51 +02:00
|
|
|
SMARTLIST_FOREACH(s, directory_token_t *, t,
|
2008-07-24 15:44:04 +02:00
|
|
|
if (t->tp == K_ACCEPT || t->tp == K_ACCEPT6 ||
|
|
|
|
t->tp == K_REJECT || t->tp == K_REJECT6)
|
|
|
|
smartlist_add(out,t));
|
2004-05-10 19:30:51 +02:00
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2012-06-05 06:17:54 +02:00
|
|
|
/** Helper function for <b>router_get_hash_impl</b>: given <b>s</b>,
|
|
|
|
* <b>s_len</b>, <b>start_str</b>, <b>end_str</b>, and <b>end_c</b> with the
|
|
|
|
* same semantics as in that function, set *<b>start_out</b> (inclusive) and
|
|
|
|
* *<b>end_out</b> (exclusive) to the boundaries of the string to be hashed.
|
|
|
|
*
|
|
|
|
* Return 0 on success and -1 on failure.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static int
|
2010-02-27 21:34:02 +01:00
|
|
|
router_get_hash_impl_helper(const char *s, size_t s_len,
|
2009-09-16 23:01:01 +02:00
|
|
|
const char *start_str,
|
|
|
|
const char *end_str, char end_c,
|
2017-05-03 17:48:08 +02:00
|
|
|
int log_severity,
|
2009-09-16 23:01:01 +02:00
|
|
|
const char **start_out, const char **end_out)
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
2010-02-25 10:31:36 +01:00
|
|
|
const char *start, *end;
|
|
|
|
start = tor_memstr(s, s_len, start_str);
|
2004-05-10 19:30:51 +02:00
|
|
|
if (!start) {
|
2017-05-03 17:48:08 +02:00
|
|
|
log_fn(log_severity,LD_DIR,
|
|
|
|
"couldn't find start of hashed material \"%s\"",start_str);
|
2004-05-10 19:30:51 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2004-08-06 21:54:20 +02:00
|
|
|
if (start != s && *(start-1) != '\n') {
|
2017-05-03 17:48:08 +02:00
|
|
|
log_fn(log_severity,LD_DIR,
|
2006-02-13 11:33:00 +01:00
|
|
|
"first occurrence of \"%s\" is not at the start of a line",
|
|
|
|
start_str);
|
2004-08-06 21:54:20 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2010-02-25 10:31:36 +01:00
|
|
|
end = tor_memstr(start+strlen(start_str),
|
|
|
|
s_len - (start-s) - strlen(start_str), end_str);
|
2004-05-10 19:30:51 +02:00
|
|
|
if (!end) {
|
2017-05-03 17:48:08 +02:00
|
|
|
log_fn(log_severity,LD_DIR,
|
|
|
|
"couldn't find end of hashed material \"%s\"",end_str);
|
2004-05-10 19:30:51 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2010-02-25 10:31:36 +01:00
|
|
|
end = memchr(end+strlen(end_str), end_c, s_len - (end-s) - strlen(end_str));
|
2004-05-10 19:30:51 +02:00
|
|
|
if (!end) {
|
2017-05-03 17:48:08 +02:00
|
|
|
log_fn(log_severity,LD_DIR,
|
|
|
|
"couldn't find EOL");
|
2004-05-10 19:30:51 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
++end;
|
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
*start_out = start;
|
|
|
|
*end_out = end;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Compute the digest of the substring of <b>s</b> taken from the first
|
|
|
|
* occurrence of <b>start_str</b> through the first instance of c after the
|
2012-11-23 16:51:11 +01:00
|
|
|
* first subsequent occurrence of <b>end_str</b>; store the 20-byte or 32-byte
|
|
|
|
* result in <b>digest</b>; return 0 on success.
|
2009-09-16 23:01:01 +02:00
|
|
|
*
|
|
|
|
* If no such substring exists, return -1.
|
|
|
|
*/
|
|
|
|
static int
|
2010-02-27 21:34:02 +01:00
|
|
|
router_get_hash_impl(const char *s, size_t s_len, char *digest,
|
2009-09-16 23:01:01 +02:00
|
|
|
const char *start_str,
|
|
|
|
const char *end_str, char end_c,
|
|
|
|
digest_algorithm_t alg)
|
|
|
|
{
|
|
|
|
const char *start=NULL, *end=NULL;
|
2017-05-03 17:48:08 +02:00
|
|
|
if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,LOG_WARN,
|
2010-02-27 22:33:22 +01:00
|
|
|
&start,&end)<0)
|
2009-09-16 23:01:01 +02:00
|
|
|
return -1;
|
|
|
|
|
2016-12-14 02:22:34 +01:00
|
|
|
return router_compute_hash_final(digest, start, end-start, alg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Compute the digest of the <b>len</b>-byte directory object at
|
|
|
|
* <b>start</b>, using <b>alg</b>. Store the result in <b>digest</b>, which
|
|
|
|
* must be long enough to hold it. */
|
|
|
|
MOCK_IMPL(STATIC int,
|
|
|
|
router_compute_hash_final,(char *digest,
|
|
|
|
const char *start, size_t len,
|
|
|
|
digest_algorithm_t alg))
|
|
|
|
{
|
2009-08-24 18:51:33 +02:00
|
|
|
if (alg == DIGEST_SHA1) {
|
2016-12-14 02:22:34 +01:00
|
|
|
if (crypto_digest(digest, start, len) < 0) {
|
2009-08-24 18:51:33 +02:00
|
|
|
log_warn(LD_BUG,"couldn't compute digest");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
2016-12-14 02:22:34 +01:00
|
|
|
if (crypto_digest256(digest, start, len, alg) < 0) {
|
2009-08-24 18:51:33 +02:00
|
|
|
log_warn(LD_BUG,"couldn't compute digest");
|
|
|
|
return -1;
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
/** As router_get_hash_impl, but compute all hashes. */
|
|
|
|
static int
|
2016-02-10 21:28:19 +01:00
|
|
|
router_get_hashes_impl(const char *s, size_t s_len, common_digests_t *digests,
|
2009-09-16 23:01:01 +02:00
|
|
|
const char *start_str,
|
|
|
|
const char *end_str, char end_c)
|
|
|
|
{
|
|
|
|
const char *start=NULL, *end=NULL;
|
2017-05-03 17:48:08 +02:00
|
|
|
if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,LOG_WARN,
|
2010-02-27 22:33:22 +01:00
|
|
|
&start,&end)<0)
|
2009-09-16 23:01:01 +02:00
|
|
|
return -1;
|
|
|
|
|
2016-02-10 21:28:19 +01:00
|
|
|
if (crypto_common_digests(digests, start, end-start)) {
|
2009-09-16 23:01:01 +02:00
|
|
|
log_warn(LD_BUG,"couldn't compute digests");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Assuming that s starts with a microdesc, return the start of the
|
|
|
|
* *NEXT* one. Return NULL on "not found." */
|
2009-08-24 18:51:33 +02:00
|
|
|
static const char *
|
|
|
|
find_start_of_next_microdesc(const char *s, const char *eos)
|
|
|
|
{
|
|
|
|
int started_with_annotations;
|
|
|
|
s = eat_whitespace_eos(s, eos);
|
|
|
|
if (!s)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#define CHECK_LENGTH() STMT_BEGIN \
|
|
|
|
if (s+32 > eos) \
|
|
|
|
return NULL; \
|
|
|
|
STMT_END
|
|
|
|
|
|
|
|
#define NEXT_LINE() STMT_BEGIN \
|
|
|
|
s = memchr(s, '\n', eos-s); \
|
|
|
|
if (!s || s+1 >= eos) \
|
|
|
|
return NULL; \
|
|
|
|
s++; \
|
|
|
|
STMT_END
|
|
|
|
|
|
|
|
CHECK_LENGTH();
|
|
|
|
|
|
|
|
started_with_annotations = (*s == '@');
|
|
|
|
|
|
|
|
if (started_with_annotations) {
|
|
|
|
/* Start by advancing to the first non-annotation line. */
|
|
|
|
while (*s == '@')
|
|
|
|
NEXT_LINE();
|
|
|
|
}
|
|
|
|
CHECK_LENGTH();
|
|
|
|
|
|
|
|
/* Now we should be pointed at an onion-key line. If we are, then skip
|
|
|
|
* it. */
|
|
|
|
if (!strcmpstart(s, "onion-key"))
|
|
|
|
NEXT_LINE();
|
|
|
|
|
|
|
|
/* Okay, now we're pointed at the first line of the microdescriptor which is
|
|
|
|
not an annotation or onion-key. The next line that _is_ an annotation or
|
|
|
|
onion-key is the start of the next microdescriptor. */
|
|
|
|
while (s+32 < eos) {
|
|
|
|
if (*s == '@' || !strcmpstart(s, "onion-key"))
|
|
|
|
return s;
|
|
|
|
NEXT_LINE();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
#undef CHECK_LENGTH
|
|
|
|
#undef NEXT_LINE
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Parse as many microdescriptors as are found from the string starting at
|
|
|
|
* <b>s</b> and ending at <b>eos</b>. If allow_annotations is set, read any
|
2013-12-16 19:00:15 +01:00
|
|
|
* annotations we recognize and ignore ones we don't.
|
|
|
|
*
|
|
|
|
* If <b>saved_location</b> isn't SAVED_IN_CACHE, make a local copy of each
|
|
|
|
* descriptor in the body field of each microdesc_t.
|
|
|
|
*
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
* Return all newly parsed microdescriptors in a newly allocated
|
|
|
|
* smartlist_t. If <b>invalid_disgests_out</b> is provided, add a SHA256
|
|
|
|
* microdesc digest to it for every microdesc that we found to be badly
|
2014-10-13 20:15:56 +02:00
|
|
|
* formed. (This may cause duplicates) */
|
2009-08-24 18:51:33 +02:00
|
|
|
smartlist_t *
|
|
|
|
microdescs_parse_from_string(const char *s, const char *eos,
|
2013-12-16 19:00:15 +01:00
|
|
|
int allow_annotations,
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
saved_location_t where,
|
|
|
|
smartlist_t *invalid_digests_out)
|
2009-08-24 18:51:33 +02:00
|
|
|
{
|
|
|
|
smartlist_t *tokens;
|
|
|
|
smartlist_t *result;
|
|
|
|
microdesc_t *md = NULL;
|
|
|
|
memarea_t *area;
|
|
|
|
const char *start = s;
|
|
|
|
const char *start_of_next_microdesc;
|
|
|
|
int flags = allow_annotations ? TS_ANNOTATIONS_OK : 0;
|
2013-12-16 19:00:15 +01:00
|
|
|
const int copy_body = (where != SAVED_IN_CACHE);
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
directory_token_t *tok;
|
|
|
|
|
|
|
|
if (!eos)
|
|
|
|
eos = s + strlen(s);
|
|
|
|
|
|
|
|
s = eat_whitespace_eos(s, eos);
|
|
|
|
area = memarea_new();
|
2012-01-18 21:53:30 +01:00
|
|
|
result = smartlist_new();
|
|
|
|
tokens = smartlist_new();
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
while (s < eos) {
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
int okay = 0;
|
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
start_of_next_microdesc = find_start_of_next_microdesc(s, eos);
|
|
|
|
if (!start_of_next_microdesc)
|
|
|
|
start_of_next_microdesc = eos;
|
|
|
|
|
|
|
|
md = tor_malloc_zero(sizeof(microdesc_t));
|
|
|
|
{
|
|
|
|
const char *cp = tor_memstr(s, start_of_next_microdesc-s,
|
|
|
|
"onion-key");
|
2015-06-22 19:51:56 +02:00
|
|
|
const int no_onion_key = (cp == NULL);
|
|
|
|
if (no_onion_key) {
|
|
|
|
cp = s; /* So that we have *some* junk to put in the body */
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
md->bodylen = start_of_next_microdesc - cp;
|
2013-12-16 19:00:15 +01:00
|
|
|
md->saved_location = where;
|
2009-08-24 18:51:33 +02:00
|
|
|
if (copy_body)
|
2013-01-27 00:01:06 +01:00
|
|
|
md->body = tor_memdup_nulterm(cp, md->bodylen);
|
2009-08-24 18:51:33 +02:00
|
|
|
else
|
|
|
|
md->body = (char*)cp;
|
|
|
|
md->off = cp - start;
|
2015-06-22 19:51:56 +02:00
|
|
|
crypto_digest256(md->digest, md->body, md->bodylen, DIGEST_SHA256);
|
|
|
|
if (no_onion_key) {
|
|
|
|
log_fn(LOG_PROTOCOL_WARN, LD_DIR, "Malformed or truncated descriptor");
|
|
|
|
goto next;
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
2015-06-22 19:51:56 +02:00
|
|
|
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
if (tokenize_string(area, s, start_of_next_microdesc, tokens,
|
|
|
|
microdesc_token_table, flags)) {
|
|
|
|
log_warn(LD_DIR, "Unparseable microdescriptor");
|
|
|
|
goto next;
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, A_LAST_LISTED))) {
|
|
|
|
if (parse_iso_time(tok->args[0], &md->last_listed)) {
|
|
|
|
log_warn(LD_DIR, "Bad last-listed time in microdescriptor");
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tok = find_by_keyword(tokens, K_ONION_KEY);
|
2011-05-18 01:45:05 +02:00
|
|
|
if (!crypto_pk_public_exponent_ok(tok->key)) {
|
2011-04-29 00:16:54 +02:00
|
|
|
log_warn(LD_DIR,
|
|
|
|
"Relay's onion key had invalid exponent.");
|
|
|
|
goto next;
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
md->onion_pkey = tok->key;
|
|
|
|
tok->key = NULL;
|
|
|
|
|
2012-12-04 21:58:18 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_ONION_KEY_NTOR))) {
|
2013-01-06 04:53:32 +01:00
|
|
|
curve25519_public_key_t k;
|
2012-12-04 21:58:18 +01:00
|
|
|
tor_assert(tok->n_args >= 1);
|
2013-01-06 04:53:32 +01:00
|
|
|
if (curve25519_public_from_base64(&k, tok->args[0]) < 0) {
|
|
|
|
log_warn(LD_DIR, "Bogus ntor-onion-key in microdesc");
|
2012-12-04 21:58:18 +01:00
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
md->onion_curve25519_pkey =
|
2013-01-06 04:53:32 +01:00
|
|
|
tor_memdup(&k, sizeof(curve25519_public_key_t));
|
2012-12-04 21:58:18 +01:00
|
|
|
}
|
|
|
|
|
2014-10-08 17:33:59 +02:00
|
|
|
smartlist_t *id_lines = find_all_by_keyword(tokens, K_ID);
|
|
|
|
if (id_lines) {
|
|
|
|
SMARTLIST_FOREACH_BEGIN(id_lines, directory_token_t *, t) {
|
|
|
|
tor_assert(t->n_args >= 2);
|
|
|
|
if (!strcmp(t->args[0], "ed25519")) {
|
|
|
|
if (md->ed25519_identity_pkey) {
|
|
|
|
log_warn(LD_DIR, "Extra ed25519 key in microdesc");
|
2017-01-10 20:13:45 +01:00
|
|
|
smartlist_free(id_lines);
|
2014-10-08 17:33:59 +02:00
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
ed25519_public_key_t k;
|
|
|
|
if (ed25519_public_from_base64(&k, t->args[1])<0) {
|
|
|
|
log_warn(LD_DIR, "Bogus ed25519 key in microdesc");
|
2017-01-10 20:13:45 +01:00
|
|
|
smartlist_free(id_lines);
|
2014-10-08 17:33:59 +02:00
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
md->ed25519_identity_pkey = tor_memdup(&k, sizeof(k));
|
|
|
|
}
|
|
|
|
} SMARTLIST_FOREACH_END(t);
|
|
|
|
smartlist_free(id_lines);
|
|
|
|
}
|
|
|
|
|
2012-08-31 23:02:19 +02:00
|
|
|
{
|
|
|
|
smartlist_t *a_lines = find_all_by_keyword(tokens, K_A);
|
|
|
|
if (a_lines) {
|
|
|
|
find_single_ipv6_orport(a_lines, &md->ipv6_addr, &md->ipv6_orport);
|
|
|
|
smartlist_free(a_lines);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_FAMILY))) {
|
|
|
|
int i;
|
2012-01-18 21:53:30 +01:00
|
|
|
md->family = smartlist_new();
|
2009-08-24 18:51:33 +02:00
|
|
|
for (i=0;i<tok->n_args;++i) {
|
|
|
|
if (!is_legal_nickname_or_hexdigest(tok->args[i])) {
|
|
|
|
log_warn(LD_DIR, "Illegal nickname %s in family line",
|
|
|
|
escaped(tok->args[i]));
|
|
|
|
goto next;
|
|
|
|
}
|
2016-10-27 11:26:06 +02:00
|
|
|
smartlist_add_strdup(md->family, tok->args[i]);
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_P))) {
|
2010-10-02 00:12:30 +02:00
|
|
|
md->exit_policy = parse_short_policy(tok->args[0]);
|
2009-08-24 18:51:33 +02:00
|
|
|
}
|
2012-10-25 03:59:55 +02:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_P6))) {
|
|
|
|
md->ipv6_exit_policy = parse_short_policy(tok->args[0]);
|
|
|
|
}
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
smartlist_add(result, md);
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
okay = 1;
|
2009-08-24 18:51:33 +02:00
|
|
|
|
|
|
|
md = NULL;
|
|
|
|
next:
|
Treat unparseable (micro)descriptors and extrainfos as undownloadable
One pain point in evolving the Tor design and implementing has been
adding code that makes clients reject directory documents that they
previously would have accepted, if those descriptors actually exist.
When this happened, the clients would get the document, reject it,
and then decide to try downloading it again, ad infinitum. This
problem becomes particularly obnoxious with authorities, since if
some authorities accept a descriptor that others don't, the ones
that don't accept it would go crazy trying to re-fetch it over and
over. (See for example ticket #9286.)
This patch tries to solve this problem by tracking, if a descriptor
isn't parseable, what its digest was, and whether it is invalid
because of some flaw that applies to the portion containing the
digest. (This excludes RSA signature problems: RSA signatures
aren't included in the digest. This means that a directory
authority can still put another directory authority into a loop by
mentioning a descriptor, and then serving that descriptor with an
invalid RSA signatures. But that would also make the misbehaving
directory authority get DoSed by the server it's attacking, so it's
not much of an issue.)
We already have a mechanism to mark something undownloadable with
downloadstatus_mark_impossible(); we use that here for
microdescriptors, extrainfos, and router descriptors.
Unit tests to follow in another patch.
Closes ticket #11243.
2014-10-03 16:55:50 +02:00
|
|
|
if (! okay && invalid_digests_out) {
|
|
|
|
smartlist_add(invalid_digests_out,
|
|
|
|
tor_memdup(md->digest, DIGEST256_LEN));
|
|
|
|
}
|
2009-12-12 08:07:59 +01:00
|
|
|
microdesc_free(md);
|
2011-04-27 20:10:56 +02:00
|
|
|
md = NULL;
|
2009-08-24 18:51:33 +02:00
|
|
|
|
2014-04-26 18:45:34 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2009-08-24 18:51:33 +02:00
|
|
|
memarea_clear(area);
|
|
|
|
smartlist_clear(tokens);
|
|
|
|
s = start_of_next_microdesc;
|
|
|
|
}
|
|
|
|
|
2014-04-26 18:45:34 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2009-08-24 18:51:33 +02:00
|
|
|
memarea_drop_all(area);
|
|
|
|
smartlist_free(tokens);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2017-02-14 09:52:01 +01:00
|
|
|
/** Extract a Tor version from a <b>platform</b> line from a router
|
|
|
|
* descriptor, and place the result in <b>router_version</b>.
|
|
|
|
*
|
|
|
|
* Return 1 on success, -1 on parsing failure, and 0 if the
|
|
|
|
* platform line does not indicate some version of Tor.
|
|
|
|
*
|
|
|
|
* If <b>strict</b> is non-zero, finding any weird version components
|
|
|
|
* (like negative numbers) counts as a parsing failure.
|
2004-09-27 08:00:43 +02:00
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
2017-02-13 17:17:16 +01:00
|
|
|
tor_version_parse_platform(const char *platform,
|
|
|
|
tor_version_t *router_version,
|
|
|
|
int strict)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2004-09-27 08:00:43 +02:00
|
|
|
char tmp[128];
|
2017-02-13 17:17:16 +01:00
|
|
|
char *s, *s2, *start;
|
2004-09-27 08:00:43 +02:00
|
|
|
|
2017-02-13 17:17:16 +01:00
|
|
|
if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; say 0. */
|
2004-09-27 08:00:43 +02:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
start = (char *)eat_whitespace(platform+3);
|
2017-02-13 17:17:16 +01:00
|
|
|
if (!*start) return -1;
|
2004-09-27 08:00:43 +02:00
|
|
|
s = (char *)find_whitespace(start); /* also finds '\0', which is fine */
|
2007-04-17 02:58:30 +02:00
|
|
|
s2 = (char*)eat_whitespace(s);
|
2009-06-12 19:38:37 +02:00
|
|
|
if (!strcmpstart(s2, "(r") || !strcmpstart(s2, "(git-"))
|
2007-04-17 02:58:30 +02:00
|
|
|
s = (char*)find_whitespace(s2);
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if ((size_t)(s-start+1) >= sizeof(tmp)) /* too big, no */
|
2017-02-13 17:17:16 +01:00
|
|
|
return -1;
|
2004-09-27 08:00:43 +02:00
|
|
|
strlcpy(tmp, start, s-start+1);
|
|
|
|
|
2017-02-13 17:17:16 +01:00
|
|
|
if (tor_version_parse(tmp, router_version)<0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_info(LD_DIR,"Router version '%s' unparseable.",tmp);
|
2017-02-13 17:17:16 +01:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strict) {
|
|
|
|
if (router_version->major < 0 ||
|
|
|
|
router_version->minor < 0 ||
|
2017-02-17 17:31:39 +01:00
|
|
|
router_version->micro < 0 ||
|
2017-02-13 17:17:16 +01:00
|
|
|
router_version->patchlevel < 0 ||
|
|
|
|
router_version->svn_revision < 0) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2017-02-14 09:52:01 +01:00
|
|
|
|
2017-02-13 17:17:16 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Parse the Tor version of the platform string <b>platform</b>,
|
|
|
|
* and compare it to the version in <b>cutoff</b>. Return 1 if
|
|
|
|
* the router is at least as new as the cutoff, else return 0.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
tor_version_as_new_as(const char *platform, const char *cutoff)
|
|
|
|
{
|
|
|
|
tor_version_t cutoff_version, router_version;
|
|
|
|
int r;
|
|
|
|
tor_assert(platform);
|
|
|
|
|
|
|
|
if (tor_version_parse(cutoff, &cutoff_version)<0) {
|
|
|
|
log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
r = tor_version_parse_platform(platform, &router_version, 0);
|
|
|
|
if (r == 0) {
|
|
|
|
/* nonstandard Tor; be safe and say yes */
|
|
|
|
return 1;
|
|
|
|
} else if (r < 0) {
|
|
|
|
/* unparseable version; be safe and say yes. */
|
|
|
|
return 1;
|
2004-09-27 08:00:43 +02:00
|
|
|
}
|
|
|
|
|
2007-04-17 02:58:30 +02:00
|
|
|
/* Here's why we don't need to do any special handling for svn revisions:
|
|
|
|
* - If neither has an svn revision, we're fine.
|
|
|
|
* - If the router doesn't have an svn revision, we can't assume that it
|
|
|
|
* is "at least" any svn revision, so we need to return 0.
|
|
|
|
* - If the target version doesn't have an svn revision, any svn revision
|
|
|
|
* (or none at all) is good enough, so return 1.
|
|
|
|
* - If both target and router have an svn revision, we compare them.
|
|
|
|
*/
|
|
|
|
|
2004-09-27 08:00:43 +02:00
|
|
|
return tor_version_compare(&router_version, &cutoff_version) >= 0;
|
|
|
|
}
|
|
|
|
|
2004-11-09 19:22:17 +01:00
|
|
|
/** Parse a tor version from <b>s</b>, and store the result in <b>out</b>.
|
|
|
|
* Return 0 on success, -1 on failure. */
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
tor_version_parse(const char *s, tor_version_t *out)
|
2004-07-13 21:16:49 +02:00
|
|
|
{
|
2007-04-17 02:58:30 +02:00
|
|
|
char *eos=NULL;
|
|
|
|
const char *cp=NULL;
|
2017-02-15 13:57:34 +01:00
|
|
|
int ok = 1;
|
2004-07-13 21:16:49 +02:00
|
|
|
/* Format is:
|
2014-11-05 19:28:29 +01:00
|
|
|
* "Tor " ? NUM dot NUM [ dot NUM [ ( pre | rc | dot ) NUM ] ] [ - tag ]
|
2004-07-13 21:16:49 +02:00
|
|
|
*/
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(s);
|
|
|
|
tor_assert(out);
|
2004-11-30 00:04:26 +01:00
|
|
|
|
2004-07-13 21:16:49 +02:00
|
|
|
memset(out, 0, sizeof(tor_version_t));
|
2014-11-05 19:28:29 +01:00
|
|
|
out->status = VER_RELEASE;
|
2005-03-01 02:44:57 +01:00
|
|
|
if (!strcasecmpstart(s, "Tor "))
|
2006-01-02 06:11:31 +01:00
|
|
|
s += 4;
|
2005-03-01 02:44:57 +01:00
|
|
|
|
2014-11-05 19:28:29 +01:00
|
|
|
cp = s;
|
|
|
|
|
|
|
|
#define NUMBER(m) \
|
|
|
|
do { \
|
2017-02-19 12:38:06 +01:00
|
|
|
if (!cp || *cp < '0' || *cp > '9') \
|
|
|
|
return -1; \
|
2017-02-15 14:19:37 +01:00
|
|
|
out->m = (int)tor_parse_uint64(cp, 10, 0, INT32_MAX, &ok, &eos); \
|
2017-02-15 13:57:34 +01:00
|
|
|
if (!ok) \
|
|
|
|
return -1; \
|
2014-11-05 19:28:29 +01:00
|
|
|
if (!eos || eos == cp) \
|
|
|
|
return -1; \
|
|
|
|
cp = eos; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define DOT() \
|
|
|
|
do { \
|
|
|
|
if (*cp != '.') \
|
|
|
|
return -1; \
|
|
|
|
++cp; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
NUMBER(major);
|
|
|
|
DOT();
|
|
|
|
NUMBER(minor);
|
|
|
|
if (*cp == 0)
|
2004-07-13 21:16:49 +02:00
|
|
|
return 0;
|
2014-11-05 19:28:29 +01:00
|
|
|
else if (*cp == '-')
|
|
|
|
goto status_tag;
|
|
|
|
DOT();
|
|
|
|
NUMBER(micro);
|
2004-07-13 21:16:49 +02:00
|
|
|
|
|
|
|
/* Get status */
|
2014-11-05 19:28:29 +01:00
|
|
|
if (*cp == 0) {
|
|
|
|
return 0;
|
|
|
|
} else if (*cp == '.') {
|
2004-07-13 21:16:49 +02:00
|
|
|
++cp;
|
2014-11-05 19:28:29 +01:00
|
|
|
} else if (*cp == '-') {
|
|
|
|
goto status_tag;
|
2004-07-13 21:16:49 +02:00
|
|
|
} else if (0==strncmp(cp, "pre", 3)) {
|
|
|
|
out->status = VER_PRE;
|
|
|
|
cp += 3;
|
|
|
|
} else if (0==strncmp(cp, "rc", 2)) {
|
|
|
|
out->status = VER_RC;
|
|
|
|
cp += 2;
|
|
|
|
} else {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-05 19:28:29 +01:00
|
|
|
NUMBER(patchlevel);
|
2004-07-13 21:16:49 +02:00
|
|
|
|
2014-11-05 19:28:29 +01:00
|
|
|
status_tag:
|
2006-08-26 08:51:02 +02:00
|
|
|
/* Get status tag. */
|
2004-11-30 00:04:26 +01:00
|
|
|
if (*cp == '-' || *cp == '.')
|
|
|
|
++cp;
|
2007-04-17 02:58:30 +02:00
|
|
|
eos = (char*) find_whitespace(cp);
|
|
|
|
if (eos-cp >= (int)sizeof(out->status_tag))
|
|
|
|
strlcpy(out->status_tag, cp, sizeof(out->status_tag));
|
|
|
|
else {
|
|
|
|
memcpy(out->status_tag, cp, eos-cp);
|
|
|
|
out->status_tag[eos-cp] = 0;
|
|
|
|
}
|
|
|
|
cp = eat_whitespace(eos);
|
|
|
|
|
|
|
|
if (!strcmpstart(cp, "(r")) {
|
|
|
|
cp += 2;
|
2008-02-22 20:09:45 +01:00
|
|
|
out->svn_revision = (int) strtol(cp,&eos,10);
|
2009-06-12 19:38:37 +02:00
|
|
|
} else if (!strcmpstart(cp, "(git-")) {
|
|
|
|
char *close_paren = strchr(cp, ')');
|
|
|
|
int hexlen;
|
|
|
|
char digest[DIGEST_LEN];
|
|
|
|
if (! close_paren)
|
|
|
|
return -1;
|
|
|
|
cp += 5;
|
2009-09-01 05:23:47 +02:00
|
|
|
if (close_paren-cp > HEX_DIGEST_LEN)
|
|
|
|
return -1;
|
|
|
|
hexlen = (int)(close_paren-cp);
|
2012-11-07 22:09:58 +01:00
|
|
|
memwipe(digest, 0, sizeof(digest));
|
2009-09-01 05:23:47 +02:00
|
|
|
if ( hexlen == 0 || (hexlen % 2) == 1)
|
2009-06-12 19:38:37 +02:00
|
|
|
return -1;
|
2016-06-17 16:41:45 +02:00
|
|
|
if (base16_decode(digest, hexlen/2, cp, hexlen) != hexlen/2)
|
2009-06-12 19:38:37 +02:00
|
|
|
return -1;
|
|
|
|
memcpy(out->git_tag, digest, hexlen/2);
|
|
|
|
out->git_tag_len = hexlen/2;
|
2007-04-17 02:58:30 +02:00
|
|
|
}
|
2004-07-13 21:16:49 +02:00
|
|
|
|
|
|
|
return 0;
|
2014-11-05 19:28:29 +01:00
|
|
|
#undef NUMBER
|
|
|
|
#undef DOT
|
2004-07-13 21:16:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a >
|
|
|
|
* b. */
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
tor_version_compare(tor_version_t *a, tor_version_t *b)
|
2004-07-13 21:16:49 +02:00
|
|
|
{
|
|
|
|
int i;
|
2004-10-17 00:14:52 +02:00
|
|
|
tor_assert(a);
|
|
|
|
tor_assert(b);
|
2017-02-07 16:58:02 +01:00
|
|
|
|
|
|
|
/* We take this approach to comparison to ensure the same (bogus!) behavior
|
|
|
|
* on all inputs as we would have seen before bug #21278 was fixed. The
|
|
|
|
* only important difference here is that this method doesn't cause
|
|
|
|
* a signed integer underflow.
|
|
|
|
*/
|
|
|
|
#define CMP(field) do { \
|
|
|
|
unsigned aval = (unsigned) a->field; \
|
|
|
|
unsigned bval = (unsigned) b->field; \
|
|
|
|
int result = (int) (aval - bval); \
|
|
|
|
if (result < 0) \
|
|
|
|
return -1; \
|
|
|
|
else if (result > 0) \
|
|
|
|
return 1; \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
CMP(major);
|
|
|
|
CMP(minor);
|
|
|
|
CMP(micro);
|
|
|
|
CMP(status);
|
|
|
|
CMP(patchlevel);
|
|
|
|
if ((i = strcmp(a->status_tag, b->status_tag)))
|
|
|
|
return i;
|
|
|
|
CMP(svn_revision);
|
|
|
|
CMP(git_tag_len);
|
|
|
|
if (a->git_tag_len)
|
|
|
|
return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
|
2007-04-17 02:58:30 +02:00
|
|
|
else
|
2017-02-07 16:58:02 +01:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
#undef CMP
|
2004-07-13 21:16:49 +02:00
|
|
|
}
|
|
|
|
|
2005-06-11 20:52:12 +02:00
|
|
|
/** Return true iff versions <b>a</b> and <b>b</b> belong to the same series.
|
|
|
|
*/
|
2011-10-25 08:36:57 +02:00
|
|
|
int
|
2005-03-01 02:44:57 +01:00
|
|
|
tor_version_same_series(tor_version_t *a, tor_version_t *b)
|
|
|
|
{
|
|
|
|
tor_assert(a);
|
|
|
|
tor_assert(b);
|
|
|
|
return ((a->major == b->major) &&
|
|
|
|
(a->minor == b->minor) &&
|
|
|
|
(a->micro == b->micro));
|
|
|
|
}
|
2005-06-09 21:03:31 +02:00
|
|
|
|
2006-02-06 06:04:27 +01:00
|
|
|
/** Helper: Given pointers to two strings describing tor versions, return -1
|
2009-05-27 23:55:51 +02:00
|
|
|
* if _a precedes _b, 1 if _b precedes _a, and 0 if they are equivalent.
|
2006-02-06 06:04:27 +01:00
|
|
|
* Used to sort a list of versions. */
|
|
|
|
static int
|
2012-10-12 18:22:13 +02:00
|
|
|
compare_tor_version_str_ptr_(const void **_a, const void **_b)
|
2006-02-06 06:04:27 +01:00
|
|
|
{
|
|
|
|
const char *a = *_a, *b = *_b;
|
|
|
|
int ca, cb;
|
|
|
|
tor_version_t va, vb;
|
|
|
|
ca = tor_version_parse(a, &va);
|
|
|
|
cb = tor_version_parse(b, &vb);
|
|
|
|
/* If they both parse, compare them. */
|
|
|
|
if (!ca && !cb)
|
|
|
|
return tor_version_compare(&va,&vb);
|
|
|
|
/* If one parses, it comes first. */
|
|
|
|
if (!ca && cb)
|
|
|
|
return -1;
|
|
|
|
if (ca && !cb)
|
|
|
|
return 1;
|
|
|
|
/* If neither parses, compare strings. Also, the directory server admin
|
|
|
|
** needs to be smacked upside the head. But Tor is tolerant and gentle. */
|
|
|
|
return strcmp(a,b);
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Sort a list of string-representations of versions in ascending order. */
|
|
|
|
void
|
2006-04-03 08:23:24 +02:00
|
|
|
sort_version_list(smartlist_t *versions, int remove_duplicates)
|
2006-02-06 06:04:27 +01:00
|
|
|
{
|
2012-10-12 18:22:13 +02:00
|
|
|
smartlist_sort(versions, compare_tor_version_str_ptr_);
|
2006-09-15 06:27:58 +02:00
|
|
|
|
|
|
|
if (remove_duplicates)
|
2012-10-12 18:22:13 +02:00
|
|
|
smartlist_uniq(versions, compare_tor_version_str_ptr_, tor_free_);
|
2006-02-06 06:04:27 +01:00
|
|
|
}
|
|
|
|
|
2007-10-28 20:48:14 +01:00
|
|
|
/** Parse and validate the ASCII-encoded v2 descriptor in <b>desc</b>,
|
2007-10-28 20:48:16 +01:00
|
|
|
* write the parsed descriptor to the newly allocated *<b>parsed_out</b>, the
|
|
|
|
* binary descriptor ID of length DIGEST_LEN to <b>desc_id_out</b>, the
|
2007-10-28 20:48:14 +01:00
|
|
|
* encrypted introduction points to the newly allocated
|
2007-10-28 20:48:16 +01:00
|
|
|
* *<b>intro_points_encrypted_out</b>, their encrypted size to
|
|
|
|
* *<b>intro_points_encrypted_size_out</b>, the size of the encoded descriptor
|
|
|
|
* to *<b>encoded_size_out</b>, and a pointer to the possibly next
|
2007-10-29 20:10:42 +01:00
|
|
|
* descriptor to *<b>next_out</b>; return 0 for success (including validation)
|
2007-10-28 20:48:14 +01:00
|
|
|
* and -1 for failure.
|
2013-07-19 05:45:40 +02:00
|
|
|
*
|
|
|
|
* If <b>as_hsdir</b> is 1, we're parsing this as an HSDir, and we should
|
|
|
|
* be strict about time formats.
|
2007-10-28 20:48:14 +01:00
|
|
|
*/
|
|
|
|
int
|
|
|
|
rend_parse_v2_service_descriptor(rend_service_descriptor_t **parsed_out,
|
|
|
|
char *desc_id_out,
|
|
|
|
char **intro_points_encrypted_out,
|
|
|
|
size_t *intro_points_encrypted_size_out,
|
|
|
|
size_t *encoded_size_out,
|
2013-07-19 05:45:40 +02:00
|
|
|
const char **next_out, const char *desc,
|
|
|
|
int as_hsdir)
|
2007-10-28 20:48:14 +01:00
|
|
|
{
|
|
|
|
rend_service_descriptor_t *result =
|
|
|
|
tor_malloc_zero(sizeof(rend_service_descriptor_t));
|
|
|
|
char desc_hash[DIGEST_LEN];
|
|
|
|
const char *eos;
|
2012-01-18 21:53:30 +01:00
|
|
|
smartlist_t *tokens = smartlist_new();
|
2007-10-28 20:48:14 +01:00
|
|
|
directory_token_t *tok;
|
|
|
|
char secret_id_part[DIGEST_LEN];
|
2008-02-06 01:54:47 +01:00
|
|
|
int i, version, num_ok=1;
|
2007-10-28 20:48:14 +01:00
|
|
|
smartlist_t *versions;
|
|
|
|
char public_key_hash[DIGEST_LEN];
|
|
|
|
char test_desc_id[DIGEST_LEN];
|
2008-03-26 17:56:31 +01:00
|
|
|
memarea_t *area = NULL;
|
2013-07-19 05:45:40 +02:00
|
|
|
const int strict_time_fmt = as_hsdir;
|
|
|
|
|
2007-10-28 20:48:14 +01:00
|
|
|
tor_assert(desc);
|
|
|
|
/* Check if desc starts correctly. */
|
|
|
|
if (strncmp(desc, "rendezvous-service-descriptor ",
|
|
|
|
strlen("rendezvous-service-descriptor "))) {
|
|
|
|
log_info(LD_REND, "Descriptor does not start correctly.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Compute descriptor hash for later validation. */
|
2010-02-25 10:31:36 +01:00
|
|
|
if (router_get_hash_impl(desc, strlen(desc), desc_hash,
|
2007-10-28 20:48:14 +01:00
|
|
|
"rendezvous-service-descriptor ",
|
2009-08-24 18:51:33 +02:00
|
|
|
"\nsignature", '\n', DIGEST_SHA1) < 0) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Couldn't compute descriptor hash.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Determine end of string. */
|
|
|
|
eos = strstr(desc, "\nrendezvous-service-descriptor ");
|
|
|
|
if (!eos)
|
|
|
|
eos = desc + strlen(desc);
|
|
|
|
else
|
|
|
|
eos = eos + 1;
|
2008-08-19 17:41:28 +02:00
|
|
|
/* Check length. */
|
2011-04-18 22:53:13 +02:00
|
|
|
if (eos-desc > REND_DESC_MAX_SIZE) {
|
2016-05-30 22:18:16 +02:00
|
|
|
/* XXXX+ If we are parsing this descriptor as a server, this
|
2011-04-18 22:25:48 +02:00
|
|
|
* should be a protocol warning. */
|
2011-04-19 18:40:26 +02:00
|
|
|
log_warn(LD_REND, "Descriptor length is %d which exceeds "
|
|
|
|
"maximum rendezvous descriptor size of %d bytes.",
|
2011-04-18 22:53:13 +02:00
|
|
|
(int)(eos-desc), REND_DESC_MAX_SIZE);
|
2008-08-19 17:41:28 +02:00
|
|
|
goto err;
|
|
|
|
}
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Tokenize descriptor. */
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2008-03-26 17:56:31 +01:00
|
|
|
if (tokenize_string(area, desc, eos, tokens, desc_token_table, 0)) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Error tokenizing descriptor.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Set next to next descriptor, if available. */
|
|
|
|
*next_out = eos;
|
|
|
|
/* Set length of encoded descriptor. */
|
|
|
|
*encoded_size_out = eos - desc;
|
|
|
|
/* Check min allowed length of token list. */
|
2007-11-27 22:06:34 +01:00
|
|
|
if (smartlist_len(tokens) < 7) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Impossibly short descriptor.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Parse base32-encoded descriptor ID. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_RENDEZVOUS_SERVICE_DESCRIPTOR);
|
2007-10-28 20:48:16 +01:00
|
|
|
tor_assert(tok == smartlist_get(tokens, 0));
|
2007-10-28 20:48:14 +01:00
|
|
|
tor_assert(tok->n_args == 1);
|
2015-04-21 20:04:39 +02:00
|
|
|
if (!rend_valid_descriptor_id(tok->args[0])) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Invalid descriptor ID: '%s'", tok->args[0]);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (base32_decode(desc_id_out, DIGEST_LEN,
|
2007-10-31 21:48:06 +01:00
|
|
|
tok->args[0], REND_DESC_ID_V2_LEN_BASE32) < 0) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Descriptor ID contains illegal characters: %s",
|
|
|
|
tok->args[0]);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Parse descriptor version. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_VERSION);
|
2007-10-28 20:48:14 +01:00
|
|
|
tor_assert(tok->n_args == 1);
|
2008-02-06 01:54:47 +01:00
|
|
|
result->version =
|
|
|
|
(int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &num_ok, NULL);
|
|
|
|
if (result->version != 2 || !num_ok) {
|
2007-11-02 03:25:28 +01:00
|
|
|
/* If it's <2, it shouldn't be under this format. If the number
|
|
|
|
* is greater than 2, we bumped it because we broke backward
|
|
|
|
* compatibility. See how version numbers in our other formats
|
2008-02-06 01:54:47 +01:00
|
|
|
* work. */
|
|
|
|
log_warn(LD_REND, "Unrecognized descriptor version: %s",
|
|
|
|
escaped(tok->args[0]));
|
2007-10-28 20:48:14 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Parse public key. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_PERMANENT_KEY);
|
2007-10-28 20:48:14 +01:00
|
|
|
result->pk = tok->key;
|
|
|
|
tok->key = NULL; /* Prevent free */
|
|
|
|
/* Parse secret ID part. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_SECRET_ID_PART);
|
2007-10-28 20:48:14 +01:00
|
|
|
tor_assert(tok->n_args == 1);
|
2007-10-29 20:10:42 +01:00
|
|
|
if (strlen(tok->args[0]) != REND_SECRET_ID_PART_LEN_BASE32 ||
|
|
|
|
strspn(tok->args[0], BASE32_CHARS) != REND_SECRET_ID_PART_LEN_BASE32) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Invalid secret ID part: '%s'", tok->args[0]);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (base32_decode(secret_id_part, DIGEST_LEN, tok->args[0], 32) < 0) {
|
|
|
|
log_warn(LD_REND, "Secret ID part contains illegal characters: %s",
|
|
|
|
tok->args[0]);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Parse publication time -- up-to-date check is done when storing the
|
|
|
|
* descriptor. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_PUBLICATION_TIME);
|
2007-10-28 20:48:14 +01:00
|
|
|
tor_assert(tok->n_args == 1);
|
2016-11-14 20:57:32 +01:00
|
|
|
if (parse_iso_time_(tok->args[0], &result->timestamp,
|
|
|
|
strict_time_fmt, 0) < 0) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Invalid publication time: '%s'", tok->args[0]);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Parse protocol versions. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_PROTOCOL_VERSIONS);
|
2007-10-28 20:48:14 +01:00
|
|
|
tor_assert(tok->n_args == 1);
|
2012-01-18 21:53:30 +01:00
|
|
|
versions = smartlist_new();
|
2007-10-28 20:48:14 +01:00
|
|
|
smartlist_split_string(versions, tok->args[0], ",",
|
|
|
|
SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
|
|
|
|
for (i = 0; i < smartlist_len(versions); i++) {
|
2008-02-06 01:54:47 +01:00
|
|
|
version = (int) tor_parse_long(smartlist_get(versions, i),
|
|
|
|
10, 0, INT_MAX, &num_ok, NULL);
|
|
|
|
if (!num_ok) /* It's a string; let's ignore it. */
|
|
|
|
continue;
|
2012-09-13 13:39:39 +02:00
|
|
|
if (version >= REND_PROTOCOL_VERSION_BITMASK_WIDTH)
|
|
|
|
/* Avoid undefined left-shift behaviour. */
|
|
|
|
continue;
|
2007-10-28 20:48:14 +01:00
|
|
|
result->protocols |= 1 << version;
|
|
|
|
}
|
2007-10-28 20:48:16 +01:00
|
|
|
SMARTLIST_FOREACH(versions, char *, cp, tor_free(cp));
|
2007-10-28 20:48:14 +01:00
|
|
|
smartlist_free(versions);
|
|
|
|
/* Parse encrypted introduction points. Don't verify. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_opt_by_keyword(tokens, R_INTRODUCTION_POINTS);
|
2007-11-27 22:06:34 +01:00
|
|
|
if (tok) {
|
|
|
|
if (strcmp(tok->object_type, "MESSAGE")) {
|
|
|
|
log_warn(LD_DIR, "Bad object type: introduction points should be of "
|
|
|
|
"type MESSAGE");
|
|
|
|
goto err;
|
|
|
|
}
|
2008-03-26 17:56:31 +01:00
|
|
|
*intro_points_encrypted_out = tor_memdup(tok->object_body,
|
|
|
|
tok->object_size);
|
2007-11-27 22:06:34 +01:00
|
|
|
*intro_points_encrypted_size_out = tok->object_size;
|
|
|
|
} else {
|
|
|
|
*intro_points_encrypted_out = NULL;
|
|
|
|
*intro_points_encrypted_size_out = 0;
|
2007-11-02 03:25:28 +01:00
|
|
|
}
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Parse and verify signature. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_SIGNATURE);
|
2009-09-14 18:02:00 +02:00
|
|
|
if (check_signature_token(desc_hash, DIGEST_LEN, tok, result->pk, 0,
|
2007-10-28 20:48:14 +01:00
|
|
|
"v2 rendezvous service descriptor") < 0)
|
|
|
|
goto err;
|
|
|
|
/* Verify that descriptor ID belongs to public key and secret ID part. */
|
2017-08-09 15:24:16 +02:00
|
|
|
if (crypto_pk_get_digest(result->pk, public_key_hash) < 0) {
|
|
|
|
log_warn(LD_REND, "Unable to compute rend descriptor public key digest");
|
|
|
|
goto err;
|
|
|
|
}
|
2007-10-28 20:48:14 +01:00
|
|
|
rend_get_descriptor_id_bytes(test_desc_id, public_key_hash,
|
|
|
|
secret_id_part);
|
2011-05-10 22:58:38 +02:00
|
|
|
if (tor_memneq(desc_id_out, test_desc_id, DIGEST_LEN)) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Parsed descriptor ID does not match "
|
|
|
|
"computed descriptor ID.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
goto done;
|
|
|
|
err:
|
2009-12-12 08:07:59 +01:00
|
|
|
rend_service_descriptor_free(result);
|
2007-10-28 20:48:14 +01:00
|
|
|
result = NULL;
|
|
|
|
done:
|
|
|
|
if (tokens) {
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2007-10-28 20:48:14 +01:00
|
|
|
smartlist_free(tokens);
|
|
|
|
}
|
2008-03-26 17:56:31 +01:00
|
|
|
if (area)
|
|
|
|
memarea_drop_all(area);
|
2007-10-28 20:48:14 +01:00
|
|
|
*parsed_out = result;
|
|
|
|
if (result)
|
|
|
|
return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2008-08-19 17:41:28 +02:00
|
|
|
/** Decrypt the encrypted introduction points in <b>ipos_encrypted</b> of
|
|
|
|
* length <b>ipos_encrypted_size</b> using <b>descriptor_cookie</b> and
|
|
|
|
* write the result to a newly allocated string that is pointed to by
|
|
|
|
* <b>ipos_decrypted</b> and its length to <b>ipos_decrypted_size</b>.
|
|
|
|
* Return 0 if decryption was successful and -1 otherwise. */
|
2007-10-28 20:48:14 +01:00
|
|
|
int
|
2008-08-19 17:41:28 +02:00
|
|
|
rend_decrypt_introduction_points(char **ipos_decrypted,
|
|
|
|
size_t *ipos_decrypted_size,
|
2007-10-28 20:48:14 +01:00
|
|
|
const char *descriptor_cookie,
|
2008-08-19 17:41:28 +02:00
|
|
|
const char *ipos_encrypted,
|
|
|
|
size_t ipos_encrypted_size)
|
|
|
|
{
|
|
|
|
tor_assert(ipos_encrypted);
|
|
|
|
tor_assert(descriptor_cookie);
|
|
|
|
if (ipos_encrypted_size < 2) {
|
|
|
|
log_warn(LD_REND, "Size of encrypted introduction points is too "
|
|
|
|
"small.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (ipos_encrypted[0] == (int)REND_BASIC_AUTH) {
|
|
|
|
char iv[CIPHER_IV_LEN], client_id[REND_BASIC_AUTH_CLIENT_ID_LEN],
|
|
|
|
session_key[CIPHER_KEY_LEN], *dec;
|
|
|
|
int declen, client_blocks;
|
|
|
|
size_t pos = 0, len, client_entries_len;
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_digest_t *digest;
|
|
|
|
crypto_cipher_t *cipher;
|
2008-08-19 17:41:28 +02:00
|
|
|
client_blocks = (int) ipos_encrypted[1];
|
|
|
|
client_entries_len = client_blocks * REND_BASIC_AUTH_CLIENT_MULTIPLE *
|
|
|
|
REND_BASIC_AUTH_CLIENT_ENTRY_LEN;
|
|
|
|
if (ipos_encrypted_size < 2 + client_entries_len + CIPHER_IV_LEN + 1) {
|
|
|
|
log_warn(LD_REND, "Size of encrypted introduction points is too "
|
|
|
|
"small.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
memcpy(iv, ipos_encrypted + 2 + client_entries_len, CIPHER_IV_LEN);
|
2012-01-18 21:53:30 +01:00
|
|
|
digest = crypto_digest_new();
|
2008-08-19 17:41:28 +02:00
|
|
|
crypto_digest_add_bytes(digest, descriptor_cookie, REND_DESC_COOKIE_LEN);
|
|
|
|
crypto_digest_add_bytes(digest, iv, CIPHER_IV_LEN);
|
|
|
|
crypto_digest_get_digest(digest, client_id,
|
|
|
|
REND_BASIC_AUTH_CLIENT_ID_LEN);
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_digest_free(digest);
|
2008-08-19 17:41:28 +02:00
|
|
|
for (pos = 2; pos < 2 + client_entries_len;
|
|
|
|
pos += REND_BASIC_AUTH_CLIENT_ENTRY_LEN) {
|
2011-05-10 22:23:43 +02:00
|
|
|
if (tor_memeq(ipos_encrypted + pos, client_id,
|
2008-08-19 17:41:28 +02:00
|
|
|
REND_BASIC_AUTH_CLIENT_ID_LEN)) {
|
|
|
|
/* Attempt to decrypt introduction points. */
|
2012-03-20 20:35:43 +01:00
|
|
|
cipher = crypto_cipher_new(descriptor_cookie);
|
2008-08-19 17:41:28 +02:00
|
|
|
if (crypto_cipher_decrypt(cipher, session_key, ipos_encrypted
|
|
|
|
+ pos + REND_BASIC_AUTH_CLIENT_ID_LEN,
|
|
|
|
CIPHER_KEY_LEN) < 0) {
|
|
|
|
log_warn(LD_REND, "Could not decrypt session key for client.");
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_cipher_free(cipher);
|
2008-08-19 17:41:28 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_cipher_free(cipher);
|
2012-03-20 20:35:43 +01:00
|
|
|
|
2008-08-19 17:41:28 +02:00
|
|
|
len = ipos_encrypted_size - 2 - client_entries_len - CIPHER_IV_LEN;
|
2016-12-19 02:13:58 +01:00
|
|
|
dec = tor_malloc_zero(len + 1);
|
2012-03-20 20:35:43 +01:00
|
|
|
declen = crypto_cipher_decrypt_with_iv(session_key, dec, len,
|
2008-08-19 17:41:28 +02:00
|
|
|
ipos_encrypted + 2 + client_entries_len,
|
|
|
|
ipos_encrypted_size - 2 - client_entries_len);
|
2012-03-20 20:35:43 +01:00
|
|
|
|
2008-08-19 17:41:28 +02:00
|
|
|
if (declen < 0) {
|
|
|
|
log_warn(LD_REND, "Could not decrypt introduction point string.");
|
|
|
|
tor_free(dec);
|
|
|
|
return -1;
|
|
|
|
}
|
2011-05-10 22:58:38 +02:00
|
|
|
if (fast_memcmpstart(dec, declen, "introduction-point ")) {
|
2008-08-19 17:41:28 +02:00
|
|
|
log_warn(LD_REND, "Decrypted introduction points don't "
|
|
|
|
"look like we could parse them.");
|
|
|
|
tor_free(dec);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*ipos_decrypted = dec;
|
|
|
|
*ipos_decrypted_size = declen;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
log_warn(LD_REND, "Could not decrypt introduction points. Please "
|
|
|
|
"check your authorization for this service!");
|
|
|
|
return -1;
|
|
|
|
} else if (ipos_encrypted[0] == (int)REND_STEALTH_AUTH) {
|
|
|
|
char *dec;
|
|
|
|
int declen;
|
2011-11-06 17:23:14 +01:00
|
|
|
if (ipos_encrypted_size < CIPHER_IV_LEN + 2) {
|
|
|
|
log_warn(LD_REND, "Size of encrypted introduction points is too "
|
|
|
|
"small.");
|
|
|
|
return -1;
|
|
|
|
}
|
2016-12-19 02:13:58 +01:00
|
|
|
dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 1 + 1);
|
2012-03-20 20:35:43 +01:00
|
|
|
|
|
|
|
declen = crypto_cipher_decrypt_with_iv(descriptor_cookie, dec,
|
2008-08-19 17:41:28 +02:00
|
|
|
ipos_encrypted_size -
|
|
|
|
CIPHER_IV_LEN - 1,
|
|
|
|
ipos_encrypted + 1,
|
|
|
|
ipos_encrypted_size - 1);
|
2012-03-20 20:35:43 +01:00
|
|
|
|
2008-08-19 17:41:28 +02:00
|
|
|
if (declen < 0) {
|
|
|
|
log_warn(LD_REND, "Decrypting introduction points failed!");
|
|
|
|
tor_free(dec);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
*ipos_decrypted = dec;
|
|
|
|
*ipos_decrypted_size = declen;
|
|
|
|
return 0;
|
|
|
|
} else {
|
|
|
|
log_warn(LD_REND, "Unknown authorization type number: %d",
|
|
|
|
ipos_encrypted[0]);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Parse the encoded introduction points in <b>intro_points_encoded</b> of
|
|
|
|
* length <b>intro_points_encoded_size</b> and write the result to the
|
|
|
|
* descriptor in <b>parsed</b>; return the number of successfully parsed
|
|
|
|
* introduction points or -1 in case of a failure. */
|
|
|
|
int
|
|
|
|
rend_parse_introduction_points(rend_service_descriptor_t *parsed,
|
|
|
|
const char *intro_points_encoded,
|
|
|
|
size_t intro_points_encoded_size)
|
2007-10-28 20:48:14 +01:00
|
|
|
{
|
2008-11-03 17:36:15 +01:00
|
|
|
const char *current_ipo, *end_of_intro_points;
|
2015-04-02 14:42:06 +02:00
|
|
|
smartlist_t *tokens = NULL;
|
2007-10-28 20:48:14 +01:00
|
|
|
directory_token_t *tok;
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_t *intro;
|
2007-10-28 20:48:14 +01:00
|
|
|
extend_info_t *info;
|
2008-02-06 01:54:47 +01:00
|
|
|
int result, num_ok=1;
|
2008-03-26 17:56:31 +01:00
|
|
|
memarea_t *area = NULL;
|
2007-10-28 20:48:14 +01:00
|
|
|
tor_assert(parsed);
|
2007-12-21 10:28:22 +01:00
|
|
|
/** Function may only be invoked once. */
|
|
|
|
tor_assert(!parsed->intro_nodes);
|
2015-04-02 14:42:06 +02:00
|
|
|
if (!intro_points_encoded || intro_points_encoded_size == 0) {
|
|
|
|
log_warn(LD_REND, "Empty or zero size introduction point list");
|
|
|
|
goto err;
|
|
|
|
}
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Consider one intro point after the other. */
|
2008-11-03 17:36:15 +01:00
|
|
|
current_ipo = intro_points_encoded;
|
|
|
|
end_of_intro_points = intro_points_encoded + intro_points_encoded_size;
|
2012-01-18 21:53:30 +01:00
|
|
|
tokens = smartlist_new();
|
|
|
|
parsed->intro_nodes = smartlist_new();
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2008-11-03 17:36:15 +01:00
|
|
|
|
2011-05-10 22:58:38 +02:00
|
|
|
while (!fast_memcmpstart(current_ipo, end_of_intro_points-current_ipo,
|
2008-11-03 17:36:15 +01:00
|
|
|
"introduction-point ")) {
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Determine end of string. */
|
2008-11-03 17:36:15 +01:00
|
|
|
const char *eos = tor_memstr(current_ipo, end_of_intro_points-current_ipo,
|
|
|
|
"\nintroduction-point ");
|
2007-10-28 20:48:14 +01:00
|
|
|
if (!eos)
|
2008-11-03 17:36:15 +01:00
|
|
|
eos = end_of_intro_points;
|
2007-10-28 20:48:14 +01:00
|
|
|
else
|
|
|
|
eos = eos+1;
|
2008-11-03 17:36:15 +01:00
|
|
|
tor_assert(eos <= intro_points_encoded+intro_points_encoded_size);
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Free tokens and clear token list. */
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2007-10-28 20:48:14 +01:00
|
|
|
smartlist_clear(tokens);
|
2008-03-26 17:56:31 +01:00
|
|
|
memarea_clear(area);
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Tokenize string. */
|
2008-11-03 17:36:15 +01:00
|
|
|
if (tokenize_string(area, current_ipo, eos, tokens, ipo_token_table, 0)) {
|
|
|
|
log_warn(LD_REND, "Error tokenizing introduction point");
|
2007-10-28 20:48:14 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Advance to next introduction point, if available. */
|
2008-11-03 17:36:15 +01:00
|
|
|
current_ipo = eos;
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Check minimum allowed length of introduction point. */
|
|
|
|
if (smartlist_len(tokens) < 5) {
|
|
|
|
log_warn(LD_REND, "Impossibly short introduction point.");
|
|
|
|
goto err;
|
|
|
|
}
|
2007-12-21 10:28:22 +01:00
|
|
|
/* Allocate new intro point and extend info. */
|
|
|
|
intro = tor_malloc_zero(sizeof(rend_intro_point_t));
|
|
|
|
info = intro->extend_info = tor_malloc_zero(sizeof(extend_info_t));
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Parse identifier. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_IPO_IDENTIFIER);
|
2007-10-28 20:48:14 +01:00
|
|
|
if (base32_decode(info->identity_digest, DIGEST_LEN,
|
2007-10-29 20:10:42 +01:00
|
|
|
tok->args[0], REND_INTRO_POINT_ID_LEN_BASE32) < 0) {
|
2007-10-28 20:48:14 +01:00
|
|
|
log_warn(LD_REND, "Identity digest contains illegal characters: %s",
|
|
|
|
tok->args[0]);
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_free(intro);
|
2007-10-28 20:48:14 +01:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Write identifier to nickname. */
|
|
|
|
info->nickname[0] = '$';
|
|
|
|
base16_encode(info->nickname + 1, sizeof(info->nickname) - 1,
|
|
|
|
info->identity_digest, DIGEST_LEN);
|
|
|
|
/* Parse IP address. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_IPO_IP_ADDRESS);
|
2011-10-11 17:21:31 +02:00
|
|
|
if (tor_addr_parse(&info->addr, tok->args[0])<0) {
|
2008-08-05 22:08:19 +02:00
|
|
|
log_warn(LD_REND, "Could not parse introduction point address.");
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_free(intro);
|
2007-10-28 20:48:14 +01:00
|
|
|
goto err;
|
|
|
|
}
|
2008-08-05 22:08:19 +02:00
|
|
|
if (tor_addr_family(&info->addr) != AF_INET) {
|
|
|
|
log_warn(LD_REND, "Introduction point address was not ipv4.");
|
|
|
|
rend_intro_point_free(intro);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Parse onion port. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_IPO_ONION_PORT);
|
2008-02-06 01:54:47 +01:00
|
|
|
info->port = (uint16_t) tor_parse_long(tok->args[0],10,1,65535,
|
|
|
|
&num_ok,NULL);
|
|
|
|
if (!info->port || !num_ok) {
|
|
|
|
log_warn(LD_REND, "Introduction point onion port %s is invalid",
|
|
|
|
escaped(tok->args[0]));
|
2007-12-21 10:28:22 +01:00
|
|
|
rend_intro_point_free(intro);
|
2007-11-02 03:25:28 +01:00
|
|
|
goto err;
|
|
|
|
}
|
2007-10-28 20:48:14 +01:00
|
|
|
/* Parse onion key. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_IPO_ONION_KEY);
|
2011-05-16 20:44:23 +02:00
|
|
|
if (!crypto_pk_public_exponent_ok(tok->key)) {
|
2011-04-28 23:35:03 +02:00
|
|
|
log_warn(LD_REND,
|
|
|
|
"Introduction point's onion key had invalid exponent.");
|
|
|
|
rend_intro_point_free(intro);
|
|
|
|
goto err;
|
|
|
|
}
|
2007-10-28 20:48:14 +01:00
|
|
|
info->onion_key = tok->key;
|
|
|
|
tok->key = NULL; /* Prevent free */
|
|
|
|
/* Parse service key. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, R_IPO_SERVICE_KEY);
|
2011-05-16 20:44:23 +02:00
|
|
|
if (!crypto_pk_public_exponent_ok(tok->key)) {
|
2011-04-28 23:35:03 +02:00
|
|
|
log_warn(LD_REND,
|
|
|
|
"Introduction point key had invalid exponent.");
|
|
|
|
rend_intro_point_free(intro);
|
|
|
|
goto err;
|
|
|
|
}
|
2007-12-21 10:28:22 +01:00
|
|
|
intro->intro_key = tok->key;
|
2007-10-28 20:48:14 +01:00
|
|
|
tok->key = NULL; /* Prevent free */
|
|
|
|
/* Add extend info to list of introduction points. */
|
2007-12-21 10:28:22 +01:00
|
|
|
smartlist_add(parsed->intro_nodes, intro);
|
|
|
|
}
|
|
|
|
result = smartlist_len(parsed->intro_nodes);
|
2007-10-28 20:48:14 +01:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
err:
|
|
|
|
result = -1;
|
|
|
|
|
|
|
|
done:
|
|
|
|
/* Free tokens and clear token list. */
|
2015-04-02 14:42:06 +02:00
|
|
|
if (tokens) {
|
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
|
|
|
smartlist_free(tokens);
|
|
|
|
}
|
2008-03-26 17:56:31 +01:00
|
|
|
if (area)
|
|
|
|
memarea_drop_all(area);
|
2007-10-28 20:48:14 +01:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2008-08-08 16:36:11 +02:00
|
|
|
/** Parse the content of a client_key file in <b>ckstr</b> and add
|
|
|
|
* rend_authorized_client_t's for each parsed client to
|
|
|
|
* <b>parsed_clients</b>. Return the number of parsed clients as result
|
|
|
|
* or -1 for failure. */
|
|
|
|
int
|
|
|
|
rend_parse_client_keys(strmap_t *parsed_clients, const char *ckstr)
|
|
|
|
{
|
|
|
|
int result = -1;
|
|
|
|
smartlist_t *tokens;
|
|
|
|
directory_token_t *tok;
|
|
|
|
const char *current_entry = NULL;
|
|
|
|
memarea_t *area = NULL;
|
2015-04-14 05:35:06 +02:00
|
|
|
char *err_msg = NULL;
|
2008-08-08 16:36:11 +02:00
|
|
|
if (!ckstr || strlen(ckstr) == 0)
|
|
|
|
return -1;
|
2012-01-18 21:53:30 +01:00
|
|
|
tokens = smartlist_new();
|
2008-08-08 16:36:11 +02:00
|
|
|
/* Begin parsing with first entry, skipping comments or whitespace at the
|
|
|
|
* beginning. */
|
2008-11-05 21:34:22 +01:00
|
|
|
area = memarea_new();
|
2008-08-09 17:13:28 +02:00
|
|
|
current_entry = eat_whitespace(ckstr);
|
2008-08-08 16:36:11 +02:00
|
|
|
while (!strcmpstart(current_entry, "client-name ")) {
|
|
|
|
rend_authorized_client_t *parsed_entry;
|
|
|
|
/* Determine end of string. */
|
|
|
|
const char *eos = strstr(current_entry, "\nclient-name ");
|
|
|
|
if (!eos)
|
|
|
|
eos = current_entry + strlen(current_entry);
|
|
|
|
else
|
|
|
|
eos = eos + 1;
|
|
|
|
/* Free tokens and clear token list. */
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2008-08-08 16:36:11 +02:00
|
|
|
smartlist_clear(tokens);
|
|
|
|
memarea_clear(area);
|
|
|
|
/* Tokenize string. */
|
|
|
|
if (tokenize_string(area, current_entry, eos, tokens,
|
|
|
|
client_keys_token_table, 0)) {
|
|
|
|
log_warn(LD_REND, "Error tokenizing client keys file.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Advance to next entry, if available. */
|
|
|
|
current_entry = eos;
|
|
|
|
/* Check minimum allowed length of token list. */
|
|
|
|
if (smartlist_len(tokens) < 2) {
|
|
|
|
log_warn(LD_REND, "Impossibly short client key entry.");
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Parse client name. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, C_CLIENT_NAME);
|
2008-08-08 16:36:11 +02:00
|
|
|
tor_assert(tok == smartlist_get(tokens, 0));
|
|
|
|
tor_assert(tok->n_args == 1);
|
|
|
|
|
2016-02-20 00:32:25 +01:00
|
|
|
if (!rend_valid_client_name(tok->args[0])) {
|
2008-08-08 16:36:11 +02:00
|
|
|
log_warn(LD_CONFIG, "Illegal client name: %s. (Length must be "
|
2016-02-20 00:32:25 +01:00
|
|
|
"between 1 and %d, and valid characters are "
|
|
|
|
"[A-Za-z0-9+-_].)", tok->args[0], REND_CLIENTNAME_MAX_LEN);
|
2008-08-08 16:36:11 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* Check if client name is duplicate. */
|
|
|
|
if (strmap_get(parsed_clients, tok->args[0])) {
|
|
|
|
log_warn(LD_CONFIG, "HiddenServiceAuthorizeClient contains a "
|
|
|
|
"duplicate client name: '%s'. Ignoring.", tok->args[0]);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
parsed_entry = tor_malloc_zero(sizeof(rend_authorized_client_t));
|
|
|
|
parsed_entry->client_name = tor_strdup(tok->args[0]);
|
|
|
|
strmap_set(parsed_clients, parsed_entry->client_name, parsed_entry);
|
|
|
|
/* Parse client key. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_opt_by_keyword(tokens, C_CLIENT_KEY);
|
2008-08-08 16:36:11 +02:00
|
|
|
if (tok) {
|
|
|
|
parsed_entry->client_key = tok->key;
|
|
|
|
tok->key = NULL; /* Prevent free */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse descriptor cookie. */
|
2008-12-11 20:40:58 +01:00
|
|
|
tok = find_by_keyword(tokens, C_DESCRIPTOR_COOKIE);
|
2008-08-08 16:36:11 +02:00
|
|
|
tor_assert(tok->n_args == 1);
|
2015-04-14 05:35:06 +02:00
|
|
|
if (rend_auth_decode_cookie(tok->args[0], parsed_entry->descriptor_cookie,
|
|
|
|
NULL, &err_msg) < 0) {
|
|
|
|
tor_assert(err_msg);
|
|
|
|
log_warn(LD_REND, "%s", err_msg);
|
|
|
|
tor_free(err_msg);
|
2008-08-08 16:36:11 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
result = strmap_size(parsed_clients);
|
|
|
|
goto done;
|
|
|
|
err:
|
|
|
|
result = -1;
|
|
|
|
done:
|
|
|
|
/* Free tokens and clear token list. */
|
2009-09-28 16:37:01 +02:00
|
|
|
SMARTLIST_FOREACH(tokens, directory_token_t *, t, token_clear(t));
|
2008-08-08 16:36:11 +02:00
|
|
|
smartlist_free(tokens);
|
|
|
|
if (area)
|
|
|
|
memarea_drop_all(area);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-06-30 02:39:29 +02:00
|
|
|
/** Called on startup; right now we just handle scanning the unparseable
|
|
|
|
* descriptor dumps, but hang anything else we might need to do in the
|
|
|
|
* future here as well.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
routerparse_init(void)
|
|
|
|
{
|
2016-06-30 11:37:23 +02:00
|
|
|
/*
|
|
|
|
* Check both if the sandbox is active and whether it's configured; no
|
|
|
|
* point in loading all that if we won't be able to use it after the
|
|
|
|
* sandbox becomes active.
|
|
|
|
*/
|
|
|
|
if (!(sandbox_is_active() || get_options()->Sandbox)) {
|
2016-06-30 09:45:55 +02:00
|
|
|
dump_desc_init();
|
|
|
|
}
|
2016-06-30 02:39:29 +02:00
|
|
|
}
|
|
|
|
|
2016-06-18 00:35:58 +02:00
|
|
|
/** Clean up all data structures used by routerparse.c at exit */
|
|
|
|
void
|
|
|
|
routerparse_free_all(void)
|
|
|
|
{
|
|
|
|
dump_desc_fifo_cleanup();
|
|
|
|
}
|
|
|
|
|