Line data Source code
1 : /* Copyright (c) 2019-2021, The Tor Project, Inc. */ 2 : /* See LICENSE for licensing information */ 3 : 4 : /** 5 : * \file hs_dos.c 6 : * \brief Implement denial of service mitigation for the onion service 7 : * subsystem. 8 : * 9 : * This module defenses: 10 : * 11 : * - Introduction Rate Limiting: If enabled by the consensus, an introduction 12 : * point will rate limit client introduction towards the service (INTRODUCE2 13 : * cells). It uses a token bucket model with a rate and burst per second. 14 : * 15 : * Proposal 305 will expand this module by allowing an operator to define 16 : * these values into the ESTABLISH_INTRO cell. Not yet implemented. 17 : **/ 18 : 19 : #define HS_DOS_PRIVATE 20 : 21 : #include "core/or/or.h" 22 : #include "app/config/config.h" 23 : 24 : #include "core/or/circuitlist.h" 25 : 26 : #include "feature/hs/hs_circuitmap.h" 27 : #include "feature/nodelist/networkstatus.h" 28 : #include "feature/relay/routermode.h" 29 : 30 : #include "lib/evloop/token_bucket.h" 31 : 32 : #include "feature/hs/hs_dos.h" 33 : 34 : /** Default value of the allowed INTRODUCE2 cell rate per second. Above that 35 : * value per second, the introduction is denied. */ 36 : #define HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC 25 37 : 38 : /** Default value of the allowed INTRODUCE2 cell burst per second. This is the 39 : * maximum value a token bucket has per second. We thus allow up to this value 40 : * of INTRODUCE2 cell per second but the bucket is refilled by the rate value 41 : * but never goes above that burst value. */ 42 : #define HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC 200 43 : 44 : /** Default value of the consensus parameter enabling or disabling the 45 : * introduction DoS defense. Disabled by default. */ 46 : #define HS_DOS_INTRODUCE_ENABLED_DEFAULT 0 47 : 48 : /** INTRODUCE2 rejected request counter. */ 49 : static uint64_t intro2_rejected_count = 0; 50 : 51 : /* Consensus parameters. The ESTABLISH_INTRO DoS cell extension have higher 52 : * priority than these values. If no extension is sent, these are used only by 53 : * the introduction point. */ 54 : static uint32_t consensus_param_introduce_rate_per_sec = 55 : HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC; 56 : static uint32_t consensus_param_introduce_burst_per_sec = 57 : HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC; 58 : static uint32_t consensus_param_introduce_defense_enabled = 59 : HS_DOS_INTRODUCE_ENABLED_DEFAULT; 60 : 61 : STATIC uint32_t 62 196 : get_intro2_enable_consensus_param(const networkstatus_t *ns) 63 : { 64 196 : return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSDefense", 65 : HS_DOS_INTRODUCE_ENABLED_DEFAULT, 0, 1); 66 : } 67 : 68 : /** Return the parameter for the introduction rate per sec. */ 69 : STATIC uint32_t 70 198 : get_intro2_rate_consensus_param(const networkstatus_t *ns) 71 : { 72 198 : return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSRatePerSec", 73 : HS_DOS_INTRODUCE_DEFAULT_CELL_RATE_PER_SEC, 74 : 0, INT32_MAX); 75 : } 76 : 77 : /** Return the parameter for the introduction burst per sec. */ 78 : STATIC uint32_t 79 401 : get_intro2_burst_consensus_param(const networkstatus_t *ns) 80 : { 81 401 : return networkstatus_get_param(ns, "HiddenServiceEnableIntroDoSBurstPerSec", 82 : HS_DOS_INTRODUCE_DEFAULT_CELL_BURST_PER_SEC, 83 : 0, INT32_MAX); 84 : } 85 : 86 : /** Go over all introduction circuit relay side and adjust their rate/burst 87 : * values using the global parameters. This is called right after the 88 : * consensus parameters might have changed. */ 89 : static void 90 196 : update_intro_circuits(void) 91 : { 92 : /* Returns all HS version intro circuits. */ 93 196 : smartlist_t *intro_circs = hs_circuitmap_get_all_intro_circ_relay_side(); 94 : 95 196 : SMARTLIST_FOREACH_BEGIN(intro_circs, circuit_t *, circ) { 96 : /* Ignore circuit if the defenses were set explicitly through the 97 : * ESTABLISH_INTRO cell DoS extension. */ 98 0 : if (TO_OR_CIRCUIT(circ)->introduce2_dos_defense_explicit) { 99 0 : continue; 100 : } 101 : /* Defenses might have been enabled or disabled. */ 102 0 : TO_OR_CIRCUIT(circ)->introduce2_dos_defense_enabled = 103 : consensus_param_introduce_defense_enabled; 104 : /* Adjust the rate/burst value that might have changed. */ 105 0 : token_bucket_ctr_adjust(&TO_OR_CIRCUIT(circ)->introduce2_bucket, 106 : consensus_param_introduce_rate_per_sec, 107 : consensus_param_introduce_burst_per_sec); 108 0 : } SMARTLIST_FOREACH_END(circ); 109 : 110 196 : smartlist_free(intro_circs); 111 196 : } 112 : 113 : /** Set consensus parameters. */ 114 : static void 115 196 : set_consensus_parameters(const networkstatus_t *ns) 116 : { 117 392 : consensus_param_introduce_rate_per_sec = 118 196 : get_intro2_rate_consensus_param(ns); 119 392 : consensus_param_introduce_burst_per_sec = 120 196 : get_intro2_burst_consensus_param(ns); 121 392 : consensus_param_introduce_defense_enabled = 122 196 : get_intro2_enable_consensus_param(ns); 123 : 124 : /* The above might have changed which means we need to go through all 125 : * introduction circuits (relay side) and update the token buckets. */ 126 196 : update_intro_circuits(); 127 196 : } 128 : 129 : /* 130 : * Public API. 131 : */ 132 : 133 : /** Initialize the INTRODUCE2 token bucket for the DoS defenses using the 134 : * consensus/default values. We might get a cell extension that changes those 135 : * later but if we don't, the default or consensus parameters are used. */ 136 : void 137 7 : hs_dos_setup_default_intro2_defenses(or_circuit_t *circ) 138 : { 139 7 : tor_assert(circ); 140 : 141 7 : circ->introduce2_dos_defense_enabled = 142 : consensus_param_introduce_defense_enabled; 143 7 : token_bucket_ctr_init(&circ->introduce2_bucket, 144 : consensus_param_introduce_rate_per_sec, 145 : consensus_param_introduce_burst_per_sec, 146 7 : (uint32_t) approx_time()); 147 7 : } 148 : 149 : /** Called when the consensus has changed. We might have new consensus 150 : * parameters to look at. */ 151 : void 152 6 : hs_dos_consensus_has_changed(const networkstatus_t *ns) 153 : { 154 : /* No point on updating these values if we are not a public relay that can 155 : * be picked to be an introduction point. */ 156 6 : if (!public_server_mode(get_options())) { 157 : return; 158 : } 159 : 160 1 : set_consensus_parameters(ns); 161 : } 162 : 163 : /** Return true iff an INTRODUCE2 cell can be sent on the given service 164 : * introduction circuit. */ 165 : bool 166 324 : hs_dos_can_send_intro2(or_circuit_t *s_intro_circ) 167 : { 168 324 : tor_assert(s_intro_circ); 169 : 170 : /* Allow to send the cell if the DoS defenses are disabled on the circuit. 171 : * This can be set by the consensus, the ESTABLISH_INTRO cell extension or 172 : * the hardcoded values in tor code. */ 173 324 : if (!s_intro_circ->introduce2_dos_defense_enabled) { 174 1 : goto allow; 175 : } 176 : 177 : /* Should not happen but if so, scream loudly. */ 178 323 : if (BUG(TO_CIRCUIT(s_intro_circ)->purpose != CIRCUIT_PURPOSE_INTRO_POINT)) { 179 0 : goto disallow; 180 : } 181 : 182 : /* This is called just after we got a valid and parsed INTRODUCE1 cell. The 183 : * service has been found and we have its introduction circuit. 184 : * 185 : * First, the INTRODUCE2 bucket will be refilled (if any). Then, decremented 186 : * because we are about to send or not the cell we just got. Finally, 187 : * evaluate if we can send it based on our token bucket state. */ 188 : 189 : /* Refill INTRODUCE2 bucket. */ 190 323 : token_bucket_ctr_refill(&s_intro_circ->introduce2_bucket, 191 323 : (uint32_t) approx_time()); 192 : 193 : /* Decrement the bucket for this valid INTRODUCE1 cell we just got. Don't 194 : * underflow else we end up with a too big of a bucket. */ 195 323 : if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) { 196 223 : token_bucket_ctr_dec(&s_intro_circ->introduce2_bucket, 1); 197 : } 198 : 199 : /* Finally, we can send a new INTRODUCE2 if there are still tokens. */ 200 323 : if (token_bucket_ctr_get(&s_intro_circ->introduce2_bucket) > 0) { 201 222 : goto allow; 202 : } 203 : 204 : /* If we reach this point, then it means the bucket has reached zero, and 205 : we're going to disallow. */ 206 : 207 101 : disallow: 208 : /* Increment stats counter, we are rejecting the INTRO2 cell. */ 209 101 : intro2_rejected_count++; 210 101 : return false; 211 : 212 : allow: 213 : return true; 214 : } 215 : 216 : /** Return rolling count of rejected INTRO2. */ 217 : uint64_t 218 1 : hs_dos_get_intro2_rejected_count(void) 219 : { 220 1 : return intro2_rejected_count; 221 : } 222 : 223 : /** Initialize the onion service Denial of Service subsystem. */ 224 : void 225 195 : hs_dos_init(void) 226 : { 227 195 : set_consensus_parameters(NULL); 228 195 : }