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