diff options
Diffstat (limited to 'src/or/consdiff.c')
-rw-r--r-- | src/or/consdiff.c | 116 |
1 files changed, 107 insertions, 9 deletions
diff --git a/src/or/consdiff.c b/src/or/consdiff.c index c0fe979b60..1baa11897c 100644 --- a/src/or/consdiff.c +++ b/src/or/consdiff.c @@ -65,17 +65,35 @@ line_str_eq(const cdline_t *a, const char *b) return lines_eq(a, &bline); } -/** Add a cdline_t to <b>lst</b> holding as its contents the nul-terminated - * string s. Use the provided memory area for storage. */ -STATIC void -smartlist_add_linecpy(smartlist_t *lst, memarea_t *area, const char *s) +/** Return true iff a begins with the same contents as the nul-terminated + * string b. */ +static int +line_starts_with_str(const cdline_t *a, const char *b) +{ + const size_t len = strlen(b); + tor_assert(len <= UINT32_MAX); + return a->len >= len && fast_memeq(a->s, b, len); +} + +/** Return a new cdline_t holding as its contents the nul-terminated + * string s. Use the provided memory area for storage. */ +static cdline_t * +cdline_linecpy(memarea_t *area, const char *s) { size_t len = strlen(s); const char *ss = memarea_memdup(area, s, len); cdline_t *line = memarea_alloc(area, sizeof(cdline_t)); line->s = ss; line->len = (uint32_t)len; - smartlist_add(lst, line); + return line; +} + +/** Add a cdline_t to <b>lst</b> holding as its contents the nul-terminated + * string s. Use the provided memory area for storage. */ +STATIC void +smartlist_add_linecpy(smartlist_t *lst, memarea_t *area, const char *s) +{ + smartlist_add(lst, cdline_linecpy(area, s)); } /** Compute the digest of <b>cons</b>, and store the result in @@ -91,6 +109,18 @@ consensus_compute_digest,(const char *cons, return r; } +/** Compute the digest-as-signed of <b>cons</b>, and store the result in + * <b>digest_out</b>. Return 0 on success, -1 on failure. */ +/* This is a separate, mockable function so that we can override it when + * fuzzing. */ +MOCK_IMPL(STATIC int, +consensus_compute_digest_as_signed,(const char *cons, + consensus_digest_t *digest_out)) +{ + return router_get_networkstatus_v3_sha3_as_signed(digest_out->sha3_256, + cons); +} + /** Return true iff <b>d1</b> and <b>d2</b> contain the same digest */ /* This is a separate, mockable function so that we can override it when * fuzzing. */ @@ -533,6 +563,42 @@ find_next_router_line(const smartlist_t *cons, return 0; } +/** Line-prefix indicating the beginning of the signatures section that we + * intend to delete. */ +#define START_OF_SIGNATURES_SECTION "directory-signature " + +/** Pre-process a consensus in <b>cons</b> (represented as a list of cdline_t) + * to remove the signatures from it. If the footer is removed, return a + * cdline_t containing a delete command to delete the footer, allocated in + * <b>area</>. If no footer is removed, return NULL. + * + * We remove the signatures here because they are not themselves signed, and + * as such there might be different encodings for them. + */ +static cdline_t * +preprocess_consensus(memarea_t *area, + smartlist_t *cons) +{ + int idx; + int dirsig_idx = -1; + for (idx = 0; idx < smartlist_len(cons); ++idx) { + cdline_t *line = smartlist_get(cons, idx); + if (line_starts_with_str(line, START_OF_SIGNATURES_SECTION)) { + dirsig_idx = idx; + break; + } + } + if (dirsig_idx >= 0) { + char buf[64]; + while (smartlist_len(cons) > dirsig_idx) + smartlist_del(cons, dirsig_idx); + tor_snprintf(buf, sizeof(buf), "%d,$d", dirsig_idx+1); + return cdline_linecpy(area, buf); + } else { + return NULL; + } +} + /** Generate an ed diff as a smartlist from two consensuses, also given as * smartlists. Will return NULL if the diff could not be generated, which can * happen if any lines the script had to add matched "." or if the routers @@ -554,13 +620,22 @@ find_next_router_line(const smartlist_t *cons, * calc_changes(cons1_sl, cons2_sl, changed1, changed2); */ STATIC smartlist_t * -gen_ed_diff(const smartlist_t *cons1, const smartlist_t *cons2, +gen_ed_diff(const smartlist_t *cons1_orig, const smartlist_t *cons2, memarea_t *area) { + smartlist_t *cons1 = smartlist_new(); + smartlist_add_all(cons1, cons1_orig); + cdline_t *remove_trailer = preprocess_consensus(area, cons1); + int len1 = smartlist_len(cons1); int len2 = smartlist_len(cons2); smartlist_t *result = smartlist_new(); + if (remove_trailer) { + /* There's a delete-the-trailer line at the end, so add it here. */ + smartlist_add(result, remove_trailer); + } + /* Initialize the changed bitarrays to zero, so that calc_changes only needs * to set the ones that matter and leave the rest untouched. */ @@ -734,6 +809,7 @@ gen_ed_diff(const smartlist_t *cons1, const smartlist_t *cons2, } } + smartlist_free(cons1); bitarray_free(changed1); bitarray_free(changed2); @@ -741,6 +817,7 @@ gen_ed_diff(const smartlist_t *cons1, const smartlist_t *cons2, error_cleanup: + smartlist_free(cons1); bitarray_free(changed1); bitarray_free(changed2); @@ -799,6 +876,7 @@ apply_ed_diff(const smartlist_t *cons1, const smartlist_t *diff, const char *ptr = diff_line; int start = 0, end = 0; int had_range = 0; + int end_was_eof = 0; if (get_linenum(&ptr, &start) < 0) { log_warn(LD_CONSDIFF, "Could not apply consensus diff because " "an ed command was missing a line number."); @@ -808,7 +886,11 @@ apply_ed_diff(const smartlist_t *cons1, const smartlist_t *diff, /* Two-item range */ had_range = 1; ++ptr; - if (get_linenum(&ptr, &end) < 0) { + if (*ptr == '$') { + end_was_eof = 1; + end = smartlist_len(cons1); + ++ptr; + } else if (get_linenum(&ptr, &end) < 0) { log_warn(LD_CONSDIFF, "Could not apply consensus diff because " "an ed command was missing a range end line number."); goto error_cleanup; @@ -855,6 +937,13 @@ apply_ed_diff(const smartlist_t *cons1, const smartlist_t *diff, goto error_cleanup; } + /** $ is not allowed with non-d actions. */ + if (end_was_eof && action != 'd') { + log_warn(LD_CONSDIFF, "Could not apply consensus diff because " + "it wanted to use $ with a command other than delete"); + goto error_cleanup; + } + /* 'a' commands are not allowed to have ranges. */ if (had_range && action == 'a') { log_warn(LD_CONSDIFF, "Could not apply consensus diff because " @@ -1250,7 +1339,7 @@ consensus_diff_generate(const char *cons1, int r1, r2; char *result = NULL; - r1 = consensus_compute_digest(cons1, &d1); + r1 = consensus_compute_digest_as_signed(cons1, &d1); r2 = consensus_compute_digest(cons2, &d2); if (BUG(r1 < 0 || r2 < 0)) return NULL; // LCOV_EXCL_LINE @@ -1291,7 +1380,7 @@ consensus_diff_apply(const char *consensus, char *result = NULL; memarea_t *area = memarea_new(); - r1 = consensus_compute_digest(consensus, &d1); + r1 = consensus_compute_digest_as_signed(consensus, &d1); if (BUG(r1 < 0)) return NULL; // LCOV_EXCL_LINE @@ -1312,3 +1401,12 @@ consensus_diff_apply(const char *consensus, return result; } +/** Return true iff, based on its header, <b>document</b> is likely + * to be a consensus diff. */ +int +looks_like_a_consensus_diff(const char *document, size_t len) +{ + return (len >= strlen(ns_diff_version) && + fast_memeq(document, ns_diff_version, strlen(ns_diff_version))); +} + |