2015-06-22 19:51:56 +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.
|
2015-01-02 20:27:39 +01:00
|
|
|
* Copyright (c) 2007-2015, The Tor Project, Inc. */
|
2004-05-10 19:30:51 +02:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file routerparse.c
|
|
|
|
* \brief Code to parse and validate router descriptors and directories.
|
|
|
|
**/
|
|
|
|
|
2015-01-29 15:57:00 +01:00
|
|
|
#define ROUTERPARSE_PRIVATE
|
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
#include "or.h"
|
2010-07-22 10:22:51 +02:00
|
|
|
#include "config.h"
|
2012-10-15 20:48:34 +02:00
|
|
|
#include "circuitstats.h"
|
2010-07-22 12:09:49 +02:00
|
|
|
#include "dirserv.h"
|
2010-07-22 12:19:28 +02:00
|
|
|
#include "dirvote.h"
|
2010-07-23 20:51:25 +02:00
|
|
|
#include "policies.h"
|
2010-07-21 17:52:54 +02:00
|
|
|
#include "rendcommon.h"
|
2010-07-21 16:17:10 +02:00
|
|
|
#include "router.h"
|
2010-07-21 17:08:11 +02:00
|
|
|
#include "routerlist.h"
|
2008-03-26 17:33:33 +01:00
|
|
|
#include "memarea.h"
|
2010-07-23 20:04:12 +02:00
|
|
|
#include "microdesc.h"
|
2010-07-23 20:18:55 +02:00
|
|
|
#include "networkstatus.h"
|
2010-07-23 22:57:20 +02:00
|
|
|
#include "rephist.h"
|
2014-10-01 17:54:07 +02:00
|
|
|
#include "routerkeys.h"
|
2010-07-23 23:23:43 +02:00
|
|
|
#include "routerparse.h"
|
2015-01-29 15:57:00 +01:00
|
|
|
#include "entrynodes.h"
|
2014-10-01 05:36:47 +02:00
|
|
|
#include "torcert.h"
|
|
|
|
|
2010-01-30 00:40:40 +01:00
|
|
|
#undef log
|
|
|
|
#include <math.h>
|
2004-05-10 19:30:51 +02:00
|
|
|
|
|
|
|
/****************************************************************************/
|
|
|
|
|
2007-09-26 18:19:44 +02:00
|
|
|
/** Enumeration of possible token types. The ones starting with K_ correspond
|
2014-10-28 18:12:52 +01:00
|
|
|
* to directory 'keywords'. A_ is for an annotation, R or C is related to
|
|
|
|
* hidden services, ERR_ is an error in the tokenizing process, EOF_ is an
|
|
|
|
* end-of-file marker, and NIL_ is used to encode not-a-token.
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
|
|
|
typedef enum {
|
2007-04-16 06:18:21 +02:00
|
|
|
K_ACCEPT = 0,
|
2008-07-24 15:44:04 +02:00
|
|
|
K_ACCEPT6,
|
2004-05-10 19:30:51 +02:00
|
|
|
K_DIRECTORY_SIGNATURE,
|
|
|
|
K_RECOMMENDED_SOFTWARE,
|
|
|
|
K_REJECT,
|
2008-07-24 15:44:04 +02:00
|
|
|
K_REJECT6,
|
2004-05-10 19:30:51 +02:00
|
|
|
K_ROUTER,
|
|
|
|
K_SIGNED_DIRECTORY,
|
|
|
|
K_SIGNING_KEY,
|
|
|
|
K_ONION_KEY,
|
2012-12-04 21:58:18 +01:00
|
|
|
K_ONION_KEY_NTOR,
|
2004-05-10 19:30:51 +02:00
|
|
|
K_ROUTER_SIGNATURE,
|
|
|
|
K_PUBLISHED,
|
|
|
|
K_RUNNING_ROUTERS,
|
2004-10-27 02:48:51 +02:00
|
|
|
K_ROUTER_STATUS,
|
2004-05-10 19:30:51 +02:00
|
|
|
K_PLATFORM,
|
|
|
|
K_OPT,
|
|
|
|
K_BANDWIDTH,
|
2004-06-21 06:37:27 +02:00
|
|
|
K_CONTACT,
|
2004-06-25 02:29:31 +02:00
|
|
|
K_NETWORK_STATUS,
|
2004-08-17 07:29:41 +02:00
|
|
|
K_UPTIME,
|
2004-10-07 23:10:40 +02:00
|
|
|
K_DIR_SIGNING_KEY,
|
2004-10-15 03:58:11 +02:00
|
|
|
K_FAMILY,
|
2005-05-02 23:22:31 +02:00
|
|
|
K_FINGERPRINT,
|
|
|
|
K_HIBERNATING,
|
|
|
|
K_READ_HISTORY,
|
|
|
|
K_WRITE_HISTORY,
|
2005-09-02 22:37:31 +02:00
|
|
|
K_NETWORK_STATUS_VERSION,
|
|
|
|
K_DIR_SOURCE,
|
|
|
|
K_DIR_OPTIONS,
|
|
|
|
K_CLIENT_VERSIONS,
|
|
|
|
K_SERVER_VERSIONS,
|
2011-11-08 22:51:30 +01:00
|
|
|
K_OR_ADDRESS,
|
2014-10-08 17:33:59 +02:00
|
|
|
K_ID,
|
2008-08-15 01:00:44 +02:00
|
|
|
K_P,
|
2012-10-25 03:59:55 +02:00
|
|
|
K_P6,
|
2005-09-02 22:37:31 +02:00
|
|
|
K_R,
|
2012-08-14 14:03:58 +02:00
|
|
|
K_A,
|
2005-09-02 22:37:31 +02:00
|
|
|
K_S,
|
2006-12-24 05:09:48 +01:00
|
|
|
K_V,
|
2008-08-15 01:00:44 +02:00
|
|
|
K_W,
|
2009-08-20 17:39:22 +02:00
|
|
|
K_M,
|
2007-04-16 06:17:58 +02:00
|
|
|
K_EXTRA_INFO,
|
|
|
|
K_EXTRA_INFO_DIGEST,
|
|
|
|
K_CACHES_EXTRA_INFO,
|
2007-10-29 20:10:42 +01:00
|
|
|
K_HIDDEN_SERVICE_DIR,
|
2008-09-26 20:58:45 +02:00
|
|
|
K_ALLOW_SINGLE_HOP_EXITS,
|
2012-10-25 03:59:55 +02:00
|
|
|
K_IPV6_POLICY,
|
2014-10-01 05:36:47 +02:00
|
|
|
K_ROUTER_SIG_ED25519,
|
|
|
|
K_IDENTITY_ED25519,
|
2015-06-01 17:24:55 +02:00
|
|
|
K_MASTER_KEY_ED25519,
|
2014-10-01 17:54:07 +02:00
|
|
|
K_ONION_KEY_CROSSCERT,
|
|
|
|
K_NTOR_ONION_KEY_CROSSCERT,
|
2007-05-02 23:37:55 +02:00
|
|
|
|
2009-08-14 15:30:24 +02:00
|
|
|
K_DIRREQ_END,
|
|
|
|
K_DIRREQ_V2_IPS,
|
|
|
|
K_DIRREQ_V3_IPS,
|
|
|
|
K_DIRREQ_V2_REQS,
|
|
|
|
K_DIRREQ_V3_REQS,
|
|
|
|
K_DIRREQ_V2_SHARE,
|
|
|
|
K_DIRREQ_V3_SHARE,
|
|
|
|
K_DIRREQ_V2_RESP,
|
|
|
|
K_DIRREQ_V3_RESP,
|
|
|
|
K_DIRREQ_V2_DIR,
|
|
|
|
K_DIRREQ_V3_DIR,
|
|
|
|
K_DIRREQ_V2_TUN,
|
|
|
|
K_DIRREQ_V3_TUN,
|
|
|
|
K_ENTRY_END,
|
|
|
|
K_ENTRY_IPS,
|
|
|
|
K_CELL_END,
|
|
|
|
K_CELL_PROCESSED,
|
|
|
|
K_CELL_QUEUED,
|
|
|
|
K_CELL_TIME,
|
|
|
|
K_CELL_CIRCS,
|
|
|
|
K_EXIT_END,
|
|
|
|
K_EXIT_WRITTEN,
|
|
|
|
K_EXIT_READ,
|
|
|
|
K_EXIT_OPENED,
|
|
|
|
|
2007-05-02 23:37:55 +02:00
|
|
|
K_DIR_KEY_CERTIFICATE_VERSION,
|
|
|
|
K_DIR_IDENTITY_KEY,
|
|
|
|
K_DIR_KEY_PUBLISHED,
|
|
|
|
K_DIR_KEY_EXPIRES,
|
|
|
|
K_DIR_KEY_CERTIFICATION,
|
2008-12-12 19:31:39 +01:00
|
|
|
K_DIR_KEY_CROSSCERT,
|
2007-10-19 18:28:10 +02:00
|
|
|
K_DIR_ADDRESS,
|
2014-10-28 18:12:52 +01:00
|
|
|
K_DIR_TUNNELLED,
|
2007-05-02 23:37:55 +02:00
|
|
|
|
|
|
|
K_VOTE_STATUS,
|
2007-06-03 05:05:10 +02:00
|
|
|
K_VALID_AFTER,
|
|
|
|
K_FRESH_UNTIL,
|
2007-05-02 23:37:55 +02:00
|
|
|
K_VALID_UNTIL,
|
2007-06-03 05:05:10 +02:00
|
|
|
K_VOTING_DELAY,
|
|
|
|
|
2007-05-02 23:37:55 +02:00
|
|
|
K_KNOWN_FLAGS,
|
2009-09-15 04:15:57 +02:00
|
|
|
K_PARAMS,
|
2010-01-30 00:40:40 +01:00
|
|
|
K_BW_WEIGHTS,
|
2007-05-02 23:37:55 +02:00
|
|
|
K_VOTE_DIGEST,
|
|
|
|
K_CONSENSUS_DIGEST,
|
2009-09-16 18:34:44 +02:00
|
|
|
K_ADDITIONAL_DIGEST,
|
|
|
|
K_ADDITIONAL_SIGNATURE,
|
2007-07-17 19:49:16 +02:00
|
|
|
K_CONSENSUS_METHODS,
|
2007-10-19 04:15:47 +02:00
|
|
|
K_CONSENSUS_METHOD,
|
2008-05-12 04:14:01 +02:00
|
|
|
K_LEGACY_DIR_KEY,
|
2010-02-27 22:33:46 +01:00
|
|
|
K_DIRECTORY_FOOTER,
|
2015-01-09 17:36:47 +01:00
|
|
|
K_PACKAGE,
|
2007-05-02 23:37:55 +02:00
|
|
|
|
2007-09-26 18:19:44 +02:00
|
|
|
A_PURPOSE,
|
2009-08-24 18:51:33 +02:00
|
|
|
A_LAST_LISTED,
|
2012-10-12 18:22:13 +02:00
|
|
|
A_UNKNOWN_,
|
2007-09-26 18:19:44 +02:00
|
|
|
|
2007-10-28 20:48:14 +01:00
|
|
|
R_RENDEZVOUS_SERVICE_DESCRIPTOR,
|
|
|
|
R_VERSION,
|
|
|
|
R_PERMANENT_KEY,
|
|
|
|
R_SECRET_ID_PART,
|
|
|
|
R_PUBLICATION_TIME,
|
|
|
|
R_PROTOCOL_VERSIONS,
|
|
|
|
R_INTRODUCTION_POINTS,
|
|
|
|
R_SIGNATURE,
|
|
|
|
|
|
|
|
R_IPO_IDENTIFIER,
|
|
|
|
R_IPO_IP_ADDRESS,
|
|
|
|
R_IPO_ONION_PORT,
|
|
|
|
R_IPO_ONION_KEY,
|
|
|
|
R_IPO_SERVICE_KEY,
|
|
|
|
|
2008-08-08 16:36:11 +02:00
|
|
|
C_CLIENT_NAME,
|
|
|
|
C_DESCRIPTOR_COOKIE,
|
|
|
|
C_CLIENT_KEY,
|
|
|
|
|
2012-10-12 18:22:13 +02:00
|
|
|
ERR_,
|
|
|
|
EOF_,
|
|
|
|
NIL_
|
2004-05-10 19:30:51 +02:00
|
|
|
} directory_keyword;
|
|
|
|
|
2007-09-26 18:19:44 +02:00
|
|
|
#define MIN_ANNOTATION A_PURPOSE
|
2012-10-12 18:22:13 +02:00
|
|
|
#define MAX_ANNOTATION A_UNKNOWN_
|
2007-09-26 18:19:44 +02:00
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
/** Structure to hold a single directory token.
|
|
|
|
*
|
|
|
|
* We parse a directory by breaking it into "tokens", each consisting
|
|
|
|
* of a keyword, a line full of arguments, and a binary object. The
|
|
|
|
* arguments and object are both optional, depending on the keyword
|
|
|
|
* type.
|
2008-03-26 17:56:34 +01:00
|
|
|
*
|
|
|
|
* This structure is only allocated in memareas; do not allocate it on
|
2009-09-28 16:37:01 +02:00
|
|
|
* the heap, or token_clear() won't work.
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
|
|
|
typedef struct directory_token_t {
|
|
|
|
directory_keyword tp; /**< Type of the token. */
|
2008-03-26 17:56:34 +01:00
|
|
|
int n_args:30; /**< Number of elements in args */
|
2004-05-10 19:30:51 +02:00
|
|
|
char **args; /**< Array of arguments from keyword line. */
|
2008-03-26 17:56:37 +01:00
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
char *object_type; /**< -----BEGIN [object_type]-----*/
|
2007-04-16 06:18:21 +02:00
|
|
|
size_t object_size; /**< Bytes in object_body */
|
2004-05-10 19:30:51 +02:00
|
|
|
char *object_body; /**< Contents of object, base64-decoded. */
|
2008-03-26 17:56:34 +01:00
|
|
|
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_pk_t *key; /**< For public keys only. Heap-allocated. */
|
2008-03-26 17:56:34 +01:00
|
|
|
|
2012-10-12 18:22:13 +02:00
|
|
|
char *error; /**< For ERR_ tokens only. */
|
2004-05-10 19:30:51 +02:00
|
|
|
} directory_token_t;
|
|
|
|
|
|
|
|
/* ********************************************************************** */
|
|
|
|
|
|
|
|
/** We use a table of rules to decide how to parse each token type. */
|
|
|
|
|
|
|
|
/** Rules for whether the keyword needs an object. */
|
|
|
|
typedef enum {
|
2007-05-15 00:51:05 +02:00
|
|
|
NO_OBJ, /**< No object, ever. */
|
|
|
|
NEED_OBJ, /**< Object is required. */
|
2008-08-08 16:36:11 +02:00
|
|
|
NEED_SKEY_1024,/**< Object is required, and must be a 1024 bit private key */
|
2007-05-15 00:51:05 +02:00
|
|
|
NEED_KEY_1024, /**< Object is required, and must be a 1024 bit public key */
|
|
|
|
NEED_KEY, /**< Object is required, and must be a public key. */
|
|
|
|
OBJ_OK, /**< Object is optional. */
|
2004-05-10 19:30:51 +02:00
|
|
|
} obj_syntax;
|
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
#define AT_START 1
|
2007-06-03 05:05:10 +02:00
|
|
|
#define AT_END 2
|
2007-05-15 00:51:05 +02:00
|
|
|
|
|
|
|
/** Determines the parsing rules for a single token type. */
|
2007-04-16 06:18:21 +02:00
|
|
|
typedef struct token_rule_t {
|
2007-05-15 00:51:05 +02:00
|
|
|
/** The string value of the keyword identifying the type of item. */
|
|
|
|
const char *t;
|
|
|
|
/** The corresponding directory_keyword enum. */
|
|
|
|
directory_keyword v;
|
|
|
|
/** Minimum number of arguments for this item */
|
|
|
|
int min_args;
|
|
|
|
/** Maximum number of arguments for this item */
|
|
|
|
int max_args;
|
|
|
|
/** If true, we concatenate all arguments for this item into a single
|
|
|
|
* string. */
|
|
|
|
int concat_args;
|
2009-05-27 23:55:51 +02:00
|
|
|
/** Requirements on object syntax for this item. */
|
2007-04-16 18:28:06 +02:00
|
|
|
obj_syntax os;
|
2007-05-15 00:51:05 +02:00
|
|
|
/** Lowest number of times this item may appear in a document. */
|
|
|
|
int min_cnt;
|
|
|
|
/** Highest number of times this item may appear in a document. */
|
|
|
|
int max_cnt;
|
|
|
|
/** One or more of AT_START/AT_END to limit where the item may appear in a
|
|
|
|
* document. */
|
|
|
|
int pos;
|
2007-10-04 18:21:58 +02:00
|
|
|
/** True iff this token is an annotation. */
|
2007-09-26 18:19:44 +02:00
|
|
|
int is_annotation;
|
2007-04-16 06:18:21 +02:00
|
|
|
} token_rule_t;
|
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/*
|
|
|
|
* Helper macros to define token tables. 's' is a string, 't' is a
|
|
|
|
* directory_keyword, 'a' is a trio of argument multiplicities, and 'o' is an
|
|
|
|
* object syntax.
|
|
|
|
*
|
|
|
|
*/
|
2007-04-16 06:18:21 +02:00
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/** Appears to indicate the end of a table. */
|
2012-10-12 18:22:13 +02:00
|
|
|
#define END_OF_TABLE { NULL, NIL_, 0,0,0, NO_OBJ, 0, INT_MAX, 0, 0 }
|
2007-05-15 00:51:05 +02:00
|
|
|
/** An item with no restrictions: used for obsolete document types */
|
2007-09-26 18:19:44 +02:00
|
|
|
#define T(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 }
|
2007-05-15 00:51:05 +02:00
|
|
|
/** An item with no restrictions on multiplicity or location. */
|
2007-09-26 18:19:44 +02:00
|
|
|
#define T0N(s,t,a,o) { s, t, a, o, 0, INT_MAX, 0, 0 }
|
2007-05-15 00:51:05 +02:00
|
|
|
/** An item that must appear exactly once */
|
2007-09-26 18:19:44 +02:00
|
|
|
#define T1(s,t,a,o) { s, t, a, o, 1, 1, 0, 0 }
|
2007-05-15 00:51:05 +02:00
|
|
|
/** An item that must appear exactly once, at the start of the document */
|
2007-09-26 18:19:44 +02:00
|
|
|
#define T1_START(s,t,a,o) { s, t, a, o, 1, 1, AT_START, 0 }
|
2007-05-15 00:51:05 +02:00
|
|
|
/** An item that must appear exactly once, at the end of the document */
|
2007-09-26 18:19:44 +02:00
|
|
|
#define T1_END(s,t,a,o) { s, t, a, o, 1, 1, AT_END, 0 }
|
2007-05-15 00:51:05 +02:00
|
|
|
/** An item that must appear one or more times */
|
2007-09-26 18:19:44 +02:00
|
|
|
#define T1N(s,t,a,o) { s, t, a, o, 1, INT_MAX, 0, 0 }
|
2007-05-15 00:51:05 +02:00
|
|
|
/** An item that must appear no more than once */
|
2007-09-26 18:19:44 +02:00
|
|
|
#define T01(s,t,a,o) { s, t, a, o, 0, 1, 0, 0 }
|
|
|
|
/** An annotation that must appear no more than once */
|
2008-12-17 18:20:42 +01:00
|
|
|
#define A01(s,t,a,o) { s, t, a, o, 0, 1, 0, 1 }
|
2007-05-15 00:51:05 +02:00
|
|
|
|
|
|
|
/* Argument multiplicity: any number of arguments. */
|
2007-04-16 18:28:06 +02:00
|
|
|
#define ARGS 0,INT_MAX,0
|
2007-05-15 00:51:05 +02:00
|
|
|
/* Argument multiplicity: no arguments. */
|
2007-04-16 18:28:06 +02:00
|
|
|
#define NO_ARGS 0,0,0
|
2007-05-15 00:51:05 +02:00
|
|
|
/* Argument multiplicity: concatenate all arguments. */
|
2007-04-16 18:28:06 +02:00
|
|
|
#define CONCAT_ARGS 1,1,1
|
2007-05-15 00:51:05 +02:00
|
|
|
/* Argument multiplicity: at least <b>n</b> arguments. */
|
2007-04-16 18:28:06 +02:00
|
|
|
#define GE(n) n,INT_MAX,0
|
2007-05-15 00:51:05 +02:00
|
|
|
/* Argument multiplicity: exactly <b>n</b> arguments. */
|
2007-04-16 18:28:06 +02:00
|
|
|
#define EQ(n) n,n,0
|
|
|
|
|
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 ),
|
|
|
|
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 ),
|
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 ),
|
2015-01-09 17:36:47 +01:00
|
|
|
T0N("package", K_PACKAGE, 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
|
|
|
|
|
|
|
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,
|
|
|
|
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,
|
|
|
|
digests_t *digests,
|
2009-09-16 23:01:01 +02:00
|
|
|
const char *start_str, const char *end_str,
|
|
|
|
char end_char);
|
2009-09-28 16:37:01 +02:00
|
|
|
static void token_clear(directory_token_t *tok);
|
2011-11-08 22:51:30 +01:00
|
|
|
static smartlist_t *find_all_by_keyword(smartlist_t *s, directory_keyword k);
|
2004-05-10 19:30:51 +02:00
|
|
|
static smartlist_t *find_all_exitpolicy(smartlist_t *s);
|
2012-10-12 18:22:13 +02:00
|
|
|
static directory_token_t *find_by_keyword_(smartlist_t *s,
|
2008-12-11 20:40:58 +01:00
|
|
|
directory_keyword keyword,
|
|
|
|
const char *keyword_str);
|
2012-10-12 18:22:13 +02:00
|
|
|
#define find_by_keyword(s, keyword) find_by_keyword_((s), (keyword), #keyword)
|
2008-12-11 20:40:58 +01:00
|
|
|
static directory_token_t *find_opt_by_keyword(smartlist_t *s,
|
|
|
|
directory_keyword keyword);
|
|
|
|
|
2007-09-27 18:08:10 +02:00
|
|
|
#define TS_ANNOTATIONS_OK 1
|
|
|
|
#define TS_NOCHECK 2
|
|
|
|
#define TS_NO_NEW_ANNOTATIONS 4
|
2008-03-26 17:33:33 +01:00
|
|
|
static int tokenize_string(memarea_t *area,
|
|
|
|
const char *start, const char *end,
|
2007-04-16 06:18:21 +02:00
|
|
|
smartlist_t *out,
|
2007-09-26 18:19:44 +02:00
|
|
|
token_rule_t *table,
|
2007-09-27 18:08:10 +02:00
|
|
|
int flags);
|
2008-03-26 17:33:33 +01:00
|
|
|
static directory_token_t *get_next_token(memarea_t *area,
|
|
|
|
const char **s,
|
2007-07-10 22:08:18 +02:00
|
|
|
const char *eos,
|
2007-05-15 00:51:05 +02:00
|
|
|
token_rule_t *table);
|
2008-12-12 19:31:39 +01:00
|
|
|
#define CST_CHECK_AUTHORITY (1<<0)
|
|
|
|
#define CST_NO_CHECK_OBJTYPE (1<<1)
|
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
|
|
|
|
#else
|
|
|
|
#define DUMP_AREA(a,name) STMT_NIL
|
|
|
|
#endif
|
|
|
|
|
2009-08-20 18:45:03 +02:00
|
|
|
/** Last time we dumped a descriptor to disk. */
|
|
|
|
static time_t last_desc_dumped = 0;
|
|
|
|
|
|
|
|
/** 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. */
|
|
|
|
static void
|
|
|
|
dump_desc(const char *desc, const char *type)
|
|
|
|
{
|
|
|
|
time_t now = time(NULL);
|
|
|
|
tor_assert(desc);
|
|
|
|
tor_assert(type);
|
|
|
|
if (!last_desc_dumped || last_desc_dumped + 60 < now) {
|
|
|
|
char *debugfile = get_datadir_fname("unparseable-desc");
|
|
|
|
size_t filelen = 50 + strlen(type) + strlen(desc);
|
|
|
|
char *content = tor_malloc_zero(filelen);
|
|
|
|
tor_snprintf(content, filelen, "Unable to parse descriptor of type "
|
|
|
|
"%s:\n%s", type, desc);
|
2014-03-28 04:53:03 +01:00
|
|
|
write_str_to_file(debugfile, content, 1);
|
2009-08-20 18:45:03 +02:00
|
|
|
log_info(LD_DIR, "Unable to parse descriptor of type %s. See file "
|
|
|
|
"unparseable-desc in data directory for details.", type);
|
|
|
|
tor_free(content);
|
|
|
|
tor_free(debugfile);
|
|
|
|
last_desc_dumped = now;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
router_get_networkstatus_v3_hashes(const char *s, digests_t *digests)
|
|
|
|
{
|
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
|
|
|
}
|
|
|
|
|
2004-10-07 23:10:40 +02:00
|
|
|
/** Return true iff <b>key</b> is allowed to sign directories.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
static int
|
2012-01-18 21:53:30 +01:00
|
|
|
dir_signing_key_is_trusted(crypto_pk_t *key)
|
2004-10-07 23:10:40 +02:00
|
|
|
{
|
|
|
|
char digest[DIGEST_LEN];
|
|
|
|
if (!key) return 0;
|
|
|
|
if (crypto_pk_get_digest(key, digest) < 0) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Error computing dir-signing-key digest");
|
2004-10-07 23:10:40 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (!router_digest_is_trusted_dir(digest)) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "Listed dir-signing-key is not trusted");
|
2004-10-07 23:10:40 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/** Check whether the object body of the token in <b>tok</b> has a good
|
|
|
|
* signature for <b>digest</b> using key <b>pkey</b>. If
|
2008-12-12 19:31:39 +01:00
|
|
|
* <b>CST_CHECK_AUTHORITY</b> is set, make sure that <b>pkey</b> is the key of
|
|
|
|
* a directory authority. If <b>CST_NO_CHECK_OBJTYPE</b> is set, do not check
|
|
|
|
* 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_authority = (flags & CST_CHECK_AUTHORITY);
|
|
|
|
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);
|
|
|
|
|
|
|
|
if (check_authority && !dir_signing_key_is_trusted(pkey)) {
|
|
|
|
log_warn(LD_DIR, "Key on %s did not come from an authority; rejecting",
|
|
|
|
doctype);
|
2004-10-19 20:19:59 +02:00
|
|
|
return -1;
|
2004-08-07 03:48:50 +02:00
|
|
|
}
|
2004-10-07 23:10:40 +02:00
|
|
|
|
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));
|
2011-05-11 22:23:42 +02:00
|
|
|
if (tor_memneq(digest, 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));
|
|
|
|
#else
|
|
|
|
(void)severity; /* suppress "unused parameter" warning */
|
|
|
|
#endif
|
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;
|
2007-09-27 18:08:10 +02:00
|
|
|
char *cp =
|
|
|
|
router->cache_info.signed_descriptor_body = tor_malloc(len+1);
|
|
|
|
if (prepend_annotations) {
|
2007-09-27 22:46:30 +02:00
|
|
|
memcpy(cp, prepend_annotations, prepend_len);
|
|
|
|
cp += 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. */
|
2008-02-20 00:29:45 +01:00
|
|
|
tor_assert(cp+(end-start_of_annotations) ==
|
|
|
|
router->cache_info.signed_descriptor_body+len);
|
2007-09-27 22:46:30 +02:00
|
|
|
memcpy(cp, start_of_annotations, end-start_of_annotations);
|
|
|
|
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;
|
|
|
|
}
|
2014-10-01 17:54:07 +02:00
|
|
|
router->signing_key_cert = cert; /* makes sure it gets freed. */
|
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",
|
|
|
|
' ', &signed_start, &signed_end) < 0) {
|
|
|
|
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];
|
2014-10-01 05:36:47 +02:00
|
|
|
if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) {
|
|
|
|
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],
|
|
|
|
ntor_cc_cert, &ntor_cc_pk) < 0) {
|
|
|
|
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. */
|
|
|
|
if (cert->valid_until < ntor_cc_cert->valid_until)
|
|
|
|
router->cert_expiration_time = cert->valid_until;
|
|
|
|
else
|
|
|
|
router->cert_expiration_time = ntor_cc_cert->valid_until;
|
|
|
|
}
|
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], " ");
|
|
|
|
if (base16_decode(d, DIGEST_LEN, tok->args[0], strlen(tok->args[0]))) {
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_PLATFORM))) {
|
2004-05-10 19:30:51 +02:00
|
|
|
router->platform = tor_strdup(tok->args[0]);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-11-15 02:51:41 +01:00
|
|
|
if (policy_is_reject_star(router->exit_policy, AF_INET) &&
|
|
|
|
(!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;
|
|
|
|
}
|
|
|
|
smartlist_add(router->declared_family, tor_strdup(tok->args[i]));
|
|
|
|
}
|
|
|
|
}
|
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) {
|
2007-04-16 23:37:21 +02:00
|
|
|
base16_decode(router->cache_info.extra_info_digest,
|
|
|
|
DIGEST_LEN, tok->args[0], HEX_DIGEST_LEN);
|
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) {
|
|
|
|
if (digest256_from_base64(router->extra_info_digest256, tok->args[1])
|
|
|
|
< 0) {
|
|
|
|
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-10-31 20:17:07 +01:00
|
|
|
note_crypto_pk_op(VERIFY_RTR);
|
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
|
|
|
|
|
|
|
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,
|
|
|
|
tok->args[1], HEX_DIGEST_LEN)) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
extrainfo->signing_key_cert = cert; /* makes sure it gets freed. */
|
|
|
|
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",
|
|
|
|
' ', &signed_start, &signed_end) < 0) {
|
|
|
|
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];
|
|
|
|
if (tor_cert_get_checkable_sig(&check[0], cert, NULL) < 0) {
|
|
|
|
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) {
|
|
|
|
note_crypto_pk_op(VERIFY_RTR);
|
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],
|
|
|
|
strlen(tok->args[0]))) {
|
|
|
|
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);
|
2012-06-15 15:37:40 +02:00
|
|
|
/* XXX024 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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
**/
|
2005-09-02 22:37:31 +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) {
|
|
|
|
offset = -1; /* There is no identity digest */
|
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) {
|
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
|
|
|
}
|
|
|
|
}
|
2008-12-11 20:40:58 +01:00
|
|
|
if ((tok = find_opt_by_keyword(tokens, K_V))) {
|
2007-04-16 18:28:06 +02:00
|
|
|
tor_assert(tok->n_args == 1);
|
2006-12-24 05:09:48 +01:00
|
|
|
rs->version_known = 1;
|
|
|
|
if (strcmpstart(tok->args[0], "Tor ")) {
|
|
|
|
} else {
|
2012-12-06 07:53:29 +01:00
|
|
|
rs->version_supports_extend2_cells =
|
2013-03-13 17:47:48 +01:00
|
|
|
tor_version_as_new_as(tok->args[0], "0.2.4.8-alpha");
|
2005-09-02 22:37:31 +02:00
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
if (vote_rs) {
|
2008-03-26 17:33:33 +01:00
|
|
|
vote_rs->version = tor_strdup(tok->args[0]);
|
2007-06-03 05:05:10 +02:00
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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 weight_scale;
|
|
|
|
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
|
|
|
|
2013-01-31 03:41:10 +01:00
|
|
|
weight_scale = networkstatus_get_weight_scale_param(ns);
|
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;
|
|
|
|
Wgm /= weight_scale;
|
|
|
|
Wgd /= weight_scale;
|
|
|
|
|
|
|
|
Wmg /= weight_scale;
|
|
|
|
Wmm /= weight_scale;
|
|
|
|
Wme /= weight_scale;
|
|
|
|
Wmd /= weight_scale;
|
|
|
|
|
|
|
|
Weg /= weight_scale;
|
|
|
|
Wem /= weight_scale;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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;
|
2009-09-16 23:01:01 +02:00
|
|
|
digests_t ns_digests;
|
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;
|
|
|
|
int ok;
|
|
|
|
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;
|
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
if (router_get_networkstatus_v3_hashes(s, &ns_digests)) {
|
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)) {
|
|
|
|
log_warn(LD_DIR, "Error tokenizing network-status vote 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));
|
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)
|
2008-03-26 17:33:33 +01:00
|
|
|
smartlist_add(ns->supported_methods, tor_strdup(tok->args[i]));
|
2007-10-19 20:56:24 +02:00
|
|
|
} else {
|
|
|
|
smartlist_add(ns->supported_methods, tor_strdup("1"));
|
|
|
|
}
|
|
|
|
} 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) {
|
|
|
|
ns->consensus_method = (int)tor_parse_long(tok->args[0], 10, 1, INT_MAX,
|
|
|
|
&ok, NULL);
|
|
|
|
if (!ok)
|
|
|
|
goto err;
|
|
|
|
} else {
|
|
|
|
ns->consensus_method = 1;
|
|
|
|
}
|
2007-06-05 00:29:00 +02:00
|
|
|
}
|
2007-06-03 05:05:10 +02:00
|
|
|
|
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);
|
|
|
|
ns->vote_seconds =
|
|
|
|
(int) tor_parse_long(tok->args[0], 10, 0, INT_MAX, &ok, NULL);
|
|
|
|
if (!ok)
|
|
|
|
goto err;
|
|
|
|
ns->dist_seconds =
|
2007-06-13 20:16:05 +02:00
|
|
|
(int) tor_parse_long(tok->args[1], 10, 0, INT_MAX, &ok, NULL);
|
2007-06-03 05:05:10 +02:00
|
|
|
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,
|
|
|
|
smartlist_add(ns->package_lines, tor_strdup(t->args[0])));
|
|
|
|
}
|
|
|
|
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) {
|
2008-03-26 17:33:33 +01:00
|
|
|
smartlist_add(ns->known_flags, tor_strdup(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);
|
2009-09-15 04:15:57 +02:00
|
|
|
smartlist_add(ns->net_params, tor_strdup(tok->args[i]));
|
|
|
|
}
|
|
|
|
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),
|
|
|
|
tok->args[1], HEX_DIGEST_LEN) < 0) {
|
|
|
|
log_warn(LD_DIR, "Error decoding identity digest %s in "
|
|
|
|
"network-status vote.", escaped(tok->args[1]));
|
|
|
|
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);
|
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),
|
|
|
|
tok->args[0], HEX_DIGEST_LEN) < 0) {
|
|
|
|
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) {
|
|
|
|
log_warn(LD_DIR, "Missing 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
|
|
|
} 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) {
|
|
|
|
networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
|
|
|
|
if (base16_decode(voter->legacy_id_digest, DIGEST_LEN,
|
|
|
|
tok->args[0], HEX_DIGEST_LEN)<0)
|
|
|
|
bad = 1;
|
|
|
|
else
|
|
|
|
bad = 0;
|
|
|
|
}
|
|
|
|
if (bad) {
|
|
|
|
log_warn(LD_DIR, "Invalid legacy key digest %s on vote.",
|
|
|
|
escaped(tok->args[0]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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,
|
2009-09-23 21:23:04 +02:00
|
|
|
rs, 0, 0))
|
2007-06-05 00:29:00 +02:00
|
|
|
smartlist_add(ns->routerstatus_list, rs);
|
|
|
|
else {
|
|
|
|
tor_free(rs->version);
|
|
|
|
tor_free(rs);
|
|
|
|
}
|
|
|
|
} 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,
|
|
|
|
flav)))
|
2007-06-05 00:29:00 +02:00
|
|
|
smartlist_add(ns->routerstatus_list, rs);
|
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) {
|
2007-06-04 21:19:01 +02:00
|
|
|
log_warn(LD_DIR, "Vote networkstatus entries not sorted by identity "
|
|
|
|
"digest");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
smartlist_add(ns->weight_params, tor_strdup(tok->args[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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),
|
2009-09-23 21:23:04 +02:00
|
|
|
id_hexdigest, HEX_DIGEST_LEN) < 0) {
|
2007-06-05 00:29:00 +02:00
|
|
|
log_warn(LD_DIR, "Error decoding declared identity %s in "
|
2009-09-23 21:23:04 +02:00
|
|
|
"network-status vote.", escaped(id_hexdigest));
|
2007-06-05 00:29:00 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
if (!(v = networkstatus_get_voter_by_id(ns, declared_identity))) {
|
2008-01-10 18:54:24 +01:00
|
|
|
log_warn(LD_DIR, "ID on signature on network-status vote does not match "
|
2007-06-05 00:29:00 +02:00
|
|
|
"any declared directory source.");
|
|
|
|
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),
|
2009-09-23 21:23:04 +02:00
|
|
|
sk_hexdigest, HEX_DIGEST_LEN) < 0) {
|
|
|
|
log_warn(LD_DIR, "Error decoding declared signing key digest %s in "
|
|
|
|
"network-status vote.", 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
|
|
|
}
|
|
|
|
|
2009-09-16 23:01:01 +02:00
|
|
|
if (voter_get_sig_by_algorithm(v, sig->alg)) {
|
|
|
|
/* 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 "
|
|
|
|
"that contains two votes from the same voter with the same "
|
|
|
|
"algorithm. Ignoring the second vote.");
|
|
|
|
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,
|
2009-09-16 23:01:01 +02:00
|
|
|
"network-status vote")) {
|
|
|
|
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) {
|
|
|
|
log_warn(LD_DIR, "No signatures on networkstatus vote.");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2009-10-15 22:06:00 +02:00
|
|
|
/** Return the digests_t that holds the digests of the
|
|
|
|
* <b>flavor_name</b>-flavored networkstatus according to the detached
|
|
|
|
* signatures document <b>sigs</b>, allocating a new digests_t as neeeded. */
|
2009-09-16 18:34:44 +02:00
|
|
|
static digests_t *
|
|
|
|
detached_get_digests(ns_detached_signatures_t *sigs, const char *flavor_name)
|
|
|
|
{
|
|
|
|
digests_t *d = strmap_get(sigs->digests, flavor_name);
|
|
|
|
if (!d) {
|
|
|
|
d = tor_malloc_zero(sizeof(digests_t));
|
|
|
|
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>,
|
|
|
|
* allocating a new digests_t as neeeded. */
|
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;
|
2009-09-16 23:01:01 +02:00
|
|
|
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;
|
|
|
|
size_t expected_length;
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
expected_length =
|
|
|
|
(alg == DIGEST_SHA1) ? HEX_DIGEST_LEN : HEX_DIGEST256_LEN;
|
|
|
|
|
|
|
|
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);
|
|
|
|
if (!tor_mem_is_zero(digests->d[alg], DIGEST256_LEN)) {
|
|
|
|
log_warn(LD_DIR, "Multiple digests for %s with %s on detached "
|
|
|
|
"signatures document", flavor, algname);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (base16_decode(digests->d[alg], DIGEST256_LEN,
|
|
|
|
hexdigest, strlen(hexdigest)) < 0) {
|
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),
|
2009-09-16 18:34:44 +02:00
|
|
|
id_hexdigest, HEX_DIGEST_LEN) < 0) {
|
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),
|
2009-09-16 18:34:44 +02:00
|
|
|
sk_hexdigest, HEX_DIGEST_LEN) < 0) {
|
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.
|
|
|
|
*
|
2015-12-16 14:49:32 +01:00
|
|
|
* Set *<b>malformed_list>/b> to true if the entire policy list should be
|
|
|
|
* 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
|
|
|
/*
|
|
|
|
* Low-level tokenizer for router descriptors and directories.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** Free all resources allocated for <b>tok</b> */
|
|
|
|
static void
|
2009-09-28 16:37:01 +02:00
|
|
|
token_clear(directory_token_t *tok)
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
|
|
|
if (tok->key)
|
2012-01-18 21:53:30 +01:00
|
|
|
crypto_pk_free(tok->key);
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
2008-03-26 17:56:34 +01:00
|
|
|
#define ALLOC_ZERO(sz) memarea_alloc_zero(area,sz)
|
|
|
|
#define ALLOC(sz) memarea_alloc(area,sz)
|
|
|
|
#define STRDUP(str) memarea_strdup(area,str)
|
|
|
|
#define STRNDUP(str,n) memarea_strndup(area,(str),(n))
|
2008-03-26 17:33:33 +01:00
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
#define RET_ERR(msg) \
|
2007-06-17 20:22:39 +02:00
|
|
|
STMT_BEGIN \
|
2009-09-28 16:37:01 +02:00
|
|
|
if (tok) token_clear(tok); \
|
2008-03-26 17:33:33 +01:00
|
|
|
tok = ALLOC_ZERO(sizeof(directory_token_t)); \
|
2012-10-12 18:22:13 +02:00
|
|
|
tok->tp = ERR_; \
|
2008-03-26 17:33:33 +01:00
|
|
|
tok->error = STRDUP(msg); \
|
2007-06-17 20:22:39 +02:00
|
|
|
goto done_tokenizing; \
|
|
|
|
STMT_END
|
2007-05-15 00:51:05 +02:00
|
|
|
|
2008-12-22 18:53:04 +01:00
|
|
|
/** Helper: make sure that the token <b>tok</b> with keyword <b>kwd</b> obeys
|
|
|
|
* the object syntax of <b>o_syn</b>. Allocate all storage in <b>area</b>.
|
2012-10-12 18:22:13 +02:00
|
|
|
* Return <b>tok</b> on success, or a new ERR_ token if the token didn't
|
2008-12-22 18:53:04 +01:00
|
|
|
* conform to the syntax we wanted.
|
|
|
|
**/
|
2015-12-10 16:19:43 +01:00
|
|
|
static inline directory_token_t *
|
2008-03-26 17:33:33 +01:00
|
|
|
token_check_object(memarea_t *area, const char *kwd,
|
2007-05-15 00:51:05 +02:00
|
|
|
directory_token_t *tok, obj_syntax o_syn)
|
|
|
|
{
|
|
|
|
char ebuf[128];
|
|
|
|
switch (o_syn) {
|
|
|
|
case NO_OBJ:
|
2008-12-22 18:53:04 +01:00
|
|
|
/* No object is allowed for this token. */
|
2007-05-15 00:51:05 +02:00
|
|
|
if (tok->object_body) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected object for %s", kwd);
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
|
|
|
if (tok->key) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Unexpected public key for %s", kwd);
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NEED_OBJ:
|
2008-12-22 18:53:04 +01:00
|
|
|
/* There must be a (non-key) object. */
|
2007-05-15 00:51:05 +02:00
|
|
|
if (!tok->object_body) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Missing object for %s", kwd);
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
|
|
|
break;
|
2008-12-22 18:53:04 +01:00
|
|
|
case NEED_KEY_1024: /* There must be a 1024-bit public key. */
|
|
|
|
case NEED_SKEY_1024: /* There must be a 1024-bit private key. */
|
2011-06-02 18:32:59 +02:00
|
|
|
if (tok->key && crypto_pk_num_bits(tok->key) != PK_BYTES*8) {
|
2007-05-15 00:51:05 +02:00
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Wrong size on key for %s: %d bits",
|
2011-06-02 18:32:59 +02:00
|
|
|
kwd, crypto_pk_num_bits(tok->key));
|
2007-05-15 00:51:05 +02:00
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
|
|
|
/* fall through */
|
2008-12-22 18:53:04 +01:00
|
|
|
case NEED_KEY: /* There must be some kind of key. */
|
2007-05-15 00:51:05 +02:00
|
|
|
if (!tok->key) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Missing public key for %s", kwd);
|
2009-04-09 21:58:16 +02:00
|
|
|
RET_ERR(ebuf);
|
2007-05-15 00:51:05 +02:00
|
|
|
}
|
2008-08-08 16:36:11 +02:00
|
|
|
if (o_syn != NEED_SKEY_1024) {
|
|
|
|
if (crypto_pk_key_is_private(tok->key)) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf),
|
|
|
|
"Private key given for %s, which wants a public key", kwd);
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
|
|
|
} else { /* o_syn == NEED_SKEY_1024 */
|
|
|
|
if (!crypto_pk_key_is_private(tok->key)) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf),
|
|
|
|
"Public key given for %s, which wants a private key", kwd);
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
|
|
|
}
|
2007-05-15 00:51:05 +02:00
|
|
|
break;
|
|
|
|
case OBJ_OK:
|
2008-12-22 18:53:04 +01:00
|
|
|
/* Anything goes with this token. */
|
2007-05-15 00:51:05 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
done_tokenizing:
|
|
|
|
return tok;
|
|
|
|
}
|
|
|
|
|
2008-12-29 17:54:56 +01:00
|
|
|
/** Helper: parse space-separated arguments from the string <b>s</b> ending at
|
|
|
|
* <b>eol</b>, and store them in the args field of <b>tok</b>. Store the
|
|
|
|
* number of parsed elements into the n_args field of <b>tok</b>. Allocate
|
|
|
|
* all storage in <b>area</b>. Return the number of arguments parsed, or
|
|
|
|
* return -1 if there was an insanely high number of arguments. */
|
2015-12-10 16:19:43 +01:00
|
|
|
static inline int
|
2008-12-29 17:54:56 +01:00
|
|
|
get_token_arguments(memarea_t *area, directory_token_t *tok,
|
|
|
|
const char *s, const char *eol)
|
|
|
|
{
|
|
|
|
/** Largest number of arguments we'll accept to any token, ever. */
|
|
|
|
#define MAX_ARGS 512
|
|
|
|
char *mem = memarea_strndup(area, s, eol-s);
|
|
|
|
char *cp = mem;
|
|
|
|
int j = 0;
|
|
|
|
char *args[MAX_ARGS];
|
|
|
|
while (*cp) {
|
|
|
|
if (j == MAX_ARGS)
|
|
|
|
return -1;
|
|
|
|
args[j++] = cp;
|
|
|
|
cp = (char*)find_whitespace(cp);
|
|
|
|
if (!cp || !*cp)
|
|
|
|
break; /* End of the line. */
|
|
|
|
*cp++ = '\0';
|
|
|
|
cp = (char*)eat_whitespace(cp);
|
|
|
|
}
|
|
|
|
tok->n_args = j;
|
|
|
|
tok->args = memarea_memdup(area, args, j*sizeof(char*));
|
|
|
|
return j;
|
|
|
|
#undef MAX_ARGS
|
|
|
|
}
|
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/** Helper function: read the next token from *s, advance *s to the end of the
|
|
|
|
* token, and return the parsed token. Parse *<b>s</b> according to the list
|
|
|
|
* of tokens in <b>table</b>.
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
|
|
|
static directory_token_t *
|
2008-03-26 17:33:33 +01:00
|
|
|
get_next_token(memarea_t *area,
|
|
|
|
const char **s, const char *eos, token_rule_t *table)
|
2005-06-11 20:52:12 +02:00
|
|
|
{
|
2011-01-10 18:12:11 +01:00
|
|
|
/** Reject any object at least this big; it is probably an overflow, an
|
|
|
|
* attack, a bug, or some other nonsense. */
|
|
|
|
#define MAX_UNPARSED_OBJECT_SIZE (128*1024)
|
2011-01-19 19:25:17 +01:00
|
|
|
/** Reject any line at least this big; it is probably an overflow, an
|
|
|
|
* attack, a bug, or some other nonsense. */
|
|
|
|
#define MAX_LINE_LENGTH (128*1024)
|
2011-01-10 18:12:11 +01:00
|
|
|
|
2007-07-16 20:26:31 +02:00
|
|
|
const char *next, *eol, *obstart;
|
2008-02-22 20:09:45 +01:00
|
|
|
size_t obname_len;
|
2008-03-26 17:56:34 +01:00
|
|
|
int i;
|
2004-05-10 19:30:51 +02:00
|
|
|
directory_token_t *tok;
|
|
|
|
obj_syntax o_syn = NO_OBJ;
|
2007-04-16 18:28:06 +02:00
|
|
|
char ebuf[128];
|
|
|
|
const char *kwd = "";
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2008-03-26 17:56:34 +01:00
|
|
|
tor_assert(area);
|
2008-03-26 17:33:33 +01:00
|
|
|
tok = ALLOC_ZERO(sizeof(directory_token_t));
|
2012-10-12 18:22:13 +02:00
|
|
|
tok->tp = ERR_;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2007-07-16 20:26:31 +02:00
|
|
|
/* Set *s to first token, eol to end-of-line, next to after first token */
|
|
|
|
*s = eat_whitespace_eos(*s, eos); /* eat multi-line whitespace */
|
2008-02-20 17:57:39 +01:00
|
|
|
tor_assert(eos >= *s);
|
2007-07-16 20:26:31 +02:00
|
|
|
eol = memchr(*s, '\n', eos-*s);
|
|
|
|
if (!eol)
|
|
|
|
eol = eos;
|
2011-01-19 19:25:17 +01:00
|
|
|
if (eol - *s > MAX_LINE_LENGTH) {
|
|
|
|
RET_ERR("Line far too long");
|
|
|
|
}
|
|
|
|
|
2007-07-16 20:26:31 +02:00
|
|
|
next = find_whitespace_eos(*s, eol);
|
|
|
|
|
|
|
|
if (!strcmp_len(*s, "opt", next-*s)) {
|
2007-05-15 00:51:05 +02:00
|
|
|
/* Skip past an "opt" at the start of the line. */
|
2007-07-16 20:26:31 +02:00
|
|
|
*s = eat_whitespace_eos_no_nl(next, eol);
|
|
|
|
next = find_whitespace_eos(*s, eol);
|
|
|
|
} else if (*s == eos) { /* If no "opt", and end-of-line, line is invalid */
|
|
|
|
RET_ERR("Unexpected EOF");
|
2004-06-21 06:37:27 +02:00
|
|
|
}
|
2007-07-16 20:26:31 +02:00
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
/* Search the table for the appropriate entry. (I tried a binary search
|
|
|
|
* instead, but it wasn't any faster.) */
|
2007-04-16 06:18:21 +02:00
|
|
|
for (i = 0; table[i].t ; ++i) {
|
2007-07-16 20:26:31 +02:00
|
|
|
if (!strcmp_len(*s, table[i].t, next-*s)) {
|
2004-05-10 19:30:51 +02:00
|
|
|
/* We've found the keyword. */
|
2007-04-16 18:28:06 +02:00
|
|
|
kwd = table[i].t;
|
2007-04-16 06:18:21 +02:00
|
|
|
tok->tp = table[i].v;
|
|
|
|
o_syn = table[i].os;
|
2007-07-16 20:26:31 +02:00
|
|
|
*s = eat_whitespace_eos_no_nl(next, eol);
|
2007-08-14 04:23:57 +02:00
|
|
|
/* We go ahead whether there are arguments or not, so that tok->args is
|
|
|
|
* always set if we want arguments. */
|
|
|
|
if (table[i].concat_args) {
|
|
|
|
/* The keyword takes the line as a single argument */
|
2008-03-26 17:33:33 +01:00
|
|
|
tok->args = ALLOC(sizeof(char*));
|
|
|
|
tok->args[0] = STRNDUP(*s,eol-*s); /* Grab everything on line */
|
2007-08-14 04:23:57 +02:00
|
|
|
tok->n_args = 1;
|
|
|
|
} else {
|
|
|
|
/* This keyword takes multiple arguments. */
|
2008-12-29 17:54:56 +01:00
|
|
|
if (get_token_arguments(area, tok, *s, eol)<0) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf),"Far too many arguments to %s", kwd);
|
|
|
|
RET_ERR(ebuf);
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2008-03-26 17:56:34 +01:00
|
|
|
*s = eol;
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2007-04-16 18:28:06 +02:00
|
|
|
if (tok->n_args < table[i].min_args) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Too few arguments to %s", kwd);
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
} else if (tok->n_args > table[i].max_args) {
|
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Too many arguments to %s", kwd);
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2007-07-16 20:26:31 +02:00
|
|
|
|
2012-10-12 18:22:13 +02:00
|
|
|
if (tok->tp == ERR_) {
|
2007-09-26 18:19:44 +02:00
|
|
|
/* No keyword matched; call it an "K_opt" or "A_unrecognized" */
|
|
|
|
if (**s == '@')
|
2012-10-12 18:22:13 +02:00
|
|
|
tok->tp = A_UNKNOWN_;
|
2007-09-26 18:19:44 +02:00
|
|
|
else
|
|
|
|
tok->tp = K_OPT;
|
2008-03-26 17:33:33 +01:00
|
|
|
tok->args = ALLOC(sizeof(char*));
|
|
|
|
tok->args[0] = STRNDUP(*s, eol-*s);
|
2006-12-24 05:09:42 +01:00
|
|
|
tok->n_args = 1;
|
|
|
|
o_syn = OBJ_OK;
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2007-07-16 20:26:31 +02:00
|
|
|
|
|
|
|
/* Check whether there's an object present */
|
|
|
|
*s = eat_whitespace_eos(eol, eos); /* Scan from end of first line */
|
2008-02-20 17:57:39 +01:00
|
|
|
tor_assert(eos >= *s);
|
2007-07-16 20:26:31 +02:00
|
|
|
eol = memchr(*s, '\n', eos-*s);
|
|
|
|
if (!eol || eol-*s<11 || strcmpstart(*s, "-----BEGIN ")) /* No object. */
|
2007-05-15 00:51:05 +02:00
|
|
|
goto check_object;
|
2007-07-16 20:26:31 +02:00
|
|
|
|
|
|
|
obstart = *s; /* Set obstart to start of object spec */
|
2009-04-09 21:58:16 +02:00
|
|
|
if (*s+16 >= eol || memchr(*s+11,'\0',eol-*s-16) || /* no short lines, */
|
2011-01-15 16:42:11 +01:00
|
|
|
strcmp_len(eol-5, "-----", 5) || /* nuls or invalid endings */
|
|
|
|
(eol-*s) > MAX_UNPARSED_OBJECT_SIZE) { /* name too long */
|
2004-05-10 19:30:51 +02:00
|
|
|
RET_ERR("Malformed object: bad begin line");
|
|
|
|
}
|
2008-03-26 17:33:33 +01:00
|
|
|
tok->object_type = STRNDUP(*s+11, eol-*s-16);
|
2007-07-16 20:26:31 +02:00
|
|
|
obname_len = eol-*s-16; /* store objname length here to avoid a strlen() */
|
|
|
|
*s = eol+1; /* Set *s to possible start of object data (could be eos) */
|
|
|
|
|
|
|
|
/* Go to the end of the object */
|
2007-07-10 22:08:18 +02:00
|
|
|
next = tor_memstr(*s, eos-*s, "-----END ");
|
2004-05-10 19:30:51 +02:00
|
|
|
if (!next) {
|
2007-07-16 20:26:31 +02:00
|
|
|
RET_ERR("Malformed object: missing object end line");
|
|
|
|
}
|
2008-02-20 17:57:39 +01:00
|
|
|
tor_assert(eos >= next);
|
2007-07-16 20:26:31 +02:00
|
|
|
eol = memchr(next, '\n', eos-next);
|
|
|
|
if (!eol) /* end-of-line marker, or eos if there's no '\n' */
|
|
|
|
eol = eos;
|
|
|
|
/* Validate the ending tag, which should be 9 + NAME + 5 + eol */
|
2008-02-22 20:09:45 +01:00
|
|
|
if ((size_t)(eol-next) != 9+obname_len+5 ||
|
2007-07-16 20:26:31 +02:00
|
|
|
strcmp_len(next+9, tok->object_type, obname_len) ||
|
|
|
|
strcmp_len(eol-5, "-----", 5)) {
|
2012-12-19 22:51:53 +01:00
|
|
|
tor_snprintf(ebuf, sizeof(ebuf), "Malformed object: mismatched end tag %s",
|
2007-07-16 20:26:31 +02:00
|
|
|
tok->object_type);
|
|
|
|
ebuf[sizeof(ebuf)-1] = '\0';
|
|
|
|
RET_ERR(ebuf);
|
|
|
|
}
|
2011-01-10 18:12:11 +01:00
|
|
|
if (next - *s > MAX_UNPARSED_OBJECT_SIZE)
|
|
|
|
RET_ERR("Couldn't parse object: missing footer or object much too big.");
|
|
|
|
|
2008-08-08 16:36:11 +02:00
|
|
|
if (!strcmp(tok->object_type, "RSA PUBLIC KEY")) { /* If it's a public key */
|
2012-01-18 21:53:30 +01:00
|
|
|
tok->key = crypto_pk_new();
|
2007-07-16 20:26:31 +02:00
|
|
|
if (crypto_pk_read_public_key_from_string(tok->key, obstart, eol-obstart))
|
2004-05-10 19:30:51 +02:00
|
|
|
RET_ERR("Couldn't parse public key.");
|
2008-08-08 16:36:11 +02:00
|
|
|
} else if (!strcmp(tok->object_type, "RSA PRIVATE KEY")) { /* private key */
|
2012-01-18 21:53:30 +01:00
|
|
|
tok->key = crypto_pk_new();
|
2011-01-10 18:07:34 +01:00
|
|
|
if (crypto_pk_read_private_key_from_string(tok->key, obstart, eol-obstart))
|
2008-08-08 16:36:11 +02:00
|
|
|
RET_ERR("Couldn't parse private key.");
|
2007-07-16 20:26:31 +02:00
|
|
|
} else { /* If it's something else, try to base64-decode it */
|
|
|
|
int r;
|
2008-03-26 17:33:33 +01:00
|
|
|
tok->object_body = ALLOC(next-*s); /* really, this is too much RAM. */
|
2007-07-16 20:26:31 +02:00
|
|
|
r = base64_decode(tok->object_body, next-*s, *s, next-*s);
|
|
|
|
if (r<0)
|
2004-05-10 19:30:51 +02:00
|
|
|
RET_ERR("Malformed object: bad base64-encoded data");
|
2007-07-16 20:26:31 +02:00
|
|
|
tok->object_size = r;
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
2007-07-16 20:26:31 +02:00
|
|
|
*s = eol;
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
check_object:
|
2008-03-26 17:33:33 +01:00
|
|
|
tok = token_check_object(area, kwd, tok, o_syn);
|
2004-05-10 19:30:51 +02:00
|
|
|
|
2007-05-15 00:51:05 +02:00
|
|
|
done_tokenizing:
|
2004-05-10 19:30:51 +02:00
|
|
|
return tok;
|
2007-05-15 00:51:05 +02:00
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
#undef RET_ERR
|
2008-03-26 17:33:33 +01:00
|
|
|
#undef ALLOC
|
|
|
|
#undef ALLOC_ZERO
|
|
|
|
#undef STRDUP
|
|
|
|
#undef STRNDUP
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Read all tokens from a string between <b>start</b> and <b>end</b>, and add
|
2007-05-15 00:51:05 +02:00
|
|
|
* them to <b>out</b>. Parse according to the token rules in <b>table</b>.
|
2007-12-19 04:11:02 +01:00
|
|
|
* Caller must free tokens in <b>out</b>. If <b>end</b> is NULL, use the
|
|
|
|
* entire string.
|
2004-05-10 19:30:51 +02:00
|
|
|
*/
|
|
|
|
static int
|
2008-03-26 17:33:33 +01:00
|
|
|
tokenize_string(memarea_t *area,
|
|
|
|
const char *start, const char *end, smartlist_t *out,
|
2007-09-27 18:08:10 +02:00
|
|
|
token_rule_t *table, int flags)
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
|
|
|
const char **s;
|
|
|
|
directory_token_t *tok = NULL;
|
2012-10-12 18:22:13 +02:00
|
|
|
int counts[NIL_];
|
2007-04-16 06:18:21 +02:00
|
|
|
int i;
|
2007-09-26 18:19:44 +02:00
|
|
|
int first_nonannotation;
|
2007-09-27 18:08:10 +02:00
|
|
|
int prev_len = smartlist_len(out);
|
2008-03-26 17:56:34 +01:00
|
|
|
tor_assert(area);
|
2007-04-16 06:18:21 +02:00
|
|
|
|
2004-05-10 19:30:51 +02:00
|
|
|
s = &start;
|
2013-03-15 18:49:04 +01:00
|
|
|
if (!end) {
|
2005-09-08 21:10:36 +02:00
|
|
|
end = start+strlen(start);
|
2013-03-15 18:49:04 +01:00
|
|
|
} else {
|
|
|
|
/* it's only meaningful to check for nuls if we got an end-of-string ptr */
|
|
|
|
if (memchr(start, '\0', end-start)) {
|
|
|
|
log_warn(LD_DIR, "parse error: internal NUL character.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2012-10-12 18:22:13 +02:00
|
|
|
for (i = 0; i < NIL_; ++i)
|
2007-04-16 06:18:21 +02:00
|
|
|
counts[i] = 0;
|
2010-11-02 16:49:58 +01:00
|
|
|
|
|
|
|
SMARTLIST_FOREACH(out, const directory_token_t *, t, ++counts[t->tp]);
|
|
|
|
|
2012-10-12 18:22:13 +02:00
|
|
|
while (*s < end && (!tok || tok->tp != EOF_)) {
|
2008-03-26 17:33:33 +01:00
|
|
|
tok = get_next_token(area, s, end, table);
|
2012-10-12 18:22:13 +02:00
|
|
|
if (tok->tp == ERR_) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR, "parse error: %s", tok->error);
|
2009-09-28 16:37:01 +02:00
|
|
|
token_clear(tok);
|
2004-05-10 19:30:51 +02:00
|
|
|
return -1;
|
|
|
|
}
|
2007-04-16 06:18:21 +02:00
|
|
|
++counts[tok->tp];
|
2004-05-10 19:30:51 +02:00
|
|
|
smartlist_add(out, tok);
|
2007-07-10 22:08:18 +02:00
|
|
|
*s = eat_whitespace_eos(*s, end);
|
2004-05-10 19:30:51 +02:00
|
|
|
}
|
|
|
|
|
2007-09-27 18:08:10 +02:00
|
|
|
if (flags & TS_NOCHECK)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ((flags & TS_ANNOTATIONS_OK)) {
|
2007-09-26 18:19:44 +02:00
|
|
|
first_nonannotation = -1;
|
|
|
|
for (i = 0; i < smartlist_len(out); ++i) {
|
|
|
|
tok = smartlist_get(out, i);
|
|
|
|
if (tok->tp < MIN_ANNOTATION || tok->tp > MAX_ANNOTATION) {
|
|
|
|
first_nonannotation = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (first_nonannotation < 0) {
|
|
|
|
log_warn(LD_DIR, "parse error: item contains only annotations");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
for (i=first_nonannotation; i < smartlist_len(out); ++i) {
|
|
|
|
tok = smartlist_get(out, i);
|
|
|
|
if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
|
|
|
|
log_warn(LD_DIR, "parse error: Annotations mixed with keywords");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2007-09-27 18:08:10 +02:00
|
|
|
if ((flags & TS_NO_NEW_ANNOTATIONS)) {
|
|
|
|
if (first_nonannotation != prev_len) {
|
2009-05-27 23:55:51 +02:00
|
|
|
log_warn(LD_DIR, "parse error: Unexpected annotations.");
|
2007-09-27 18:08:10 +02:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2007-09-26 18:19:44 +02:00
|
|
|
} else {
|
|
|
|
for (i=0; i < smartlist_len(out); ++i) {
|
|
|
|
tok = smartlist_get(out, i);
|
|
|
|
if (tok->tp >= MIN_ANNOTATION && tok->tp <= MAX_ANNOTATION) {
|
|
|
|
log_warn(LD_DIR, "parse error: no annotations allowed.");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
first_nonannotation = 0;
|
|
|
|
}
|
2007-04-16 06:18:21 +02:00
|
|
|
for (i = 0; table[i].t; ++i) {
|
|
|
|
if (counts[table[i].v] < table[i].min_cnt) {
|
|
|
|
log_warn(LD_DIR, "Parse error: missing %s element.", table[i].t);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (counts[table[i].v] > table[i].max_cnt) {
|
|
|
|
log_warn(LD_DIR, "Parse error: too many %s elements.", table[i].t);
|
|
|
|
return -1;
|
|
|
|
}
|
2007-05-15 00:51:05 +02:00
|
|
|
if (table[i].pos & AT_START) {
|
|
|
|
if (smartlist_len(out) < 1 ||
|
2007-09-26 18:19:44 +02:00
|
|
|
(tok = smartlist_get(out, first_nonannotation))->tp != table[i].v) {
|
2007-05-15 00:51:05 +02:00
|
|
|
log_warn(LD_DIR, "Parse error: first item is not %s.", table[i].t);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (table[i].pos & AT_END) {
|
|
|
|
if (smartlist_len(out) < 1 ||
|
|
|
|
(tok = smartlist_get(out, smartlist_len(out)-1))->tp != table[i].v) {
|
|
|
|
log_warn(LD_DIR, "Parse error: last item is not %s.", table[i].t);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2007-04-16 06:18:21 +02:00
|
|
|
}
|
2004-05-10 19:30:51 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; return
|
|
|
|
* NULL if no such keyword is found.
|
|
|
|
*/
|
|
|
|
static directory_token_t *
|
2008-12-11 20:40:58 +01:00
|
|
|
find_opt_by_keyword(smartlist_t *s, directory_keyword keyword)
|
2004-05-10 19:30:51 +02:00
|
|
|
{
|
|
|
|
SMARTLIST_FOREACH(s, directory_token_t *, t, if (t->tp == keyword) return t);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-12-11 20:40:58 +01:00
|
|
|
/** Find the first token in <b>s</b> whose keyword is <b>keyword</b>; fail
|
|
|
|
* with an assert if no such keyword is found.
|
|
|
|
*/
|
|
|
|
static directory_token_t *
|
2012-10-12 18:22:13 +02:00
|
|
|
find_by_keyword_(smartlist_t *s, directory_keyword keyword,
|
2008-12-11 20:40:58 +01:00
|
|
|
const char *keyword_as_string)
|
|
|
|
{
|
|
|
|
directory_token_t *tok = find_opt_by_keyword(s, keyword);
|
|
|
|
if (PREDICT_UNLIKELY(!tok)) {
|
|
|
|
log_err(LD_BUG, "Missing %s [%d] in directory object that should have "
|
|
|
|
"been validated. Internal error.", keyword_as_string, (int)keyword);
|
|
|
|
tor_assert(tok);
|
|
|
|
}
|
|
|
|
return tok;
|
|
|
|
}
|
|
|
|
|
2012-06-05 00:50:13 +02:00
|
|
|
/** If there are any directory_token_t entries in <b>s</b> whose keyword is
|
|
|
|
* <b>k</b>, return a newly allocated smartlist_t containing all such entries,
|
|
|
|
* in the same order in which they occur in <b>s</b>. Otherwise return
|
|
|
|
* NULL. */
|
2011-11-08 22:51:30 +01:00
|
|
|
static smartlist_t *
|
|
|
|
find_all_by_keyword(smartlist_t *s, directory_keyword k)
|
|
|
|
{
|
|
|
|
smartlist_t *out = NULL;
|
|
|
|
SMARTLIST_FOREACH(s, directory_token_t *, t,
|
|
|
|
if (t->tp == k) {
|
|
|
|
if (!out)
|
2012-01-18 21:53:30 +01:00
|
|
|
out = smartlist_new();
|
2011-11-08 22:51:30 +01:00
|
|
|
smartlist_add(out, t);
|
|
|
|
});
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
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,
|
|
|
|
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) {
|
2006-09-30 00:51:47 +02:00
|
|
|
log_warn(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') {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(LD_DIR,
|
|
|
|
"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) {
|
2006-09-30 00:51:47 +02:00
|
|
|
log_warn(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) {
|
2006-02-13 11:33:00 +01:00
|
|
|
log_warn(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;
|
2010-02-27 22:33:22 +01:00
|
|
|
if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,
|
|
|
|
&start,&end)<0)
|
2009-09-16 23:01:01 +02:00
|
|
|
return -1;
|
|
|
|
|
2009-08-24 18:51:33 +02:00
|
|
|
if (alg == DIGEST_SHA1) {
|
|
|
|
if (crypto_digest(digest, start, end-start)) {
|
|
|
|
log_warn(LD_BUG,"couldn't compute digest");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (crypto_digest256(digest, start, end-start, alg)) {
|
|
|
|
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
|
2010-02-27 21:34:02 +01:00
|
|
|
router_get_hashes_impl(const char *s, size_t s_len, 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;
|
2010-02-27 22:33:22 +01:00
|
|
|
if (router_get_hash_impl_helper(s,s_len,start_str,end_str,end_c,
|
|
|
|
&start,&end)<0)
|
2009-09-16 23:01:01 +02:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (crypto_digest_all(digests, start, end-start)) {
|
|
|
|
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");
|
|
|
|
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");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
smartlist_add(md->family, tor_strdup(tok->args[i]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2004-09-27 08:00:43 +02:00
|
|
|
/** 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.
|
|
|
|
*/
|
2005-06-11 20:52:12 +02:00
|
|
|
int
|
|
|
|
tor_version_as_new_as(const char *platform, const char *cutoff)
|
|
|
|
{
|
2004-09-27 08:00:43 +02:00
|
|
|
tor_version_t cutoff_version, router_version;
|
2007-04-17 02:58:30 +02:00
|
|
|
char *s, *s2, *start;
|
2004-09-27 08:00:43 +02:00
|
|
|
char tmp[128];
|
|
|
|
|
2006-12-13 01:28:56 +01:00
|
|
|
tor_assert(platform);
|
|
|
|
|
2004-11-28 10:05:49 +01:00
|
|
|
if (tor_version_parse(cutoff, &cutoff_version)<0) {
|
2007-03-04 21:11:46 +01:00
|
|
|
log_warn(LD_BUG,"cutoff version '%s' unparseable.",cutoff);
|
2004-09-27 08:00:43 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2004-11-28 10:05:49 +01:00
|
|
|
if (strcmpstart(platform,"Tor ")) /* nonstandard Tor; be safe and say yes */
|
2004-09-27 08:00:43 +02:00
|
|
|
return 1;
|
|
|
|
|
|
|
|
start = (char *)eat_whitespace(platform+3);
|
|
|
|
if (!*start) return 0;
|
|
|
|
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 */
|
2004-09-27 08:00:43 +02:00
|
|
|
return 0;
|
|
|
|
strlcpy(tmp, start, s-start+1);
|
|
|
|
|
2004-11-28 10:05:49 +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);
|
2004-09-27 08:00:43 +02:00
|
|
|
return 1; /* be safe and say yes */
|
|
|
|
}
|
|
|
|
|
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;
|
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 { \
|
|
|
|
out->m = (int)strtol(cp, &eos, 10); \
|
|
|
|
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;
|
|
|
|
if (base16_decode(digest, hexlen/2, cp, hexlen))
|
|
|
|
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);
|
2004-07-13 21:16:49 +02:00
|
|
|
if ((i = a->major - b->major))
|
|
|
|
return i;
|
|
|
|
else if ((i = a->minor - b->minor))
|
|
|
|
return i;
|
|
|
|
else if ((i = a->micro - b->micro))
|
|
|
|
return i;
|
|
|
|
else if ((i = a->status - b->status))
|
|
|
|
return i;
|
|
|
|
else if ((i = a->patchlevel - b->patchlevel))
|
|
|
|
return i;
|
2007-04-17 02:58:30 +02:00
|
|
|
else if ((i = strcmp(a->status_tag, b->status_tag)))
|
|
|
|
return i;
|
2009-06-12 19:38:37 +02:00
|
|
|
else if ((i = a->svn_revision - b->svn_revision))
|
|
|
|
return i;
|
|
|
|
else if ((i = a->git_tag_len - b->git_tag_len))
|
|
|
|
return i;
|
|
|
|
else if (a->git_tag_len)
|
2011-05-11 22:32:30 +02:00
|
|
|
return fast_memcmp(a->git_tag, b->git_tag, a->git_tag_len);
|
2007-04-17 02:58:30 +02:00
|
|
|
else
|
2009-06-12 19:38:37 +02:00
|
|
|
return 0;
|
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) {
|
2011-04-18 22:25:48 +02:00
|
|
|
/* XXX023 If we are parsing this descriptor as a server, this
|
|
|
|
* 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);
|
2013-07-19 05:45:40 +02:00
|
|
|
if (parse_iso_time_(tok->args[0], &result->timestamp, strict_time_fmt) < 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);
|
2007-10-28 20:48:14 +01:00
|
|
|
note_crypto_pk_op(VERIFY_RTR);
|
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. */
|
|
|
|
crypto_pk_get_digest(result->pk, public_key_hash);
|
|
|
|
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;
|
2008-11-03 17:36:15 +01:00
|
|
|
dec = tor_malloc(len);
|
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;
|
|
|
|
}
|
2008-08-19 17:41:28 +02:00
|
|
|
dec = tor_malloc_zero(ipos_encrypted_size - CIPHER_IV_LEN - 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;
|
|
|
|
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;
|
|
|
|
size_t len;
|
|
|
|
char descriptor_cookie_tmp[REND_DESC_COOKIE_LEN+2];
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
len = strlen(tok->args[0]);
|
|
|
|
if (len < 1 || len > 19 ||
|
|
|
|
strspn(tok->args[0], REND_LEGAL_CLIENTNAME_CHARACTERS) != len) {
|
|
|
|
log_warn(LD_CONFIG, "Illegal client name: %s. (Length must be "
|
|
|
|
"between 1 and 19, and valid characters are "
|
|
|
|
"[A-Za-z0-9+-_].)", tok->args[0]);
|
|
|
|
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);
|
|
|
|
if (strlen(tok->args[0]) != REND_DESC_COOKIE_LEN_BASE64 + 2) {
|
|
|
|
log_warn(LD_REND, "Descriptor cookie has illegal length: %s",
|
|
|
|
escaped(tok->args[0]));
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
/* The size of descriptor_cookie_tmp needs to be REND_DESC_COOKIE_LEN+2,
|
|
|
|
* because a base64 encoding of length 24 does not fit into 16 bytes in all
|
|
|
|
* cases. */
|
2012-04-19 23:13:47 +02:00
|
|
|
if (base64_decode(descriptor_cookie_tmp, sizeof(descriptor_cookie_tmp),
|
|
|
|
tok->args[0], strlen(tok->args[0]))
|
|
|
|
!= REND_DESC_COOKIE_LEN) {
|
2008-08-08 16:36:11 +02:00
|
|
|
log_warn(LD_REND, "Descriptor cookie contains illegal characters: "
|
2012-04-19 05:02:09 +02:00
|
|
|
"%s", escaped(tok->args[0]));
|
2008-08-08 16:36:11 +02:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
memcpy(parsed_entry->descriptor_cookie, descriptor_cookie_tmp,
|
|
|
|
REND_DESC_COOKIE_LEN);
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|