]>
Commit | Line | Data |
---|---|---|
519713d3 JG |
1 | // Copyright (c) 2015-2017 The Bitcoin Core developers |
2 | // Copyright (c) 2017 The Zcash developers | |
3 | // Distributed under the MIT software license, see the accompanying | |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
5 | ||
eb5f63fe WL |
6 | #include "torcontrol.h" |
7 | #include "utilstrencodings.h" | |
8 | #include "net.h" | |
9 | #include "util.h" | |
975dc649 | 10 | #include "crypto/hmac_sha256.h" |
eb5f63fe WL |
11 | |
12 | #include <vector> | |
13 | #include <deque> | |
14 | #include <set> | |
15 | #include <stdlib.h> | |
16 | ||
17 | #include <boost/function.hpp> | |
18 | #include <boost/bind.hpp> | |
19 | #include <boost/signals2/signal.hpp> | |
20 | #include <boost/foreach.hpp> | |
21 | #include <boost/algorithm/string/predicate.hpp> | |
22 | #include <boost/algorithm/string/split.hpp> | |
23 | #include <boost/algorithm/string/classification.hpp> | |
975dc649 | 24 | #include <boost/algorithm/string/replace.hpp> |
eb5f63fe WL |
25 | |
26 | #include <event2/bufferevent.h> | |
27 | #include <event2/buffer.h> | |
28 | #include <event2/util.h> | |
29 | #include <event2/event.h> | |
975dc649 | 30 | #include <event2/thread.h> |
eb5f63fe | 31 | |
975dc649 | 32 | /** Default control port */ |
eb5f63fe | 33 | const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:9051"; |
975dc649 WL |
34 | /** Tor cookie size (from control-spec.txt) */ |
35 | static const int TOR_COOKIE_SIZE = 32; | |
36 | /** Size of client/server nonce for SAFECOOKIE */ | |
37 | static const int TOR_NONCE_SIZE = 32; | |
38 | /** For computing serverHash in SAFECOOKIE */ | |
39 | static const std::string TOR_SAFE_SERVERKEY = "Tor safe cookie authentication server-to-controller hash"; | |
40 | /** For computing clientHash in SAFECOOKIE */ | |
41 | static const std::string TOR_SAFE_CLIENTKEY = "Tor safe cookie authentication controller-to-server hash"; | |
42 | /** Exponential backoff configuration - initial timeout in seconds */ | |
43 | static const float RECONNECT_TIMEOUT_START = 1.0; | |
44 | /** Exponential backoff configuration - growth factor */ | |
45 | static const float RECONNECT_TIMEOUT_EXP = 1.5; | |
46 | /** Maximum length for lines received on TorControlConnection. | |
47 | * tor-control-spec.txt mentions that there is explicitly no limit defined to line length, | |
48 | * this is belt-and-suspenders sanity limit to prevent memory exhaustion. | |
49 | */ | |
50 | static const int MAX_LINE_LENGTH = 100000; | |
eb5f63fe WL |
51 | |
52 | /****** Low-level TorControlConnection ********/ | |
53 | ||
54 | /** Reply from Tor, can be single or multi-line */ | |
55 | class TorControlReply | |
56 | { | |
57 | public: | |
58 | TorControlReply() { Clear(); } | |
59 | ||
60 | int code; | |
61 | std::vector<std::string> lines; | |
62 | ||
63 | void Clear() | |
64 | { | |
65 | code = 0; | |
66 | lines.clear(); | |
67 | } | |
68 | }; | |
69 | ||
70 | /** Low-level handling for Tor control connection. | |
71 | * Speaks the SMTP-like protocol as defined in torspec/control-spec.txt | |
72 | */ | |
73 | class TorControlConnection | |
74 | { | |
75 | public: | |
76 | typedef boost::function<void(TorControlConnection&)> ConnectionCB; | |
77 | typedef boost::function<void(TorControlConnection &,const TorControlReply &)> ReplyHandlerCB; | |
78 | ||
79 | /** Create a new TorControlConnection. | |
80 | */ | |
81 | TorControlConnection(struct event_base *base); | |
82 | ~TorControlConnection(); | |
83 | ||
84 | /** | |
85 | * Connect to a Tor control port. | |
86 | * target is address of the form host:port. | |
e10e2124 | 87 | * connected is the handler that is called when connection is successfully established. |
eb5f63fe WL |
88 | * disconnected is a handler that is called when the connection is broken. |
89 | * Return true on success. | |
90 | */ | |
91 | bool Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected); | |
92 | ||
93 | /** | |
94 | * Disconnect from Tor control port. | |
95 | */ | |
96 | bool Disconnect(); | |
97 | ||
98 | /** Send a command, register a handler for the reply. | |
99 | * A trailing CRLF is automatically added. | |
100 | * Return true on success. | |
101 | */ | |
102 | bool Command(const std::string &cmd, const ReplyHandlerCB& reply_handler); | |
103 | ||
104 | /** Response handlers for async replies */ | |
105 | boost::signals2::signal<void(TorControlConnection &,const TorControlReply &)> async_handler; | |
106 | private: | |
107 | /** Callback when ready for use */ | |
108 | boost::function<void(TorControlConnection&)> connected; | |
109 | /** Callback when connection lost */ | |
110 | boost::function<void(TorControlConnection&)> disconnected; | |
111 | /** Libevent event base */ | |
112 | struct event_base *base; | |
113 | /** Connection to control socket */ | |
114 | struct bufferevent *b_conn; | |
115 | /** Message being received */ | |
116 | TorControlReply message; | |
117 | /** Response handlers */ | |
118 | std::deque<ReplyHandlerCB> reply_handlers; | |
119 | ||
120 | /** Libevent handlers: internal */ | |
121 | static void readcb(struct bufferevent *bev, void *ctx); | |
122 | static void eventcb(struct bufferevent *bev, short what, void *ctx); | |
123 | }; | |
124 | ||
125 | TorControlConnection::TorControlConnection(struct event_base *base): | |
126 | base(base), b_conn(0) | |
127 | { | |
128 | } | |
129 | ||
130 | TorControlConnection::~TorControlConnection() | |
131 | { | |
132 | if (b_conn) | |
133 | bufferevent_free(b_conn); | |
134 | } | |
135 | ||
136 | void TorControlConnection::readcb(struct bufferevent *bev, void *ctx) | |
137 | { | |
138 | TorControlConnection *self = (TorControlConnection*)ctx; | |
139 | struct evbuffer *input = bufferevent_get_input(bev); | |
140 | size_t n_read_out = 0; | |
141 | char *line; | |
142 | assert(input); | |
143 | // If there is not a whole line to read, evbuffer_readln returns NULL | |
144 | while((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) != NULL) | |
145 | { | |
146 | std::string s(line, n_read_out); | |
147 | free(line); | |
148 | if (s.size() < 4) // Short line | |
149 | continue; | |
150 | // <status>(-|+| )<data><CRLF> | |
975dc649 | 151 | self->message.code = atoi(s.substr(0,3)); |
eb5f63fe WL |
152 | self->message.lines.push_back(s.substr(4)); |
153 | char ch = s[3]; // '-','+' or ' ' | |
154 | if (ch == ' ') { | |
155 | // Final line, dispatch reply and clean up | |
156 | if (self->message.code >= 600) { | |
157 | // Dispatch async notifications to async handler | |
158 | // Synchronous and asynchronous messages are never interleaved | |
159 | self->async_handler(*self, self->message); | |
160 | } else { | |
161 | if (!self->reply_handlers.empty()) { | |
162 | // Invoke reply handler with message | |
163 | self->reply_handlers.front()(*self, self->message); | |
164 | self->reply_handlers.pop_front(); | |
165 | } else { | |
975dc649 | 166 | LogPrint("tor", "tor: Received unexpected sync reply %i\n", self->message.code); |
eb5f63fe WL |
167 | } |
168 | } | |
169 | self->message.Clear(); | |
170 | } | |
171 | } | |
975dc649 WL |
172 | // Check for size of buffer - protect against memory exhaustion with very long lines |
173 | // Do this after evbuffer_readln to make sure all full lines have been | |
174 | // removed from the buffer. Everything left is an incomplete line. | |
175 | if (evbuffer_get_length(input) > MAX_LINE_LENGTH) { | |
176 | LogPrintf("tor: Disconnecting because MAX_LINE_LENGTH exceeded\n"); | |
177 | self->Disconnect(); | |
178 | } | |
eb5f63fe WL |
179 | } |
180 | ||
181 | void TorControlConnection::eventcb(struct bufferevent *bev, short what, void *ctx) | |
182 | { | |
183 | TorControlConnection *self = (TorControlConnection*)ctx; | |
184 | if (what & BEV_EVENT_CONNECTED) { | |
e10e2124 | 185 | LogPrint("tor", "tor: Successfully connected!\n"); |
eb5f63fe WL |
186 | self->connected(*self); |
187 | } else if (what & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) { | |
188 | if (what & BEV_EVENT_ERROR) | |
975dc649 | 189 | LogPrint("tor", "tor: Error connecting to Tor control socket\n"); |
eb5f63fe | 190 | else |
975dc649 | 191 | LogPrint("tor", "tor: End of stream\n"); |
eb5f63fe WL |
192 | self->Disconnect(); |
193 | self->disconnected(*self); | |
194 | } | |
195 | } | |
196 | ||
197 | bool TorControlConnection::Connect(const std::string &target, const ConnectionCB& connected, const ConnectionCB& disconnected) | |
198 | { | |
199 | if (b_conn) | |
200 | Disconnect(); | |
201 | // Parse target address:port | |
202 | struct sockaddr_storage connect_to_addr; | |
203 | int connect_to_addrlen = sizeof(connect_to_addr); | |
204 | if (evutil_parse_sockaddr_port(target.c_str(), | |
205 | (struct sockaddr*)&connect_to_addr, &connect_to_addrlen)<0) { | |
975dc649 | 206 | LogPrintf("tor: Error parsing socket address %s\n", target); |
eb5f63fe WL |
207 | return false; |
208 | } | |
209 | ||
210 | // Create a new socket, set up callbacks and enable notification bits | |
211 | b_conn = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE); | |
212 | if (!b_conn) | |
213 | return false; | |
214 | bufferevent_setcb(b_conn, TorControlConnection::readcb, NULL, TorControlConnection::eventcb, this); | |
215 | bufferevent_enable(b_conn, EV_READ|EV_WRITE); | |
216 | this->connected = connected; | |
217 | this->disconnected = disconnected; | |
218 | ||
219 | // Finally, connect to target | |
220 | if (bufferevent_socket_connect(b_conn, (struct sockaddr*)&connect_to_addr, connect_to_addrlen) < 0) { | |
975dc649 | 221 | LogPrintf("tor: Error connecting to address %s\n", target); |
eb5f63fe WL |
222 | return false; |
223 | } | |
224 | return true; | |
225 | } | |
226 | ||
227 | bool TorControlConnection::Disconnect() | |
228 | { | |
229 | if (b_conn) | |
230 | bufferevent_free(b_conn); | |
231 | b_conn = 0; | |
232 | return true; | |
233 | } | |
234 | ||
235 | bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler) | |
236 | { | |
237 | if (!b_conn) | |
238 | return false; | |
239 | struct evbuffer *buf = bufferevent_get_output(b_conn); | |
240 | if (!buf) | |
241 | return false; | |
242 | evbuffer_add(buf, cmd.data(), cmd.size()); | |
243 | evbuffer_add(buf, "\r\n", 2); | |
244 | reply_handlers.push_back(reply_handler); | |
245 | return true; | |
246 | } | |
247 | ||
248 | /****** General parsing utilities ********/ | |
249 | ||
250 | /* Split reply line in the form 'AUTH METHODS=...' into a type | |
251 | * 'AUTH' and arguments 'METHODS=...'. | |
89665980 JG |
252 | * Grammar is implicitly defined in https://spec.torproject.org/control-spec by |
253 | * the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24). | |
eb5f63fe WL |
254 | */ |
255 | static std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s) | |
256 | { | |
257 | size_t ptr=0; | |
258 | std::string type; | |
259 | while (ptr < s.size() && s[ptr] != ' ') { | |
260 | type.push_back(s[ptr]); | |
261 | ++ptr; | |
262 | } | |
263 | if (ptr < s.size()) | |
264 | ++ptr; // skip ' ' | |
265 | return make_pair(type, s.substr(ptr)); | |
266 | } | |
267 | ||
268 | /** Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'. | |
87b7f4d8 | 269 | * Returns a map of keys to values, or an empty map if there was an error. |
89665980 JG |
270 | * Grammar is implicitly defined in https://spec.torproject.org/control-spec by |
271 | * the server reply formats for PROTOCOLINFO (S3.21), AUTHCHALLENGE (S3.24), | |
272 | * and ADD_ONION (S3.27). See also sections 2.1 and 2.3. | |
eb5f63fe WL |
273 | */ |
274 | static std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s) | |
275 | { | |
276 | std::map<std::string,std::string> mapping; | |
277 | size_t ptr=0; | |
278 | while (ptr < s.size()) { | |
279 | std::string key, value; | |
64101d04 | 280 | while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') { |
eb5f63fe WL |
281 | key.push_back(s[ptr]); |
282 | ++ptr; | |
283 | } | |
284 | if (ptr == s.size()) // unexpected end of line | |
285 | return std::map<std::string,std::string>(); | |
64101d04 JG |
286 | if (s[ptr] == ' ') // The remaining string is an OptArguments |
287 | break; | |
eb5f63fe WL |
288 | ++ptr; // skip '=' |
289 | if (ptr < s.size() && s[ptr] == '"') { // Quoted string | |
89665980 | 290 | ++ptr; // skip opening '"' |
eb5f63fe | 291 | bool escape_next = false; |
64101d04 | 292 | while (ptr < s.size() && (escape_next || s[ptr] != '"')) { |
0b431fbd JG |
293 | // Repeated backslashes must be interpreted as pairs |
294 | escape_next = (s[ptr] == '\\' && !escape_next); | |
eb5f63fe WL |
295 | value.push_back(s[ptr]); |
296 | ++ptr; | |
297 | } | |
298 | if (ptr == s.size()) // unexpected end of line | |
299 | return std::map<std::string,std::string>(); | |
300 | ++ptr; // skip closing '"' | |
b93cedaf | 301 | /** |
0b431fbd | 302 | * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1: |
b93cedaf JG |
303 | * |
304 | * For future-proofing, controller implementors MAY use the following | |
305 | * rules to be compatible with buggy Tor implementations and with | |
306 | * future ones that implement the spec as intended: | |
307 | * | |
308 | * Read \n \t \r and \0 ... \377 as C escapes. | |
309 | * Treat a backslash followed by any other character as that character. | |
eb5f63fe | 310 | */ |
b93cedaf JG |
311 | std::string escaped_value; |
312 | for (size_t i = 0; i < value.size(); ++i) { | |
313 | if (value[i] == '\\') { | |
0b431fbd JG |
314 | // This will always be valid, because if the QuotedString |
315 | // ended in an odd number of backslashes, then the parser | |
316 | // would already have returned above, due to a missing | |
317 | // terminating double-quote. | |
b93cedaf JG |
318 | ++i; |
319 | if (value[i] == 'n') { | |
320 | escaped_value.push_back('\n'); | |
321 | } else if (value[i] == 't') { | |
322 | escaped_value.push_back('\t'); | |
323 | } else if (value[i] == 'r') { | |
324 | escaped_value.push_back('\r'); | |
325 | } else if ('0' <= value[i] && value[i] <= '7') { | |
326 | size_t j; | |
327 | // Octal escape sequences have a limit of three octal digits, | |
328 | // but terminate at the first character that is not a valid | |
329 | // octal digit if encountered sooner. | |
d15cab21 | 330 | for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {} |
b93cedaf | 331 | // Tor restricts first digit to 0-3 for three-digit octals. |
8df5fd11 JG |
332 | // A leading digit of 4-7 would therefore be interpreted as |
333 | // a two-digit octal. | |
334 | if (j == 3 && value[i] > '3') { | |
335 | j--; | |
b93cedaf | 336 | } |
8df5fd11 JG |
337 | escaped_value.push_back(strtol(value.substr(i, j).c_str(), NULL, 8)); |
338 | // Account for automatic incrementing at loop end | |
339 | i += j - 1; | |
b93cedaf JG |
340 | } else { |
341 | escaped_value.push_back(value[i]); | |
342 | } | |
343 | } else { | |
344 | escaped_value.push_back(value[i]); | |
345 | } | |
346 | } | |
347 | value = escaped_value; | |
eb5f63fe WL |
348 | } else { // Unquoted value. Note that values can contain '=' at will, just no spaces |
349 | while (ptr < s.size() && s[ptr] != ' ') { | |
350 | value.push_back(s[ptr]); | |
351 | ++ptr; | |
352 | } | |
353 | } | |
354 | if (ptr < s.size() && s[ptr] == ' ') | |
355 | ++ptr; // skip ' ' after key=value | |
356 | mapping[key] = value; | |
357 | } | |
358 | return mapping; | |
359 | } | |
360 | ||
975dc649 WL |
361 | /** Read full contents of a file and return them in a std::string. |
362 | * Returns a pair <status, string>. | |
363 | * If an error occured, status will be false, otherwise status will be true and the data will be returned in string. | |
364 | * | |
365 | * @param maxsize Puts a maximum size limit on the file that is read. If the file is larger than this, truncated data | |
366 | * (with len > maxsize) will be returned. | |
367 | */ | |
368 | static std::pair<bool,std::string> ReadBinaryFile(const std::string &filename, size_t maxsize=std::numeric_limits<size_t>::max()) | |
eb5f63fe WL |
369 | { |
370 | FILE *f = fopen(filename.c_str(), "rb"); | |
371 | if (f == NULL) | |
372 | return std::make_pair(false,""); | |
373 | std::string retval; | |
374 | char buffer[128]; | |
375 | size_t n; | |
975dc649 | 376 | while ((n=fread(buffer, 1, sizeof(buffer), f)) > 0) { |
3290567b JG |
377 | // Check for reading errors so we don't return any data if we couldn't |
378 | // read the entire file (or up to maxsize) | |
12407174 | 379 | if (ferror(f)) { |
380 | fclose(f); | |
3290567b | 381 | return std::make_pair(false,""); |
12407174 | 382 | } |
eb5f63fe | 383 | retval.append(buffer, buffer+n); |
975dc649 WL |
384 | if (retval.size() > maxsize) |
385 | break; | |
386 | } | |
eb5f63fe WL |
387 | fclose(f); |
388 | return std::make_pair(true,retval); | |
389 | } | |
390 | ||
391 | /** Write contents of std::string to a file. | |
392 | * @return true on success. | |
393 | */ | |
394 | static bool WriteBinaryFile(const std::string &filename, const std::string &data) | |
395 | { | |
396 | FILE *f = fopen(filename.c_str(), "wb"); | |
397 | if (f == NULL) | |
398 | return false; | |
975dc649 WL |
399 | if (fwrite(data.data(), 1, data.size(), f) != data.size()) { |
400 | fclose(f); | |
eb5f63fe | 401 | return false; |
975dc649 | 402 | } |
eb5f63fe WL |
403 | fclose(f); |
404 | return true; | |
405 | } | |
406 | ||
407 | /****** Bitcoin specific TorController implementation ********/ | |
408 | ||
409 | /** Controller that connects to Tor control socket, authenticate, then create | |
410 | * and maintain a ephemeral hidden service. | |
411 | */ | |
412 | class TorController | |
413 | { | |
414 | public: | |
415 | TorController(struct event_base* base, const std::string& target); | |
416 | ~TorController(); | |
417 | ||
c938fb1f | 418 | /** Get name for file to store private key in */ |
eb5f63fe WL |
419 | std::string GetPrivateKeyFile(); |
420 | ||
421 | /** Reconnect, after getting disconnected */ | |
422 | void Reconnect(); | |
423 | private: | |
424 | struct event_base* base; | |
425 | std::string target; | |
426 | TorControlConnection conn; | |
427 | std::string private_key; | |
428 | std::string service_id; | |
429 | bool reconnect; | |
eb5f63fe WL |
430 | struct event *reconnect_ev; |
431 | float reconnect_timeout; | |
975dc649 WL |
432 | CService service; |
433 | /** Cooie for SAFECOOKIE auth */ | |
434 | std::vector<uint8_t> cookie; | |
435 | /** ClientNonce for SAFECOOKIE auth */ | |
436 | std::vector<uint8_t> clientNonce; | |
eb5f63fe WL |
437 | |
438 | /** Callback for ADD_ONION result */ | |
439 | void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply); | |
440 | /** Callback for AUTHENTICATE result */ | |
441 | void auth_cb(TorControlConnection& conn, const TorControlReply& reply); | |
975dc649 WL |
442 | /** Callback for AUTHCHALLENGE result */ |
443 | void authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply); | |
eb5f63fe WL |
444 | /** Callback for PROTOCOLINFO result */ |
445 | void protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply); | |
e10e2124 | 446 | /** Callback after successful connection */ |
eb5f63fe WL |
447 | void connected_cb(TorControlConnection& conn); |
448 | /** Callback after connection lost or failed connection attempt */ | |
449 | void disconnected_cb(TorControlConnection& conn); | |
450 | ||
eb5f63fe WL |
451 | /** Callback for reconnect timer */ |
452 | static void reconnect_cb(evutil_socket_t fd, short what, void *arg); | |
453 | }; | |
454 | ||
1a41e3f6 JS |
455 | TorController::TorController(struct event_base* baseIn, const std::string& target): |
456 | base(baseIn), | |
975dc649 | 457 | target(target), conn(base), reconnect(true), reconnect_ev(0), |
eb5f63fe WL |
458 | reconnect_timeout(RECONNECT_TIMEOUT_START) |
459 | { | |
3d7cddca WL |
460 | reconnect_ev = event_new(base, -1, 0, reconnect_cb, this); |
461 | if (!reconnect_ev) | |
462 | LogPrintf("tor: Failed to create event for reconnection: out of memory?\n"); | |
eb5f63fe WL |
463 | // Start connection attempts immediately |
464 | if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1), | |
465 | boost::bind(&TorController::disconnected_cb, this, _1) )) { | |
975dc649 | 466 | LogPrintf("tor: Initiating connection to Tor control port %s failed\n", target); |
eb5f63fe WL |
467 | } |
468 | // Read service private key if cached | |
469 | std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile()); | |
470 | if (pkf.first) { | |
975dc649 | 471 | LogPrint("tor", "tor: Reading cached private key from %s\n", GetPrivateKeyFile()); |
eb5f63fe WL |
472 | private_key = pkf.second; |
473 | } | |
eb5f63fe WL |
474 | } |
475 | ||
476 | TorController::~TorController() | |
477 | { | |
3d7cddca WL |
478 | if (reconnect_ev) { |
479 | event_free(reconnect_ev); | |
480 | reconnect_ev = 0; | |
481 | } | |
975dc649 WL |
482 | if (service.IsValid()) { |
483 | RemoveLocal(service); | |
484 | } | |
eb5f63fe WL |
485 | } |
486 | ||
487 | void TorController::add_onion_cb(TorControlConnection& conn, const TorControlReply& reply) | |
488 | { | |
489 | if (reply.code == 250) { | |
e10e2124 | 490 | LogPrint("tor", "tor: ADD_ONION successful\n"); |
eb5f63fe WL |
491 | BOOST_FOREACH(const std::string &s, reply.lines) { |
492 | std::map<std::string,std::string> m = ParseTorReplyMapping(s); | |
493 | std::map<std::string,std::string>::iterator i; | |
494 | if ((i = m.find("ServiceID")) != m.end()) | |
495 | service_id = i->second; | |
496 | if ((i = m.find("PrivateKey")) != m.end()) | |
497 | private_key = i->second; | |
498 | } | |
87b7f4d8 JG |
499 | if (service_id.empty()) { |
500 | LogPrintf("tor: Error parsing ADD_ONION parameters:\n"); | |
501 | for (const std::string &s : reply.lines) { | |
502 | LogPrintf(" %s\n", SanitizeString(s)); | |
503 | } | |
504 | return; | |
505 | } | |
eb5f63fe | 506 | |
975dc649 WL |
507 | service = CService(service_id+".onion", GetListenPort(), false); |
508 | LogPrintf("tor: Got service ID %s, advertizing service %s\n", service_id, service.ToString()); | |
eb5f63fe | 509 | if (WriteBinaryFile(GetPrivateKeyFile(), private_key)) { |
975dc649 | 510 | LogPrint("tor", "tor: Cached service private key to %s\n", GetPrivateKeyFile()); |
eb5f63fe | 511 | } else { |
975dc649 | 512 | LogPrintf("tor: Error writing service private key to %s\n", GetPrivateKeyFile()); |
eb5f63fe WL |
513 | } |
514 | AddLocal(service, LOCAL_MANUAL); | |
515 | // ... onion requested - keep connection open | |
77e5601e | 516 | } else if (reply.code == 510) { // 510 Unrecognized command |
975dc649 | 517 | LogPrintf("tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)\n"); |
eb5f63fe | 518 | } else { |
975dc649 | 519 | LogPrintf("tor: Add onion failed; error code %d\n", reply.code); |
eb5f63fe WL |
520 | } |
521 | } | |
522 | ||
523 | void TorController::auth_cb(TorControlConnection& conn, const TorControlReply& reply) | |
524 | { | |
525 | if (reply.code == 250) { | |
e10e2124 | 526 | LogPrint("tor", "tor: Authentication successful\n"); |
2b30758b PT |
527 | |
528 | // Now that we know Tor is running setup the proxy for onion addresses | |
529 | // if -onion isn't set to something else. | |
530 | if (GetArg("-onion", "") == "") { | |
531 | proxyType addrOnion = proxyType(CService("127.0.0.1", 9050), true); | |
532 | SetProxy(NET_TOR, addrOnion); | |
a05be280 | 533 | SetLimited(NET_TOR, false); |
2b30758b PT |
534 | } |
535 | ||
eb5f63fe WL |
536 | // Finally - now create the service |
537 | if (private_key.empty()) // No private key, generate one | |
9e5c9d0a | 538 | private_key = "NEW:RSA1024"; // Explicitly request RSA1024 - see issue #9214 |
eb5f63fe WL |
539 | // Request hidden service, redirect port. |
540 | // Note that the 'virtual' port doesn't have to be the same as our internal port, but this is just a convenient | |
541 | // choice. TODO; refactor the shutdown sequence some day. | |
542 | conn.Command(strprintf("ADD_ONION %s Port=%i,127.0.0.1:%i", private_key, GetListenPort(), GetListenPort()), | |
543 | boost::bind(&TorController::add_onion_cb, this, _1, _2)); | |
544 | } else { | |
975dc649 WL |
545 | LogPrintf("tor: Authentication failed\n"); |
546 | } | |
547 | } | |
548 | ||
549 | /** Compute Tor SAFECOOKIE response. | |
550 | * | |
551 | * ServerHash is computed as: | |
552 | * HMAC-SHA256("Tor safe cookie authentication server-to-controller hash", | |
553 | * CookieString | ClientNonce | ServerNonce) | |
554 | * (with the HMAC key as its first argument) | |
555 | * | |
556 | * After a controller sends a successful AUTHCHALLENGE command, the | |
557 | * next command sent on the connection must be an AUTHENTICATE command, | |
558 | * and the only authentication string which that AUTHENTICATE command | |
559 | * will accept is: | |
560 | * | |
561 | * HMAC-SHA256("Tor safe cookie authentication controller-to-server hash", | |
562 | * CookieString | ClientNonce | ServerNonce) | |
563 | * | |
564 | */ | |
565 | static std::vector<uint8_t> ComputeResponse(const std::string &key, const std::vector<uint8_t> &cookie, const std::vector<uint8_t> &clientNonce, const std::vector<uint8_t> &serverNonce) | |
566 | { | |
567 | CHMAC_SHA256 computeHash((const uint8_t*)key.data(), key.size()); | |
568 | std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0); | |
569 | computeHash.Write(begin_ptr(cookie), cookie.size()); | |
570 | computeHash.Write(begin_ptr(clientNonce), clientNonce.size()); | |
571 | computeHash.Write(begin_ptr(serverNonce), serverNonce.size()); | |
572 | computeHash.Finalize(begin_ptr(computedHash)); | |
573 | return computedHash; | |
574 | } | |
575 | ||
576 | void TorController::authchallenge_cb(TorControlConnection& conn, const TorControlReply& reply) | |
577 | { | |
578 | if (reply.code == 250) { | |
e10e2124 | 579 | LogPrint("tor", "tor: SAFECOOKIE authentication challenge successful\n"); |
975dc649 WL |
580 | std::pair<std::string,std::string> l = SplitTorReplyLine(reply.lines[0]); |
581 | if (l.first == "AUTHCHALLENGE") { | |
582 | std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); | |
87b7f4d8 JG |
583 | if (m.empty()) { |
584 | LogPrintf("tor: Error parsing AUTHCHALLENGE parameters: %s\n", SanitizeString(l.second)); | |
585 | return; | |
586 | } | |
975dc649 WL |
587 | std::vector<uint8_t> serverHash = ParseHex(m["SERVERHASH"]); |
588 | std::vector<uint8_t> serverNonce = ParseHex(m["SERVERNONCE"]); | |
589 | LogPrint("tor", "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n", HexStr(serverHash), HexStr(serverNonce)); | |
590 | if (serverNonce.size() != 32) { | |
591 | LogPrintf("tor: ServerNonce is not 32 bytes, as required by spec\n"); | |
592 | return; | |
593 | } | |
594 | ||
595 | std::vector<uint8_t> computedServerHash = ComputeResponse(TOR_SAFE_SERVERKEY, cookie, clientNonce, serverNonce); | |
596 | if (computedServerHash != serverHash) { | |
597 | LogPrintf("tor: ServerHash %s does not match expected ServerHash %s\n", HexStr(serverHash), HexStr(computedServerHash)); | |
598 | return; | |
599 | } | |
600 | ||
601 | std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, cookie, clientNonce, serverNonce); | |
602 | conn.Command("AUTHENTICATE " + HexStr(computedClientHash), boost::bind(&TorController::auth_cb, this, _1, _2)); | |
603 | } else { | |
604 | LogPrintf("tor: Invalid reply to AUTHCHALLENGE\n"); | |
605 | } | |
606 | } else { | |
607 | LogPrintf("tor: SAFECOOKIE authentication challenge failed\n"); | |
eb5f63fe WL |
608 | } |
609 | } | |
610 | ||
611 | void TorController::protocolinfo_cb(TorControlConnection& conn, const TorControlReply& reply) | |
612 | { | |
613 | if (reply.code == 250) { | |
614 | std::set<std::string> methods; | |
615 | std::string cookiefile; | |
616 | /* | |
617 | * 250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/home/x/.tor/control_auth_cookie" | |
618 | * 250-AUTH METHODS=NULL | |
619 | * 250-AUTH METHODS=HASHEDPASSWORD | |
620 | */ | |
621 | BOOST_FOREACH(const std::string &s, reply.lines) { | |
622 | std::pair<std::string,std::string> l = SplitTorReplyLine(s); | |
623 | if (l.first == "AUTH") { | |
624 | std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); | |
625 | std::map<std::string,std::string>::iterator i; | |
626 | if ((i = m.find("METHODS")) != m.end()) | |
627 | boost::split(methods, i->second, boost::is_any_of(",")); | |
628 | if ((i = m.find("COOKIEFILE")) != m.end()) | |
629 | cookiefile = i->second; | |
630 | } else if (l.first == "VERSION") { | |
631 | std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); | |
632 | std::map<std::string,std::string>::iterator i; | |
633 | if ((i = m.find("Tor")) != m.end()) { | |
975dc649 | 634 | LogPrint("tor", "tor: Connected to Tor version %s\n", i->second); |
eb5f63fe WL |
635 | } |
636 | } | |
637 | } | |
638 | BOOST_FOREACH(const std::string &s, methods) { | |
975dc649 | 639 | LogPrint("tor", "tor: Supported authentication method: %s\n", s); |
eb5f63fe | 640 | } |
975dc649 | 641 | // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD |
eb5f63fe WL |
642 | /* Authentication: |
643 | * cookie: hex-encoded ~/.tor/control_auth_cookie | |
644 | * password: "password" | |
645 | */ | |
975dc649 | 646 | std::string torpassword = GetArg("-torpassword", ""); |
ca5e2295 WL |
647 | if (!torpassword.empty()) { |
648 | if (methods.count("HASHEDPASSWORD")) { | |
649 | LogPrint("tor", "tor: Using HASHEDPASSWORD authentication\n"); | |
650 | boost::replace_all(torpassword, "\"", "\\\""); | |
651 | conn.Command("AUTHENTICATE \"" + torpassword + "\"", boost::bind(&TorController::auth_cb, this, _1, _2)); | |
652 | } else { | |
653 | LogPrintf("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available\n"); | |
654 | } | |
655 | } else if (methods.count("NULL")) { | |
975dc649 | 656 | LogPrint("tor", "tor: Using NULL authentication\n"); |
eb5f63fe | 657 | conn.Command("AUTHENTICATE", boost::bind(&TorController::auth_cb, this, _1, _2)); |
975dc649 | 658 | } else if (methods.count("SAFECOOKIE")) { |
eb5f63fe | 659 | // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie |
975dc649 WL |
660 | LogPrint("tor", "tor: Using SAFECOOKIE authentication, reading cookie authentication from %s\n", cookiefile); |
661 | std::pair<bool,std::string> status_cookie = ReadBinaryFile(cookiefile, TOR_COOKIE_SIZE); | |
662 | if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { | |
663 | // conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), boost::bind(&TorController::auth_cb, this, _1, _2)); | |
664 | cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end()); | |
665 | clientNonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0); | |
666 | GetRandBytes(&clientNonce[0], TOR_NONCE_SIZE); | |
667 | conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(clientNonce), boost::bind(&TorController::authchallenge_cb, this, _1, _2)); | |
eb5f63fe | 668 | } else { |
975dc649 WL |
669 | if (status_cookie.first) { |
670 | LogPrintf("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec\n", cookiefile, TOR_COOKIE_SIZE); | |
671 | } else { | |
672 | LogPrintf("tor: Authentication cookie %s could not be opened (check permissions)\n", cookiefile); | |
673 | } | |
674 | } | |
675 | } else if (methods.count("HASHEDPASSWORD")) { | |
ca5e2295 | 676 | LogPrintf("tor: The only supported authentication mechanism left is password, but no password provided with -torpassword\n"); |
eb5f63fe | 677 | } else { |
975dc649 | 678 | LogPrintf("tor: No supported authentication method\n"); |
eb5f63fe WL |
679 | } |
680 | } else { | |
975dc649 | 681 | LogPrintf("tor: Requesting protocol info failed\n"); |
eb5f63fe WL |
682 | } |
683 | } | |
684 | ||
685 | void TorController::connected_cb(TorControlConnection& conn) | |
686 | { | |
687 | reconnect_timeout = RECONNECT_TIMEOUT_START; | |
688 | // First send a PROTOCOLINFO command to figure out what authentication is expected | |
689 | if (!conn.Command("PROTOCOLINFO 1", boost::bind(&TorController::protocolinfo_cb, this, _1, _2))) | |
975dc649 | 690 | LogPrintf("tor: Error sending initial protocolinfo command\n"); |
eb5f63fe WL |
691 | } |
692 | ||
693 | void TorController::disconnected_cb(TorControlConnection& conn) | |
694 | { | |
975dc649 WL |
695 | // Stop advertizing service when disconnected |
696 | if (service.IsValid()) | |
697 | RemoveLocal(service); | |
698 | service = CService(); | |
eb5f63fe WL |
699 | if (!reconnect) |
700 | return; | |
65fd8eb1 | 701 | |
9b46a35b | 702 | LogPrint("tor", "tor: Not connected to Tor control port %s, trying to reconnect\n", target); |
65fd8eb1 | 703 | |
eb5f63fe WL |
704 | // Single-shot timer for reconnect. Use exponential backoff. |
705 | struct timeval time = MillisToTimeval(int64_t(reconnect_timeout * 1000.0)); | |
3d7cddca WL |
706 | if (reconnect_ev) |
707 | event_add(reconnect_ev, &time); | |
eb5f63fe WL |
708 | reconnect_timeout *= RECONNECT_TIMEOUT_EXP; |
709 | } | |
710 | ||
711 | void TorController::Reconnect() | |
712 | { | |
713 | /* Try to reconnect and reestablish if we get booted - for example, Tor | |
714 | * may be restarting. | |
715 | */ | |
716 | if (!conn.Connect(target, boost::bind(&TorController::connected_cb, this, _1), | |
717 | boost::bind(&TorController::disconnected_cb, this, _1) )) { | |
975dc649 | 718 | LogPrintf("tor: Re-initiating connection to Tor control port %s failed\n", target); |
eb5f63fe WL |
719 | } |
720 | } | |
721 | ||
722 | std::string TorController::GetPrivateKeyFile() | |
723 | { | |
724 | return (GetDataDir() / "onion_private_key").string(); | |
725 | } | |
726 | ||
eb5f63fe WL |
727 | void TorController::reconnect_cb(evutil_socket_t fd, short what, void *arg) |
728 | { | |
729 | TorController *self = (TorController*)arg; | |
730 | self->Reconnect(); | |
731 | } | |
732 | ||
733 | /****** Thread ********/ | |
4b5ba449 PJ |
734 | static struct event_base *gBase; |
735 | static boost::thread torControlThread; | |
eb5f63fe WL |
736 | |
737 | static void TorControlThread() | |
738 | { | |
f0e90192 | 739 | TorController ctrl(gBase, GetArg("-torcontrol", DEFAULT_TOR_CONTROL)); |
eb5f63fe | 740 | |
f0e90192 | 741 | event_base_dispatch(gBase); |
eb5f63fe WL |
742 | } |
743 | ||
744 | void StartTorControl(boost::thread_group& threadGroup, CScheduler& scheduler) | |
745 | { | |
f0e90192 | 746 | assert(!gBase); |
975dc649 WL |
747 | #ifdef WIN32 |
748 | evthread_use_windows_threads(); | |
749 | #else | |
750 | evthread_use_pthreads(); | |
751 | #endif | |
f0e90192 PJ |
752 | gBase = event_base_new(); |
753 | if (!gBase) { | |
975dc649 WL |
754 | LogPrintf("tor: Unable to create event_base\n"); |
755 | return; | |
756 | } | |
757 | ||
758 | torControlThread = boost::thread(boost::bind(&TraceThread<void (*)()>, "torcontrol", &TorControlThread)); | |
759 | } | |
760 | ||
761 | void InterruptTorControl() | |
762 | { | |
f0e90192 | 763 | if (gBase) { |
975dc649 | 764 | LogPrintf("tor: Thread interrupt\n"); |
f0e90192 | 765 | event_base_loopbreak(gBase); |
975dc649 | 766 | } |
eb5f63fe WL |
767 | } |
768 | ||
769 | void StopTorControl() | |
770 | { | |
f0e90192 | 771 | if (gBase) { |
975dc649 | 772 | torControlThread.join(); |
f0e90192 PJ |
773 | event_base_free(gBase); |
774 | gBase = 0; | |
975dc649 | 775 | } |
eb5f63fe WL |
776 | } |
777 |