Tor  0.4.7.0-alpha-dev
replaycache.c
Go to the documentation of this file.
1  /* Copyright (c) 2012-2021, The Tor Project, Inc. */
2  /* See LICENSE for licensing information */
3 
4 /**
5  * \file replaycache.c
6  *
7  * \brief Self-scrubbing replay cache for rendservice.c
8  *
9  * To prevent replay attacks, hidden services need to recognize INTRODUCE2
10  * cells that they've already seen, and drop them. If they didn't, then
11  * sending the same INTRODUCE2 cell over and over would force the hidden
12  * service to make a huge number of circuits to the same rendezvous
13  * point, aiding traffic analysis.
14  *
15  * (It's not that simple, actually. We only check for replays in the
16  * RSA-encrypted portion of the handshake, since the rest of the handshake is
17  * malleable.)
18  *
19  * This module is used from rendservice.c.
20  */
21 
22 #define REPLAYCACHE_PRIVATE
23 
24 #include "core/or/or.h"
26 
27 /** Free the replaycache r and all of its entries.
28  */
29 void
30 replaycache_free_(replaycache_t *r)
31 {
32  if (!r) {
33  log_info(LD_BUG, "replaycache_free() called on NULL");
34  return;
35  }
36 
37  if (r->digests_seen) digest256map_free(r->digests_seen, tor_free_);
38 
39  tor_free(r);
40 }
41 
42 /** Allocate a new, empty replay detection cache, where horizon is the time
43  * for entries to age out and interval is the time after which the cache
44  * should be scrubbed for old entries.
45  */
46 replaycache_t *
47 replaycache_new(time_t horizon, time_t interval)
48 {
49  replaycache_t *r = NULL;
50 
51  if (horizon < 0) {
52  log_info(LD_BUG, "replaycache_new() called with negative"
53  " horizon parameter");
54  goto err;
55  }
56 
57  if (interval < 0) {
58  log_info(LD_BUG, "replaycache_new() called with negative interval"
59  " parameter");
60  interval = 0;
61  }
62 
63  r = tor_malloc(sizeof(*r));
64  r->scrub_interval = interval;
65  r->scrubbed = 0;
66  r->horizon = horizon;
67  r->digests_seen = digest256map_new();
68 
69  err:
70  return r;
71 }
72 
73 /** See documentation for replaycache_add_and_test().
74  */
75 STATIC int
77  time_t present, replaycache_t *r, const void *data, size_t len,
78  time_t *elapsed)
79 {
80  int rv = 0;
81  uint8_t digest[DIGEST256_LEN];
82  time_t *access_time;
83 
84  /* sanity check */
85  if (present <= 0 || !r || !data || len == 0) {
86  log_info(LD_BUG, "replaycache_add_and_test_internal() called with stupid"
87  " parameters; please fix this.");
88  goto done;
89  }
90 
91  /* compute digest */
92  crypto_digest256((char *)digest, (const char *)data, len, DIGEST_SHA256);
93 
94  /* check map */
95  access_time = digest256map_get(r->digests_seen, digest);
96 
97  /* seen before? */
98  if (access_time != NULL) {
99  /*
100  * If it's far enough in the past, no hit. If the horizon is zero, we
101  * never expire.
102  */
103  if (*access_time >= present - r->horizon || r->horizon == 0) {
104  /* replay cache hit, return 1 */
105  rv = 1;
106  /* If we want to output an elapsed time, do so */
107  if (elapsed) {
108  if (present >= *access_time) {
109  *elapsed = present - *access_time;
110  } else {
111  /* We shouldn't really be seeing hits from the future, but... */
112  *elapsed = 0;
113  }
114  }
115  }
116  /*
117  * If it's ahead of the cached time, update
118  */
119  if (*access_time < present) {
120  *access_time = present;
121  }
122  } else {
123  /* No, so no hit and update the digest map with the current time */
124  access_time = tor_malloc(sizeof(*access_time));
125  *access_time = present;
126  digest256map_set(r->digests_seen, digest, access_time);
127  }
128 
129  /* now scrub the cache if it's time */
131 
132  done:
133  return rv;
134 }
135 
136 /** See documentation for replaycache_scrub_if_needed().
137  */
138 STATIC void
139 replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
140 {
141  digest256map_iter_t *itr = NULL;
142  const uint8_t *digest;
143  void *valp;
144  time_t *access_time;
145 
146  /* sanity check */
147  if (!r || !(r->digests_seen)) {
148  log_info(LD_BUG, "replaycache_scrub_if_needed_internal() called with"
149  " stupid parameters; please fix this.");
150  return;
151  }
152 
153  /* scrub time yet? (scrubbed == 0 indicates never scrubbed before) */
154  if (present - r->scrubbed < r->scrub_interval && r->scrubbed > 0) return;
155 
156  /* if we're never expiring, don't bother scrubbing */
157  if (r->horizon == 0) return;
158 
159  /* okay, scrub time */
160  itr = digest256map_iter_init(r->digests_seen);
161  while (!digest256map_iter_done(itr)) {
162  digest256map_iter_get(itr, &digest, &valp);
163  access_time = (time_t *)valp;
164  /* aged out yet? */
165  if (*access_time < present - r->horizon) {
166  /* Advance the iterator and remove this one */
167  itr = digest256map_iter_next_rmv(r->digests_seen, itr);
168  /* Free the value removed */
169  tor_free(access_time);
170  } else {
171  /* Just advance the iterator */
172  itr = digest256map_iter_next(r->digests_seen, itr);
173  }
174  }
175 
176  /* update scrubbed timestamp */
177  if (present > r->scrubbed) r->scrubbed = present;
178 }
179 
180 /** Test the buffer of length len point to by data against the replay cache r;
181  * the digest of the buffer will be added to the cache at the current time,
182  * and the function will return 1 if it was already seen within the cache's
183  * horizon, or 0 otherwise.
184  */
185 int
186 replaycache_add_and_test(replaycache_t *r, const void *data, size_t len)
187 {
188  return replaycache_add_and_test_internal(time(NULL), r, data, len, NULL);
189 }
190 
191 /** Like replaycache_add_and_test(), but if it's a hit also return the time
192  * elapsed since this digest was last seen.
193  */
194 int
196  replaycache_t *r, const void *data, size_t len, time_t *elapsed)
197 {
198  return replaycache_add_and_test_internal(time(NULL), r, data, len, elapsed);
199 }
200 
201 /** Scrub aged entries out of r if sufficiently long has elapsed since r was
202  * last scrubbed.
203  */
204 void
206 {
208 }
209 
int crypto_digest256(char *digest, const char *m, size_t len, digest_algorithm_t algorithm)
#define DIGEST256_LEN
Definition: digest_sizes.h:23
#define LD_BUG
Definition: log.h:86
void tor_free_(void *mem)
Definition: malloc.c:227
#define tor_free(p)
Definition: malloc.h:52
Master header file for Tor-specific functionality.
replaycache_t * replaycache_new(time_t horizon, time_t interval)
Definition: replaycache.c:47
void replaycache_free_(replaycache_t *r)
Definition: replaycache.c:30
void replaycache_scrub_if_needed(replaycache_t *r)
Definition: replaycache.c:205
int replaycache_add_test_and_elapsed(replaycache_t *r, const void *data, size_t len, time_t *elapsed)
Definition: replaycache.c:195
STATIC int replaycache_add_and_test_internal(time_t present, replaycache_t *r, const void *data, size_t len, time_t *elapsed)
Definition: replaycache.c:76
int replaycache_add_and_test(replaycache_t *r, const void *data, size_t len)
Definition: replaycache.c:186
STATIC void replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
Definition: replaycache.c:139
Header file for replaycache.c.
#define STATIC
Definition: testsupport.h:32