tor
master
|
Code to parse and validate router descriptors, consenus directories, and similar objects. More...
#include "or.h"
#include "circuitstats.h"
#include "config.h"
#include "crypto_util.h"
#include "dirauth/shared_random.h"
#include "dirserv.h"
#include "entrynodes.h"
#include "memarea.h"
#include "microdesc.h"
#include "networkstatus.h"
#include "parsecommon.h"
#include "policies.h"
#include "protover.h"
#include "rendcommon.h"
#include "rephist.h"
#include "router.h"
#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "sandbox.h"
#include "shared_random_client.h"
#include "torcert.h"
#include "voting_schedule.h"
#include <math.h>
#include "dirauth/dirvote.h"
Macros | |
#define | ROUTERPARSE_PRIVATE |
#define | CERTIFICATE_MEMBERS |
#define | CST_NO_CHECK_OBJTYPE (1<<0) |
#define | DUMP_AREA(a, name) STMT_NIL |
#define | DESC_DUMP_DATADIR_SUBDIR "unparseable-descs" |
#define | DESC_DUMP_BASE_FILENAME "unparseable-desc" |
#define | BEGIN_END_OVERHEAD_LEN 64 |
#define | MAX_CERT_SIZE (128*1024) |
#define | CHECK_LENGTH() |
#define | NEXT_LINE() |
#define | NUMBER(m) |
#define | DOT() |
#define | CMP(field) |
Functions | |
STATIC void | dump_desc_fifo_cleanup (void) |
MOCK_IMPL (STATIC dumped_desc_t *, dump_desc_populate_one_file,(const char *dirname, const char *f)) | |
STATIC void | dump_desc_populate_fifo_from_directory (const char *dirname) |
MOCK_IMPL (STATIC void, dump_desc,(const char *desc, const char *type)) | |
int | router_get_dir_hash (const char *s, char *digest) |
int | router_get_router_hash (const char *s, size_t s_len, char *digest) |
int | router_get_networkstatus_v3_signed_boundaries (const char *s, const char **start_out, const char **end_out) |
int | router_get_networkstatus_v3_sha3_as_signed (uint8_t *digest_out, const char *s) |
int | router_get_networkstatus_v3_hashes (const char *s, common_digests_t *digests) |
int | router_get_extrainfo_hash (const char *s, size_t s_len, char *digest) |
char * | router_get_dirobj_signature (const char *digest, size_t digest_len, const crypto_pk_t *private_key) |
int | router_append_dirobj_signature (char *buf, size_t buf_len, const char *digest, size_t digest_len, crypto_pk_t *private_key) |
version_status_t | tor_version_is_obsolete (const char *myversion, const char *versionlist) |
MOCK_IMPL (STATIC int, signed_digest_equals,(const uint8_t *d1, const uint8_t *d2, size_t len)) | |
int | router_parse_list_from_string (const char **s, const char *eos, smartlist_t *dest, saved_location_t saved_location, int want_extrainfo, int allow_annotations, const char *prepend_annotations, smartlist_t *invalid_digests_out) |
void | dump_distinct_digest_count (int severity) |
routerinfo_t * | router_parse_entry_from_string (const char *s, const char *end, int cache_copy, int allow_annotations, const char *prepend_annotations, int *can_dl_again_out) |
extrainfo_t * | extrainfo_parse_entry_from_string (const char *s, const char *end, int cache_copy, struct digest_ri_map_t *routermap, int *can_dl_again_out) |
authority_cert_t * | authority_cert_parse_from_string (const char *s, const char **end_of_string) |
STATIC int | routerstatus_parse_guardfraction (const char *guardfraction_str, networkstatus_t *vote, vote_routerstatus_t *vote_rs, routerstatus_t *rs) |
STATIC void | summarize_protover_flags (protover_summary_flags_t *out, const char *protocols, const char *version) |
STATIC routerstatus_t * | routerstatus_parse_entry_from_string (memarea_t *area, const char **s, smartlist_t *tokens, networkstatus_t *vote, vote_routerstatus_t *vote_rs, int consensus_method, consensus_flavor_t flav) |
int | compare_vote_routerstatus_entries (const void **_a, const void **_b) |
int | networkstatus_verify_bw_weights (networkstatus_t *ns, int consensus_method) |
networkstatus_t * | networkstatus_parse_vote_from_string (const char *s, const char **eos_out, networkstatus_type_t ns_type) |
ns_detached_signatures_t * | networkstatus_parse_detached_signatures (const char *s, const char *eos) |
MOCK_IMPL (addr_policy_t *, router_parse_addr_policy_item_from_string,(const char *s, int assume_action, int *malformed_list)) | |
void | assert_addr_policy_ok (smartlist_t *lst) |
MOCK_IMPL (STATIC int, router_compute_hash_final,(char *digest, const char *start, size_t len, digest_algorithm_t alg)) | |
smartlist_t * | microdescs_parse_from_string (const char *s, const char *eos, int allow_annotations, saved_location_t where, smartlist_t *invalid_digests_out) |
int | tor_version_parse_platform (const char *platform, tor_version_t *router_version, int strict) |
int | tor_version_as_new_as (const char *platform, const char *cutoff) |
int | tor_version_parse (const char *s, tor_version_t *out) |
int | tor_version_compare (tor_version_t *a, tor_version_t *b) |
int | tor_version_same_series (tor_version_t *a, tor_version_t *b) |
void | sort_version_list (smartlist_t *versions, int remove_duplicates) |
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, const char **next_out, const char *desc, int as_hsdir) |
int | rend_decrypt_introduction_points (char **ipos_decrypted, size_t *ipos_decrypted_size, const char *descriptor_cookie, const char *ipos_encrypted, size_t ipos_encrypted_size) |
int | rend_parse_introduction_points (rend_service_descriptor_t *parsed, const char *intro_points_encoded, size_t intro_points_encoded_size) |
int | rend_parse_client_keys (strmap_t *parsed_clients, const char *ckstr) |
void | routerparse_init (void) |
void | routerparse_free_all (void) |
Variables | |
STATIC smartlist_t * | descs_dumped = NULL |
STATIC uint64_t | len_descs_dumped = 0 |
Code to parse and validate router descriptors, consenus directories, and similar objects.
The objects parsed by this module use a common text-based metaformat, documented in dir-spec.txt in torspec.git. This module is itself divided into two major kinds of function: code to handle the metaformat, and code to convert from particular instances of the metaformat into the objects that Tor uses.
The generic parsing code works by calling a table-based tokenizer on the input string. Each token corresponds to a single line with a token, plus optional arguments on that line, plus an optional base-64 encoded object after that line. Each token has a definition in a table of token_rule_t entries that describes how many arguments it can take, whether it takes an object, how many times it may appear, whether it must appear first, and so on.
The tokenizer function tokenize_string() converts its string input into a smartlist full of instances of directory_token_t, according to a provided table of token_rule_t.
The generic parts of this module additionally include functions for finding the start and end of signed information inside a signed object, and computing the digest that will be signed.
There are also functions for saving objects to disk that have caused parsing to fail.
The specific parts of this module describe conversions between particular lists of directory_token_t and particular objects. The kinds of objects that can be parsed here are:
For no terribly good reason, the functions to generate signatures on the above directory objects are also in this module.
#define CERTIFICATE_MEMBERS |
List of tokens common to V3 authority certificates and V3 consensuses.
#define CHECK_LENGTH | ( | ) |
#define CMP | ( | field | ) |
#define DOT | ( | ) |
#define NEXT_LINE | ( | ) |
#define NUMBER | ( | m | ) |
void assert_addr_policy_ok | ( | smartlist_t * | lst | ) |
Log and exit if t is malformed
authority_cert_t* authority_cert_parse_from_string | ( | const char * | s, |
const char ** | end_of_string | ||
) |
Parse a key certificate from s; point end-of-string to the first character after the certificate.
Reject any certificate at least this big; it is probably an overflow, an attack, a bug, or some other nonsense.
STATIC void dump_desc_fifo_cleanup | ( | void | ) |
Clean up on exit; just memory, leave the dumps behind
STATIC void dump_desc_populate_fifo_from_directory | ( | const char * | dirname | ) |
Scan the contents of the directory, and update FIFO/counters; this will consistency-check descriptor dump filenames against hashes of descriptor dump file content, and remove any inconsistent/unreadable dumps, and then reconstruct the dump FIFO as closely as possible for the last time the tor process shut down. If a previous dump was repeated more than once and moved ahead in the FIFO, the mtime will not have been updated and the reconstructed order will be wrong, but will always be a permutation of the original.
void dump_distinct_digest_count | ( | int | severity | ) |
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 in toto, this will tell us if we're doing too much multiple-verification.
extrainfo_t* extrainfo_parse_entry_from_string | ( | const char * | s, |
const char * | end, | ||
int | cache_copy, | ||
struct digest_ri_map_t * | routermap, | ||
int * | can_dl_again_out | ||
) |
Parse a single extrainfo entry from the string s, ending at end. (If end is NULL, parse up to the end of s.) If cache_copy is true, make a copy of the extra-info document in the cache_info fields of the result. If routermap is provided, use it as a map from router identity to routerinfo_t when looking up signing keys.
If can_dl_again_out is provided, set *can_dl_again_out 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.)
smartlist_t* microdescs_parse_from_string | ( | const char * | s, |
const char * | eos, | ||
int | allow_annotations, | ||
saved_location_t | where, | ||
smartlist_t * | invalid_digests_out | ||
) |
Parse as many microdescriptors as are found from the string starting at s and ending at eos. If allow_annotations is set, read any annotations we recognize and ignore ones we don't.
If saved_location isn't SAVED_IN_CACHE, make a local copy of each descriptor in the body field of each microdesc_t.
Return all newly parsed microdescriptors in a newly allocated smartlist_t. If invalid_disgests_out is provided, add a SHA256 microdesc digest to it for every microdesc that we found to be badly formed. (This may cause duplicates)
MOCK_IMPL | ( | STATIC dumped_desc_t * | , |
dump_desc_populate_one_file | , | ||
(const char *dirname, const char *f) | |||
) |
Handle one file for dump_desc_populate_fifo_from_directory(); make sure the filename is sensibly formed and matches the file content, and either return a dumped_desc_t for it or remove the file and return NULL.
MOCK_IMPL | ( | STATIC | void, |
dump_desc | , | ||
(const char *desc, const char *type) | |||
) |
For debugging purposes, dump unparseable descriptor *desc of type *type 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.
MOCK_IMPL | ( | addr_policy_t * | , |
router_parse_addr_policy_item_from_string | , | ||
(const char *s, int assume_action, int *malformed_list) | |||
) |
Parse the addr policy in the string s and return it. If assume_action is nonnegative, then insert its action (ADDR_POLICY_ACCEPT or ADDR_POLICY_REJECT) for items that specify no action.
Returns NULL on policy errors.
Set *malformed_list 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.
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.
MOCK_IMPL | ( | STATIC | int, |
router_compute_hash_final | , | ||
(char *digest, const char *start, size_t len, digest_algorithm_t alg) | |||
) |
Compute the digest of the len-byte directory object at start, using alg. Store the result in digest, which must be long enough to hold it.
ns_detached_signatures_t* networkstatus_parse_detached_signatures | ( | const char * | s, |
const char * | eos | ||
) |
Parse a detached v3 networkstatus signature document between s and eos and return the result. Return -1 on failure.
networkstatus_t* networkstatus_parse_vote_from_string | ( | const char * | s, |
const char ** | eos_out, | ||
networkstatus_type_t | ns_type | ||
) |
Parse a v3 networkstatus vote, opinion, or consensus (depending on ns_type), from s, and return the result. Return NULL on failure.
int networkstatus_verify_bw_weights | ( | networkstatus_t * | ns, |
int | consensus_method | ||
) |
Verify the bandwidth weights of a network status document
int rend_decrypt_introduction_points | ( | char ** | ipos_decrypted, |
size_t * | ipos_decrypted_size, | ||
const char * | descriptor_cookie, | ||
const char * | ipos_encrypted, | ||
size_t | ipos_encrypted_size | ||
) |
Decrypt the encrypted introduction points in ipos_encrypted of length ipos_encrypted_size using descriptor_cookie and write the result to a newly allocated string that is pointed to by ipos_decrypted and its length to ipos_decrypted_size. Return 0 if decryption was successful and -1 otherwise.
int rend_parse_client_keys | ( | strmap_t * | parsed_clients, |
const char * | ckstr | ||
) |
Parse the content of a client_key file in ckstr and add rend_authorized_client_t's for each parsed client to parsed_clients. Return the number of parsed clients as result or -1 for failure.
int rend_parse_introduction_points | ( | rend_service_descriptor_t * | parsed, |
const char * | intro_points_encoded, | ||
size_t | intro_points_encoded_size | ||
) |
Parse the encoded introduction points in intro_points_encoded of length intro_points_encoded_size and write the result to the descriptor in parsed; return the number of successfully parsed introduction points or -1 in case of a failure.
Function may only be invoked once.
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, | ||
const char ** | next_out, | ||
const char * | desc, | ||
int | as_hsdir | ||
) |
Parse and validate the ASCII-encoded v2 descriptor in desc, write the parsed descriptor to the newly allocated *parsed_out, the binary descriptor ID of length DIGEST_LEN to desc_id_out, the encrypted introduction points to the newly allocated *intro_points_encrypted_out, their encrypted size to *intro_points_encrypted_size_out, the size of the encoded descriptor to *encoded_size_out, and a pointer to the possibly next descriptor to *next_out; return 0 for success (including validation) and -1 for failure.
If as_hsdir is 1, we're parsing this as an HSDir, and we should be strict about time formats.
int router_append_dirobj_signature | ( | char * | buf, |
size_t | buf_len, | ||
const char * | digest, | ||
size_t | digest_len, | ||
crypto_pk_t * | private_key | ||
) |
Helper: used to generate signatures for routers, directories and network-status objects. Given a digest in digest and a secret private_key, generate a PKCS1-padded signature, BASE64-encode it, surround it with --—BEGIN/END--— pairs, and write it to the buf_len-byte buffer at buf. Return 0 on success, -1 on failure.
int router_get_dir_hash | ( | const char * | s, |
char * | digest | ||
) |
Set digest to the SHA-1 digest of the hash of the directory in s. Return 0 on success, -1 on failure.
char* router_get_dirobj_signature | ( | const char * | digest, |
size_t | digest_len, | ||
const crypto_pk_t * | private_key | ||
) |
Helper: used to generate signatures for routers, directories and network-status objects. Given a digest_len-byte digest in digest and a secret private_key, 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.
int router_get_extrainfo_hash | ( | const char * | s, |
size_t | s_len, | ||
char * | digest | ||
) |
Set digest to the SHA-1 digest of the hash of the s_len-byte extrainfo string at s. Return 0 on success, -1 on failure.
int router_get_networkstatus_v3_hashes | ( | const char * | s, |
common_digests_t * | digests | ||
) |
Set digests to all the digests of the consensus document in s
int router_get_networkstatus_v3_sha3_as_signed | ( | uint8_t * | digest_out, |
const char * | s | ||
) |
Set digest_out to the SHA3-256 digest of the signed portion of the networkstatus vote in s – or of the entirety of s if no signed portion can be identified. Return 0 on success, -1 on failure.
int router_get_networkstatus_v3_signed_boundaries | ( | const char * | s, |
const char ** | start_out, | ||
const char ** | end_out | ||
) |
Try to find the start and end of the signed portion of a networkstatus document in s. On success, set start_out to the first character of the document, and end_out to a position one after the final character of the signed document, and return 0. On failure, return -1.
int router_get_router_hash | ( | const char * | s, |
size_t | s_len, | ||
char * | digest | ||
) |
Set digest to the SHA-1 digest of the hash of the first router in s. Return 0 on success, -1 on failure.
routerinfo_t* router_parse_entry_from_string | ( | const char * | s, |
const char * | end, | ||
int | cache_copy, | ||
int | allow_annotations, | ||
const char * | prepend_annotations, | ||
int * | can_dl_again_out | ||
) |
Helper function: reads a single router entry from *s ... *end. Mallocs a new router and returns it if all goes well, else returns NULL. If cache_copy is true, duplicate the contents of s through end into the signed_descriptor_body of the resulting routerinfo_t.
If end is NULL, s must be properly NUL-terminated.
If allow_annotations, it's okay to encounter annotations in s before the router; if it's false, reject the router if it's annotated. If prepend_annotations 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.
If can_dl_again_out is provided, set *can_dl_again_out 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.)
int router_parse_list_from_string | ( | const char ** | s, |
const char * | eos, | ||
smartlist_t * | dest, | ||
saved_location_t | saved_location, | ||
int | want_extrainfo, | ||
int | allow_annotations, | ||
const char * | prepend_annotations, | ||
smartlist_t * | invalid_digests_out | ||
) |
Given a string *s containing a concatenated sequence of router descriptors (or extra-info documents if is_extrainfo is set), parses them and stores the result in dest. 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.
If saved_location 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.
Returns 0 on success and -1 on failure. Adds a digest to invalid_digests_out for every entry that was unparseable or invalid. (This may cause duplicate entries.)
void routerparse_free_all | ( | void | ) |
Clean up all data structures used by routerparse.c at exit
void routerparse_init | ( | void | ) |
Called on startup; right now we just handle scanning the unparseable descriptor dumps, but hang anything else we might need to do in the future here as well.
STATIC routerstatus_t* routerstatus_parse_entry_from_string | ( | memarea_t * | area, |
const char ** | s, | ||
smartlist_t * | tokens, | ||
networkstatus_t * | vote, | ||
vote_routerstatus_t * | vote_rs, | ||
int | consensus_method, | ||
consensus_flavor_t | flav | ||
) |
Given a string at *s, containing a routerstatus object, and an empty smartlist at tokens, parse and return the first router status object in the string, and advance *s to just after the end of the router status. Return NULL and advance *s on error.
If vote and vote_rs are provided, don't allocate a fresh routerstatus but use vote_rs instead.
If consensus_method is nonzero, this routerstatus is part of a consensus, and we should parse it according to the method used to make that consensus.
Parse according to the syntax used by the consensus flavor flav.
STATIC int routerstatus_parse_guardfraction | ( | const char * | guardfraction_str, |
networkstatus_t * | vote, | ||
vote_routerstatus_t * | vote_rs, | ||
routerstatus_t * | rs | ||
) |
Parse the GuardFraction string from a consensus or vote.
If vote or vote_rs 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().
void sort_version_list | ( | smartlist_t * | versions, |
int | remove_duplicates | ||
) |
Sort a list of string-representations of versions in ascending order.
STATIC void summarize_protover_flags | ( | protover_summary_flags_t * | out, |
const char * | protocols, | ||
const char * | version | ||
) |
Summarize the protocols listed in protocols into out, falling back or correcting them based on version as appropriate.
int tor_version_as_new_as | ( | const char * | platform, |
const char * | cutoff | ||
) |
Parse the Tor version of the platform string platform, and compare it to the version in cutoff. Return 1 if the router is at least as new as the cutoff, else return 0.
int tor_version_compare | ( | tor_version_t * | a, |
tor_version_t * | b | ||
) |
Compare two tor versions; Return <0 if a < b; 0 if a ==b, >0 if a > b.
version_status_t tor_version_is_obsolete | ( | const char * | myversion, |
const char * | versionlist | ||
) |
Return VS_RECOMMENDED if myversion is contained in versionlist. Else, return VS_EMPTY if versionlist has no entries. Else, return VS_OLD if every member of versionlist is newer than myversion. Else, return VS_NEW_IN_SERIES if there is at least one member of versionlist in the same series (major.minor.micro) as myversion, but no such member is newer than myversion.. Else, return VS_NEW if every member of versionlist is older than myversion. Else, return VS_UNRECOMMENDED.
(versionlist is a comma-separated list of version strings, optionally prefixed with "Tor". Versions that can't be parsed are ignored.)
int tor_version_parse | ( | const char * | s, |
tor_version_t * | out | ||
) |
Parse a tor version from s, and store the result in out. Return 0 on success, -1 on failure.
int tor_version_parse_platform | ( | const char * | platform, |
tor_version_t * | router_version, | ||
int | strict | ||
) |
Extract a Tor version from a platform line from a router descriptor, and place the result in router_version.
Return 1 on success, -1 on parsing failure, and 0 if the platform line does not indicate some version of Tor.
If strict is non-zero, finding any weird version components (like negative numbers) counts as a parsing failure.
int tor_version_same_series | ( | tor_version_t * | a, |
tor_version_t * | b | ||
) |
Return true iff versions a and b belong to the same series.
STATIC smartlist_t* descs_dumped = NULL |
List of dumped descriptors for FIFO cleanup purposes
STATIC uint64_t len_descs_dumped = 0 |
Total size of dumped descriptors for FIFO cleanup