torcontrol.cpp
1 // Copyright (c) 2015-present 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 6 #include <torcontrol.h> 7 8 #include <chainparams.h> 9 #include <chainparamsbase.h> 10 #include <common/args.h> 11 #include <compat/compat.h> 12 #include <crypto/hmac_sha256.h> 13 #include <logging.h> 14 #include <net.h> 15 #include <netaddress.h> 16 #include <netbase.h> 17 #include <random.h> 18 #include <tinyformat.h> 19 #include <util/check.h> 20 #include <util/fs.h> 21 #include <util/readwritefile.h> 22 #include <util/strencodings.h> 23 #include <util/string.h> 24 #include <util/thread.h> 25 #include <util/time.h> 26 27 #include <algorithm> 28 #include <cassert> 29 #include <chrono> 30 #include <cstdint> 31 #include <cstdlib> 32 #include <deque> 33 #include <functional> 34 #include <map> 35 #include <optional> 36 #include <set> 37 #include <thread> 38 #include <utility> 39 #include <vector> 40 41 using util::ReplaceAll; 42 using util::SplitString; 43 using util::ToString; 44 45 /** Default control ip and port */ 46 const std::string DEFAULT_TOR_CONTROL = "127.0.0.1:" + ToString(DEFAULT_TOR_CONTROL_PORT); 47 /** Tor cookie size (from control-spec.txt) */ 48 constexpr int TOR_COOKIE_SIZE = 32; 49 /** Size of client/server nonce for SAFECOOKIE */ 50 constexpr int TOR_NONCE_SIZE = 32; 51 /** For computing server_hash in SAFECOOKIE */ 52 static const std::string TOR_SAFE_SERVERKEY = "Tor safe cookie authentication server-to-controller hash"; 53 /** For computing clientHash in SAFECOOKIE */ 54 static const std::string TOR_SAFE_CLIENTKEY = "Tor safe cookie authentication controller-to-server hash"; 55 /** Exponential backoff configuration - initial timeout in seconds */ 56 constexpr std::chrono::duration<double> RECONNECT_TIMEOUT_START{1.0}; 57 /** Exponential backoff configuration - growth factor */ 58 constexpr double RECONNECT_TIMEOUT_EXP = 1.5; 59 /** Maximum reconnect timeout in seconds to prevent excessive delays */ 60 constexpr std::chrono::duration<double> RECONNECT_TIMEOUT_MAX{600.0}; 61 /** Maximum length for lines received on TorControlConnection. 62 * tor-control-spec.txt mentions that there is explicitly no limit defined to line length, 63 * this is belt-and-suspenders sanity limit to prevent memory exhaustion. 64 */ 65 constexpr int MAX_LINE_LENGTH = 100000; 66 /** Maximum number of lines received on TorControlConnection per reply to avoid 67 * memory exhaustion. The largest expected now is 5 (PROTOCOLINFO), but future 68 * changes to this file might need to re-evaluate MAX_LINE_COUNT. 69 */ 70 constexpr int MAX_LINE_COUNT = 1000; 71 /** Timeout for socket operations */ 72 constexpr auto SOCKET_SEND_TIMEOUT = 10s; 73 74 /****** Low-level TorControlConnection ********/ 75 76 TorControlConnection::TorControlConnection(CThreadInterrupt& interrupt) 77 : m_interrupt(interrupt) 78 { 79 } 80 81 TorControlConnection::~TorControlConnection() 82 { 83 Disconnect(); 84 } 85 86 bool TorControlConnection::Connect(const std::string& tor_control_center) 87 { 88 if (m_sock) { 89 Disconnect(); 90 } 91 92 std::optional<CService> control_service = Lookup(tor_control_center, DEFAULT_TOR_CONTROL_PORT, fNameLookup); 93 if (!control_service.has_value()) { 94 LogWarning("tor: Failed to look up control center %s", tor_control_center); 95 return false; 96 } 97 98 m_sock = ConnectDirectly(control_service.value(), /*manual_connection=*/true); 99 if (!m_sock) { 100 LogWarning("tor: Error connecting to address %s", tor_control_center); 101 return false; 102 } 103 104 m_recv_buffer.clear(); 105 m_message.Clear(); 106 m_reply_handlers.clear(); 107 108 LogDebug(BCLog::TOR, "Successfully connected to Tor control port"); 109 return true; 110 } 111 112 void TorControlConnection::Disconnect() 113 { 114 m_sock.reset(); 115 m_recv_buffer.clear(); 116 m_message.Clear(); 117 m_reply_handlers.clear(); 118 } 119 120 bool TorControlConnection::IsConnected() const 121 { 122 if (!m_sock) return false; 123 std::string errmsg; 124 const bool connected{m_sock->IsConnected(errmsg)}; 125 if (!connected && !errmsg.empty()) { 126 LogDebug(BCLog::TOR, "Connection check failed: %s", errmsg); 127 } 128 return connected; 129 } 130 131 bool TorControlConnection::WaitForData(std::chrono::milliseconds timeout) 132 { 133 if (!m_sock) return false; 134 135 Sock::Event event{0}; 136 if (!m_sock->Wait(timeout, Sock::RECV, &event)) { 137 return false; 138 } 139 if (event & Sock::ERR) { 140 LogDebug(BCLog::TOR, "Socket error detected"); 141 Disconnect(); 142 return false; 143 } 144 145 return (event & Sock::RECV); 146 } 147 148 bool TorControlConnection::ReceiveAndProcess() 149 { 150 if (!m_sock) return false; 151 152 std::byte buf[4096]; 153 ssize_t nread = m_sock->Recv(buf, sizeof(buf), MSG_DONTWAIT); 154 155 if (nread < 0) { 156 int err = WSAGetLastError(); 157 if (err == WSAEWOULDBLOCK || err == WSAEINTR || err == WSAEINPROGRESS) { 158 // No data available currently 159 return true; 160 } 161 LogWarning("tor: Error reading from socket: %s", NetworkErrorString(err)); 162 return false; 163 } 164 165 if (nread == 0) { 166 LogDebug(BCLog::TOR, "End of stream"); 167 return false; 168 } 169 170 m_recv_buffer.insert(m_recv_buffer.end(), buf, buf + nread); 171 try { 172 return ProcessBuffer(); 173 } catch (const std::runtime_error& e) { 174 LogWarning("tor: Error processing receive buffer: %s", e.what()); 175 return false; 176 } 177 } 178 179 bool TorControlConnection::ProcessBuffer() 180 { 181 util::LineReader reader(m_recv_buffer, MAX_LINE_LENGTH); 182 auto start = reader.it; 183 184 while (auto line = reader.ReadLine()) { 185 if (m_message.lines.size() == MAX_LINE_COUNT) { 186 throw std::runtime_error(strprintf("Control port reply exceeded %d lines, disconnecting", MAX_LINE_COUNT)); 187 } 188 // Skip short lines 189 if (line->size() < 4) continue; 190 191 // Parse: <code><separator><data> 192 // <status>(-|+| )<data> 193 m_message.code = ToIntegral<int>(line->substr(0, 3)).value_or(0); 194 m_message.lines.push_back(line->substr(4)); 195 char separator = (*line)[3]; // '-', '+', or ' ' 196 197 if (separator == ' ') { 198 if (m_message.code >= 600) { 199 // Async notifications are currently unused 200 // Synchronous and asynchronous messages are never interleaved 201 LogDebug(BCLog::TOR, "Received async notification %i", m_message.code); 202 } else if (!m_reply_handlers.empty()) { 203 // Invoke reply handler with message 204 m_reply_handlers.front()(*this, m_message); 205 m_reply_handlers.pop_front(); 206 } else { 207 LogDebug(BCLog::TOR, "Received unexpected sync reply %i", m_message.code); 208 } 209 m_message.Clear(); 210 } 211 } 212 213 m_recv_buffer.erase(m_recv_buffer.begin(), m_recv_buffer.begin() + std::distance(start, reader.it)); 214 return true; 215 } 216 217 bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& reply_handler) 218 { 219 if (!m_sock) return false; 220 221 std::string command = cmd + "\r\n"; 222 try { 223 m_sock->SendComplete(std::span<const char>{command}, SOCKET_SEND_TIMEOUT, m_interrupt); 224 } catch (const std::runtime_error& e) { 225 LogWarning("tor: Error sending command: %s", e.what()); 226 return false; 227 } 228 229 m_reply_handlers.push_back(reply_handler); 230 return true; 231 } 232 233 /****** General parsing utilities ********/ 234 235 /* Split reply line in the form 'AUTH METHODS=...' into a type 236 * 'AUTH' and arguments 'METHODS=...'. 237 * Grammar is implicitly defined in https://spec.torproject.org/control-spec by 238 * the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24). 239 */ 240 std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s) 241 { 242 size_t ptr=0; 243 std::string type; 244 while (ptr < s.size() && s[ptr] != ' ') { 245 type.push_back(s[ptr]); 246 ++ptr; 247 } 248 if (ptr < s.size()) 249 ++ptr; // skip ' ' 250 return make_pair(type, s.substr(ptr)); 251 } 252 253 /** Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'. 254 * Returns a map of keys to values, or an empty map if there was an error. 255 * Grammar is implicitly defined in https://spec.torproject.org/control-spec by 256 * the server reply formats for PROTOCOLINFO (S3.21), AUTHCHALLENGE (S3.24), 257 * and ADD_ONION (S3.27). See also sections 2.1 and 2.3. 258 */ 259 std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s) 260 { 261 std::map<std::string,std::string> mapping; 262 size_t ptr=0; 263 while (ptr < s.size()) { 264 std::string key, value; 265 while (ptr < s.size() && s[ptr] != '=' && s[ptr] != ' ') { 266 key.push_back(s[ptr]); 267 ++ptr; 268 } 269 if (ptr == s.size()) // unexpected end of line 270 return std::map<std::string,std::string>(); 271 if (s[ptr] == ' ') // The remaining string is an OptArguments 272 break; 273 ++ptr; // skip '=' 274 if (ptr < s.size() && s[ptr] == '"') { // Quoted string 275 ++ptr; // skip opening '"' 276 bool escape_next = false; 277 while (ptr < s.size() && (escape_next || s[ptr] != '"')) { 278 // Repeated backslashes must be interpreted as pairs 279 escape_next = (s[ptr] == '\\' && !escape_next); 280 value.push_back(s[ptr]); 281 ++ptr; 282 } 283 if (ptr == s.size()) // unexpected end of line 284 return std::map<std::string,std::string>(); 285 ++ptr; // skip closing '"' 286 /** 287 * Unescape value. Per https://spec.torproject.org/control-spec section 2.1.1: 288 * 289 * For future-proofing, controller implementers MAY use the following 290 * rules to be compatible with buggy Tor implementations and with 291 * future ones that implement the spec as intended: 292 * 293 * Read \n \t \r and \0 ... \377 as C escapes. 294 * Treat a backslash followed by any other character as that character. 295 */ 296 std::string escaped_value; 297 for (size_t i = 0; i < value.size(); ++i) { 298 if (value[i] == '\\') { 299 // This will always be valid, because if the QuotedString 300 // ended in an odd number of backslashes, then the parser 301 // would already have returned above, due to a missing 302 // terminating double-quote. 303 ++i; 304 if (value[i] == 'n') { 305 escaped_value.push_back('\n'); 306 } else if (value[i] == 't') { 307 escaped_value.push_back('\t'); 308 } else if (value[i] == 'r') { 309 escaped_value.push_back('\r'); 310 } else if ('0' <= value[i] && value[i] <= '7') { 311 size_t j; 312 // Octal escape sequences have a limit of three octal digits, 313 // but terminate at the first character that is not a valid 314 // octal digit if encountered sooner. 315 for (j = 1; j < 3 && (i+j) < value.size() && '0' <= value[i+j] && value[i+j] <= '7'; ++j) {} 316 // Tor restricts first digit to 0-3 for three-digit octals. 317 // A leading digit of 4-7 would therefore be interpreted as 318 // a two-digit octal. 319 if (j == 3 && value[i] > '3') { 320 j--; 321 } 322 const auto end{i + j}; 323 uint8_t val{0}; 324 while (i < end) { 325 val *= 8; 326 val += value[i++] - '0'; 327 } 328 escaped_value.push_back(char(val)); 329 // Account for automatic incrementing at loop end 330 --i; 331 } else { 332 escaped_value.push_back(value[i]); 333 } 334 } else { 335 escaped_value.push_back(value[i]); 336 } 337 } 338 value = escaped_value; 339 } else { // Unquoted value. Note that values can contain '=' at will, just no spaces 340 while (ptr < s.size() && s[ptr] != ' ') { 341 value.push_back(s[ptr]); 342 ++ptr; 343 } 344 } 345 if (ptr < s.size() && s[ptr] == ' ') 346 ++ptr; // skip ' ' after key=value 347 mapping[key] = value; 348 } 349 return mapping; 350 } 351 352 TorController::TorController(const std::string& tor_control_center, const CService& target) 353 : m_tor_control_center(tor_control_center), 354 m_conn(m_interrupt), 355 m_reconnect(true), 356 m_reconnect_timeout(RECONNECT_TIMEOUT_START), 357 m_target(target) 358 { 359 // Read service private key if cached 360 std::pair<bool,std::string> pkf = ReadBinaryFile(GetPrivateKeyFile()); 361 if (pkf.first) { 362 LogDebug(BCLog::TOR, "Reading cached private key from %s", fs::PathToString(GetPrivateKeyFile())); 363 m_private_key = pkf.second; 364 } 365 m_thread = std::thread(&util::TraceThread, "torcontrol", [this] { ThreadControl(); }); 366 } 367 368 TorController::~TorController() 369 { 370 Interrupt(); 371 Join(); 372 if (m_service.IsValid()) { 373 RemoveLocal(m_service); 374 } 375 } 376 377 void TorController::Interrupt() 378 { 379 m_reconnect = false; 380 m_interrupt(); 381 } 382 383 void TorController::Join() 384 { 385 if (m_thread.joinable()) { 386 m_thread.join(); 387 } 388 } 389 390 void TorController::ThreadControl() 391 { 392 LogDebug(BCLog::TOR, "Entering Tor control thread"); 393 394 while (!m_interrupt) { 395 // Try to connect if not connected already 396 if (!m_conn.IsConnected()) { 397 LogDebug(BCLog::TOR, "Attempting to connect to Tor control port %s", m_tor_control_center); 398 399 if (!m_conn.Connect(m_tor_control_center)) { 400 LogWarning("tor: Initiating connection to Tor control port %s failed", m_tor_control_center); 401 if (!m_reconnect) { 402 break; 403 } 404 // Wait before retrying with exponential backoff 405 LogDebug(BCLog::TOR, "Retrying in %.1f seconds", m_reconnect_timeout.count()); 406 if (!m_interrupt.sleep_for(std::chrono::duration_cast<std::chrono::milliseconds>(m_reconnect_timeout))) { 407 break; 408 } 409 m_reconnect_timeout = std::min(m_reconnect_timeout * RECONNECT_TIMEOUT_EXP, RECONNECT_TIMEOUT_MAX); 410 continue; 411 } 412 // Successfully connected, reset timeout and trigger connected callback 413 m_reconnect_timeout = RECONNECT_TIMEOUT_START; 414 connected_cb(m_conn); 415 } 416 // Wait for data with a timeout 417 if (!m_conn.WaitForData(std::chrono::seconds(1))) { 418 // Check if still connected 419 if (!m_conn.IsConnected()) { 420 LogDebug(BCLog::TOR, "Lost connection to Tor control port"); 421 disconnected_cb(m_conn); 422 continue; 423 } 424 // Just a timeout, continue waiting 425 continue; 426 } 427 // Process incoming data 428 if (!m_conn.ReceiveAndProcess()) { 429 disconnected_cb(m_conn); 430 } 431 } 432 LogDebug(BCLog::TOR, "Exited Tor control thread"); 433 } 434 435 void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply) 436 { 437 // NOTE: We can only get here if -onion is unset 438 std::string socks_location; 439 if (reply.code == TOR_REPLY_OK) { 440 for (const auto& line : reply.lines) { 441 if (line.starts_with("net/listeners/socks=")) { 442 const std::string port_list_str = line.substr(20); 443 std::vector<std::string> port_list = SplitString(port_list_str, ' '); 444 445 for (auto& portstr : port_list) { 446 if (portstr.empty()) continue; 447 if ((portstr[0] == '"' || portstr[0] == '\'') && portstr.size() >= 2 && (*portstr.rbegin() == portstr[0])) { 448 portstr = portstr.substr(1, portstr.size() - 2); 449 if (portstr.empty()) continue; 450 } 451 socks_location = portstr; 452 if (portstr.starts_with("127.0.0.1:")) { 453 // Prefer localhost - ignore other ports 454 break; 455 } 456 } 457 } 458 } 459 if (!socks_location.empty()) { 460 LogDebug(BCLog::TOR, "Get SOCKS port command yielded %s", socks_location); 461 } else { 462 LogWarning("tor: Get SOCKS port command returned nothing"); 463 } 464 } else if (reply.code == TOR_REPLY_UNRECOGNIZED) { 465 LogWarning("tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)"); 466 } else { 467 LogWarning("tor: Get SOCKS port command failed; error code %d", reply.code); 468 } 469 470 CService resolved; 471 Assume(!resolved.IsValid()); 472 if (!socks_location.empty()) { 473 resolved = LookupNumeric(socks_location, DEFAULT_TOR_SOCKS_PORT); 474 } 475 if (!resolved.IsValid()) { 476 // Fallback to old behaviour 477 resolved = LookupNumeric("127.0.0.1", DEFAULT_TOR_SOCKS_PORT); 478 } 479 480 Assume(resolved.IsValid()); 481 LogDebug(BCLog::TOR, "Configuring onion proxy for %s", resolved.ToStringAddrPort()); 482 483 // Add Tor as proxy for .onion addresses. 484 // Enable stream isolation to prevent connection correlation and enhance privacy, by forcing a different Tor circuit for every connection. 485 // For this to work, the IsolateSOCKSAuth flag must be enabled on SOCKSPort (which is the default, see the IsolateSOCKSAuth section of Tor's manual page). 486 Proxy addrOnion = Proxy(resolved, /*tor_stream_isolation=*/ true); 487 SetProxy(NET_ONION, addrOnion); 488 489 const auto onlynets = gArgs.GetArgs("-onlynet"); 490 491 const bool onion_allowed_by_onlynet{ 492 onlynets.empty() || 493 std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) { 494 return ParseNetwork(n) == NET_ONION; 495 })}; 496 497 if (onion_allowed_by_onlynet) { 498 // If NET_ONION is reachable, then the below is a noop. 499 // 500 // If NET_ONION is not reachable, then none of -proxy or -onion was given. 501 // Since we are here, then -torcontrol and -torpassword were given. 502 g_reachable_nets.Add(NET_ONION); 503 } 504 } 505 506 static std::string MakeAddOnionCmd(const std::string& private_key, const std::string& target, bool enable_pow) 507 { 508 // Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports. 509 return strprintf("ADD_ONION %s%s Port=%i,%s", 510 private_key, 511 enable_pow ? " PoWDefensesEnabled=1" : "", 512 Params().GetDefaultPort(), 513 target); 514 } 515 516 void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply, bool pow_was_enabled) 517 { 518 if (reply.code == TOR_REPLY_OK) { 519 LogDebug(BCLog::TOR, "ADD_ONION successful (PoW defenses %s)", pow_was_enabled ? "enabled" : "disabled"); 520 for (const std::string &s : reply.lines) { 521 std::map<std::string,std::string> m = ParseTorReplyMapping(s); 522 std::map<std::string,std::string>::iterator i; 523 if ((i = m.find("ServiceID")) != m.end()) 524 m_service_id = i->second; 525 if ((i = m.find("PrivateKey")) != m.end()) 526 m_private_key = i->second; 527 } 528 if (m_service_id.empty()) { 529 LogWarning("tor: Error parsing ADD_ONION parameters:"); 530 for (const std::string &s : reply.lines) { 531 LogWarning(" %s", SanitizeString(s)); 532 } 533 return; 534 } 535 m_service = LookupNumeric(std::string(m_service_id+".onion"), Params().GetDefaultPort()); 536 LogInfo("Got tor service ID %s, advertising service %s", m_service_id, m_service.ToStringAddrPort()); 537 if (WriteBinaryFile(GetPrivateKeyFile(), m_private_key)) { 538 LogDebug(BCLog::TOR, "Cached service private key to %s", fs::PathToString(GetPrivateKeyFile())); 539 } else { 540 LogWarning("tor: Error writing service private key to %s", fs::PathToString(GetPrivateKeyFile())); 541 } 542 AddLocal(m_service, LOCAL_MANUAL); 543 // ... onion requested - keep connection open 544 } else if (reply.code == TOR_REPLY_UNRECOGNIZED) { 545 LogWarning("tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)"); 546 } else if (pow_was_enabled && reply.code == TOR_REPLY_SYNTAX_ERROR) { 547 LogDebug(BCLog::TOR, "ADD_ONION failed with PoW defenses, retrying without"); 548 _conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/false), 549 [this](TorControlConnection& conn, const TorControlReply& reply) { 550 add_onion_cb(conn, reply, /*pow_was_enabled=*/false); 551 }); 552 } else { 553 LogWarning("tor: Add onion failed; error code %d", reply.code); 554 } 555 } 556 557 void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& reply) 558 { 559 if (reply.code == TOR_REPLY_OK) { 560 LogDebug(BCLog::TOR, "Authentication successful"); 561 562 // Now that we know Tor is running setup the proxy for onion addresses 563 // if -onion isn't set to something else. 564 if (gArgs.GetArg("-onion", "") == "") { 565 _conn.Command("GETINFO net/listeners/socks", std::bind_front(&TorController::get_socks_cb, this)); 566 } 567 568 // Finally - now create the service 569 if (m_private_key.empty()) { // No private key, generate one 570 m_private_key = "NEW:ED25519-V3"; // Explicitly request key type - see issue #9214 571 } 572 // Request onion service, redirect port. 573 _conn.Command(MakeAddOnionCmd(m_private_key, m_target.ToStringAddrPort(), /*enable_pow=*/true), 574 [this](TorControlConnection& conn, const TorControlReply& reply) { 575 add_onion_cb(conn, reply, /*pow_was_enabled=*/true); 576 }); 577 } else { 578 LogWarning("tor: Authentication failed"); 579 } 580 } 581 582 /** Compute Tor SAFECOOKIE response. 583 * 584 * ServerHash is computed as: 585 * HMAC-SHA256("Tor safe cookie authentication server-to-controller hash", 586 * CookieString | ClientNonce | ServerNonce) 587 * (with the HMAC key as its first argument) 588 * 589 * After a controller sends a successful AUTHCHALLENGE command, the 590 * next command sent on the connection must be an AUTHENTICATE command, 591 * and the only authentication string which that AUTHENTICATE command 592 * will accept is: 593 * 594 * HMAC-SHA256("Tor safe cookie authentication controller-to-server hash", 595 * CookieString | ClientNonce | ServerNonce) 596 * 597 */ 598 static std::vector<uint8_t> ComputeResponse(std::string_view key, std::span<const uint8_t> cookie, std::span<const uint8_t> client_nonce, std::span<const uint8_t> server_nonce) 599 { 600 CHMAC_SHA256 computeHash((const uint8_t*)key.data(), key.size()); 601 std::vector<uint8_t> computedHash(CHMAC_SHA256::OUTPUT_SIZE, 0); 602 computeHash.Write(cookie.data(), cookie.size()); 603 computeHash.Write(client_nonce.data(), client_nonce.size()); 604 computeHash.Write(server_nonce.data(), server_nonce.size()); 605 computeHash.Finalize(computedHash.data()); 606 return computedHash; 607 } 608 609 void TorController::authchallenge_cb(TorControlConnection& _conn, const TorControlReply& reply) 610 { 611 if (reply.code == TOR_REPLY_OK) { 612 LogDebug(BCLog::TOR, "SAFECOOKIE authentication challenge successful"); 613 if (reply.lines.empty()) { 614 LogWarning("tor: AUTHCHALLENGE reply was empty"); 615 return; 616 } 617 std::pair<std::string,std::string> l = SplitTorReplyLine(reply.lines[0]); 618 if (l.first == "AUTHCHALLENGE") { 619 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); 620 if (m.empty()) { 621 LogWarning("tor: Error parsing AUTHCHALLENGE parameters: %s", SanitizeString(l.second)); 622 return; 623 } 624 std::vector<uint8_t> server_hash = ParseHex(m["SERVERHASH"]); 625 std::vector<uint8_t> server_nonce = ParseHex(m["SERVERNONCE"]); 626 LogDebug(BCLog::TOR, "AUTHCHALLENGE ServerHash %s ServerNonce %s", HexStr(server_hash), HexStr(server_nonce)); 627 if (server_nonce.size() != 32) { 628 LogWarning("tor: ServerNonce is not 32 bytes, as required by spec"); 629 return; 630 } 631 632 std::vector<uint8_t> computed_server_hash = ComputeResponse(TOR_SAFE_SERVERKEY, m_cookie, m_client_nonce, server_nonce); 633 if (computed_server_hash != server_hash) { 634 LogWarning("tor: ServerHash %s does not match expected ServerHash %s", HexStr(server_hash), HexStr(computed_server_hash)); 635 return; 636 } 637 638 std::vector<uint8_t> computedClientHash = ComputeResponse(TOR_SAFE_CLIENTKEY, m_cookie, m_client_nonce, server_nonce); 639 _conn.Command("AUTHENTICATE " + HexStr(computedClientHash), std::bind_front(&TorController::auth_cb, this)); 640 } else { 641 LogWarning("tor: Invalid reply to AUTHCHALLENGE"); 642 } 643 } else { 644 LogWarning("tor: SAFECOOKIE authentication challenge failed"); 645 } 646 } 647 648 void TorController::protocolinfo_cb(TorControlConnection& _conn, const TorControlReply& reply) 649 { 650 if (reply.code == TOR_REPLY_OK) { 651 std::set<std::string> methods; 652 std::string cookiefile; 653 /* 654 * 250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE="/home/x/.tor/control_auth_cookie" 655 * 250-AUTH METHODS=NULL 656 * 250-AUTH METHODS=HASHEDPASSWORD 657 */ 658 for (const std::string &s : reply.lines) { 659 std::pair<std::string,std::string> l = SplitTorReplyLine(s); 660 if (l.first == "AUTH") { 661 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); 662 std::map<std::string,std::string>::iterator i; 663 if ((i = m.find("METHODS")) != m.end()) { 664 std::vector<std::string> m_vec = SplitString(i->second, ','); 665 methods = std::set<std::string>(m_vec.begin(), m_vec.end()); 666 } 667 if ((i = m.find("COOKIEFILE")) != m.end()) 668 cookiefile = i->second; 669 } else if (l.first == "VERSION") { 670 std::map<std::string,std::string> m = ParseTorReplyMapping(l.second); 671 std::map<std::string,std::string>::iterator i; 672 if ((i = m.find("Tor")) != m.end()) { 673 LogDebug(BCLog::TOR, "Connected to Tor version %s", i->second); 674 } 675 } 676 } 677 for (const std::string &s : methods) { 678 LogDebug(BCLog::TOR, "Supported authentication method: %s", s); 679 } 680 // Prefer NULL, otherwise SAFECOOKIE. If a password is provided, use HASHEDPASSWORD 681 /* Authentication: 682 * cookie: hex-encoded ~/.tor/control_auth_cookie 683 * password: "password" 684 */ 685 std::string torpassword = gArgs.GetArg("-torpassword", ""); 686 if (!torpassword.empty()) { 687 if (methods.contains("HASHEDPASSWORD")) { 688 LogDebug(BCLog::TOR, "Using HASHEDPASSWORD authentication"); 689 ReplaceAll(torpassword, "\"", "\\\""); 690 _conn.Command("AUTHENTICATE \"" + torpassword + "\"", std::bind_front(&TorController::auth_cb, this)); 691 } else { 692 LogWarning("tor: Password provided with -torpassword, but HASHEDPASSWORD authentication is not available"); 693 } 694 } else if (methods.contains("NULL")) { 695 LogDebug(BCLog::TOR, "Using NULL authentication"); 696 _conn.Command("AUTHENTICATE", std::bind_front(&TorController::auth_cb, this)); 697 } else if (methods.contains("SAFECOOKIE")) { 698 // Cookie: hexdump -e '32/1 "%02x""\n"' ~/.tor/control_auth_cookie 699 LogDebug(BCLog::TOR, "Using SAFECOOKIE authentication, reading cookie authentication from %s", cookiefile); 700 std::pair<bool,std::string> status_cookie = ReadBinaryFile(fs::PathFromString(cookiefile), TOR_COOKIE_SIZE); 701 if (status_cookie.first && status_cookie.second.size() == TOR_COOKIE_SIZE) { 702 // _conn.Command("AUTHENTICATE " + HexStr(status_cookie.second), std::bind_front(&TorController::auth_cb, this)); 703 m_cookie = std::vector<uint8_t>(status_cookie.second.begin(), status_cookie.second.end()); 704 m_client_nonce = std::vector<uint8_t>(TOR_NONCE_SIZE, 0); 705 GetRandBytes(m_client_nonce); 706 _conn.Command("AUTHCHALLENGE SAFECOOKIE " + HexStr(m_client_nonce), std::bind_front(&TorController::authchallenge_cb, this)); 707 } else { 708 if (status_cookie.first) { 709 LogWarning("tor: Authentication cookie %s is not exactly %i bytes, as is required by the spec", cookiefile, TOR_COOKIE_SIZE); 710 } else { 711 LogWarning("tor: Authentication cookie %s could not be opened (check permissions)", cookiefile); 712 } 713 } 714 } else if (methods.contains("HASHEDPASSWORD")) { 715 LogWarning("tor: The only supported authentication mechanism left is password, but no password provided with -torpassword"); 716 } else { 717 LogWarning("tor: No supported authentication method"); 718 } 719 } else { 720 LogWarning("tor: Requesting protocol info failed"); 721 } 722 } 723 724 void TorController::connected_cb(TorControlConnection& _conn) 725 { 726 m_reconnect_timeout = RECONNECT_TIMEOUT_START; 727 // First send a PROTOCOLINFO command to figure out what authentication is expected 728 if (!_conn.Command("PROTOCOLINFO 1", std::bind_front(&TorController::protocolinfo_cb, this))) 729 LogWarning("tor: Error sending initial protocolinfo command"); 730 } 731 732 void TorController::disconnected_cb(TorControlConnection& _conn) 733 { 734 // Stop advertising service when disconnected 735 if (m_service.IsValid()) 736 RemoveLocal(m_service); 737 m_service = CService(); 738 if (!m_reconnect) 739 return; 740 741 LogDebug(BCLog::TOR, "Not connected to Tor control port %s, will retry", m_tor_control_center); 742 _conn.Disconnect(); 743 } 744 745 fs::path TorController::GetPrivateKeyFile() 746 { 747 return gArgs.GetDataDirNet() / "onion_v3_private_key"; 748 } 749 750 CService DefaultOnionServiceTarget(uint16_t port) 751 { 752 struct in_addr onion_service_target; 753 onion_service_target.s_addr = htonl(INADDR_LOOPBACK); 754 return {onion_service_target, port}; 755 }