15 #include <boost/algorithm/string/classification.hpp> 16 #include <boost/algorithm/string/replace.hpp> 17 #include <boost/algorithm/string/split.hpp> 18 #include <boost/signals2/signal.hpp> 20 #include <event2/buffer.h> 21 #include <event2/bufferevent.h> 22 #include <event2/event.h> 23 #include <event2/thread.h> 24 #include <event2/util.h> 39 "Tor safe cookie authentication server-to-controller hash";
42 "Tor safe cookie authentication controller-to-server hash";
78 typedef std::function<void(TorControlConnection &, const TorControlReply &)>
94 bool Connect(
const std::string &target,
const ConnectionCB &connected,
95 const ConnectionCB &disconnected);
107 bool Command(
const std::string &cmd,
const ReplyHandlerCB &reply_handler);
129 static void readcb(
struct bufferevent *bev,
void *
ctx);
130 static void eventcb(
struct bufferevent *bev,
short what,
void *
ctx);
134 : base(_base), b_conn(nullptr) {}
144 struct evbuffer *input = bufferevent_get_input(bev);
145 size_t n_read_out = 0;
149 while ((line = evbuffer_readln(input, &n_read_out, EVBUFFER_EOL_CRLF)) !=
151 std::string s(line, n_read_out);
158 self->message.code =
atoi(s.substr(0, 3));
159 self->message.lines.push_back(s.substr(4));
164 if (self->message.code >= 600) {
167 self->async_handler(*
self, self->message);
169 if (!self->reply_handlers.empty()) {
171 self->reply_handlers.front()(*
self,
self->message);
172 self->reply_handlers.pop_front();
175 "tor: Received unexpected sync reply %i\n",
179 self->message.Clear();
187 LogPrintf(
"tor: Disconnecting because MAX_LINE_LENGTH exceeded\n");
195 if (what & BEV_EVENT_CONNECTED) {
197 self->connected(*
self);
198 }
else if (what & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
199 if (what & BEV_EVENT_ERROR) {
201 "tor: Error connecting to Tor control socket\n");
206 self->disconnected(*
self);
217 struct sockaddr_storage connect_to_addr;
218 int connect_to_addrlen =
sizeof(connect_to_addr);
219 if (evutil_parse_sockaddr_port(target.c_str(),
220 (
struct sockaddr *)&connect_to_addr,
221 &connect_to_addrlen) < 0) {
222 LogPrintf(
"tor: Error parsing socket address %s\n", target);
227 b_conn = bufferevent_socket_new(
base, -1, BEV_OPT_CLOSE_ON_FREE);
233 bufferevent_enable(
b_conn, EV_READ | EV_WRITE);
238 if (bufferevent_socket_connect(
b_conn, (
struct sockaddr *)&connect_to_addr,
239 connect_to_addrlen) < 0) {
240 LogPrintf(
"tor: Error connecting to address %s\n", target);
258 struct evbuffer *buf = bufferevent_get_output(
b_conn);
262 evbuffer_add(buf, cmd.data(), cmd.size());
263 evbuffer_add(buf,
"\r\n", 2);
278 while (ptr < s.size() && s[ptr] !=
' ') {
279 type.push_back(s[ptr]);
282 if (ptr < s.size()) {
286 return make_pair(type, s.substr(ptr));
298 std::map<std::string, std::string> mapping;
300 while (ptr < s.size()) {
301 std::string key, value;
302 while (ptr < s.size() && s[ptr] !=
'=' && s[ptr] !=
' ') {
303 key.push_back(s[ptr]);
307 if (ptr == s.size()) {
308 return std::map<std::string, std::string>();
317 if (ptr < s.size() && s[ptr] ==
'"') {
320 bool escape_next =
false;
321 while (ptr < s.size() && (escape_next || s[ptr] !=
'"')) {
323 escape_next = (s[ptr] ==
'\\' && !escape_next);
324 value.push_back(s[ptr]);
328 if (ptr == s.size()) {
329 return std::map<std::string, std::string>();
345 std::string escaped_value;
346 for (
size_t i = 0; i < value.size(); ++i) {
347 if (value[i] ==
'\\') {
353 if (value[i] ==
'n') {
354 escaped_value.push_back(
'\n');
355 }
else if (value[i] ==
't') {
356 escaped_value.push_back(
'\t');
357 }
else if (value[i] ==
'r') {
358 escaped_value.push_back(
'\r');
359 }
else if (
'0' <= value[i] && value[i] <=
'7') {
364 for (j = 1; j < 3 && (i + j) < value.size() &&
365 '0' <= value[i + j] && value[i + j] <=
'7';
371 if (j == 3 && value[i] >
'3') {
374 escaped_value.push_back(
375 strtol(value.substr(i, j).c_str(), NULL, 8));
379 escaped_value.push_back(value[i]);
382 escaped_value.push_back(value[i]);
385 value = escaped_value;
389 while (ptr < s.size() && s[ptr] !=
' ') {
390 value.push_back(s[ptr]);
394 if (ptr < s.size() && s[ptr] ==
' ') {
398 mapping[key] = value;
413 static std::pair<bool, std::string>
415 size_t maxsize = std::numeric_limits<size_t>::max()) {
418 return std::make_pair(
false,
"");
423 while ((n = fread(buffer, 1,
sizeof(buffer), f)) > 0) {
428 return std::make_pair(
false,
"");
430 retval.append(buffer, buffer + n);
431 if (retval.size() > maxsize) {
436 return std::make_pair(
true, retval);
448 if (fwrite(data.data(), 1, data.size(), f) != data.size()) {
468 fs::path GetPrivateKeyFile();
504 static void reconnect_cb(evutil_socket_t fd,
short what,
void *arg);
508 const std::string &_target)
509 :
base(_base), target(_target), conn(
base), reconnect(true),
514 "tor: Failed to create event for reconnection: out of memory?\n");
519 std::placeholders::_1),
521 std::placeholders::_1))) {
522 LogPrintf(
"tor: Initiating connection to Tor control port %s failed\n",
546 if (reply.
code == 250) {
548 for (
const std::string &s : reply.
lines) {
550 std::map<std::string, std::string>::iterator i;
551 if ((i = m.find(
"ServiceID")) != m.end()) {
554 if ((i = m.find(
"PrivateKey")) != m.end()) {
559 LogPrintf(
"tor: Error parsing ADD_ONION parameters:\n");
560 for (
const std::string &s : reply.
lines) {
567 LogPrintf(
"tor: Got service ID %s, advertising service %s\n",
573 LogPrintf(
"tor: Error writing service private key to %s\n",
578 }
else if (reply.
code == 510) {
579 LogPrintf(
"tor: Add onion failed with unrecognized command (You " 580 "probably need to upgrade Tor)\n");
582 LogPrintf(
"tor: Add onion failed; error code %d\n", reply.
code);
588 if (reply.
code == 250) {
614 std::placeholders::_1, std::placeholders::_2));
616 LogPrintf(
"tor: Authentication failed\n");
636 static std::vector<uint8_t>
639 const std::vector<uint8_t> &serverNonce) {
640 CHMAC_SHA256 computeHash((
const uint8_t *)key.data(), key.size());
642 computeHash.Write(cookie.data(), cookie.size());
643 computeHash.Write(clientNonce.data(), clientNonce.size());
644 computeHash.Write(serverNonce.data(), serverNonce.size());
645 computeHash.Finalize(computedHash.data());
651 if (reply.
code == 250) {
653 "tor: SAFECOOKIE authentication challenge successful\n");
654 std::pair<std::string, std::string> l =
656 if (l.first ==
"AUTHCHALLENGE") {
657 std::map<std::string, std::string> m =
660 LogPrintf(
"tor: Error parsing AUTHCHALLENGE parameters: %s\n",
664 std::vector<uint8_t> serverHash =
ParseHex(m[
"SERVERHASH"]);
665 std::vector<uint8_t> serverNonce =
ParseHex(m[
"SERVERNONCE"]);
667 "tor: AUTHCHALLENGE ServerHash %s ServerNonce %s\n",
669 if (serverNonce.size() != 32) {
671 "tor: ServerNonce is not 32 bytes, as required by spec\n");
677 if (computedServerHash != serverHash) {
678 LogPrintf(
"tor: ServerHash %s does not match expected " 688 std::placeholders::_1,
689 std::placeholders::_2));
691 LogPrintf(
"tor: Invalid reply to AUTHCHALLENGE\n");
694 LogPrintf(
"tor: SAFECOOKIE authentication challenge failed\n");
700 if (reply.
code == 250) {
701 std::set<std::string> methods;
702 std::string cookiefile;
709 for (
const std::string &s : reply.
lines) {
711 if (l.first ==
"AUTH") {
712 std::map<std::string, std::string> m =
714 std::map<std::string, std::string>::iterator i;
715 if ((i = m.find(
"METHODS")) != m.end()) {
716 boost::split(methods, i->second, boost::is_any_of(
","));
718 if ((i = m.find(
"COOKIEFILE")) != m.end()) {
719 cookiefile = i->second;
721 }
else if (l.first ==
"VERSION") {
722 std::map<std::string, std::string> m =
724 std::map<std::string, std::string>::iterator i;
725 if ((i = m.find(
"Tor")) != m.end()) {
731 for (
const std::string &s : methods) {
741 std::string torpassword =
gArgs.
GetArg(
"-torpassword",
"");
742 if (!torpassword.empty()) {
743 if (methods.count(
"HASHEDPASSWORD")) {
745 "tor: Using HASHEDPASSWORD authentication\n");
746 boost::replace_all(torpassword,
"\"",
"\\\"");
747 _conn.
Command(
"AUTHENTICATE \"" + torpassword +
"\"",
749 std::placeholders::_1,
750 std::placeholders::_2));
752 LogPrintf(
"tor: Password provided with -torpassword, but " 753 "HASHEDPASSWORD authentication is not available\n");
755 }
else if (methods.count(
"NULL")) {
758 this, std::placeholders::_1,
759 std::placeholders::_2));
760 }
else if (methods.count(
"SAFECOOKIE")) {
763 "tor: Using SAFECOOKIE authentication, " 764 "reading cookie authentication from %s\n",
766 std::pair<bool, std::string> status_cookie =
768 if (status_cookie.first &&
773 cookie = std::vector<uint8_t>(status_cookie.second.begin(),
774 status_cookie.second.end());
777 _conn.
Command(
"AUTHCHALLENGE SAFECOOKIE " +
HexStr(clientNonce),
779 std::placeholders::_1,
780 std::placeholders::_2));
782 if (status_cookie.first) {
783 LogPrintf(
"tor: Authentication cookie %s is not exactly %i " 784 "bytes, as is required by the spec\n",
787 LogPrintf(
"tor: Authentication cookie %s could not be " 788 "opened (check permissions)\n",
792 }
else if (methods.count(
"HASHEDPASSWORD")) {
793 LogPrintf(
"tor: The only supported authentication mechanism left " 794 "is password, but no password provided with " 797 LogPrintf(
"tor: No supported authentication method\n");
800 LogPrintf(
"tor: Requesting protocol info failed\n");
808 if (!_conn.
Command(
"PROTOCOLINFO 1",
810 std::placeholders::_1,
811 std::placeholders::_2))) {
812 LogPrintf(
"tor: Error sending initial protocolinfo command\n");
827 "tor: Not connected to Tor control port %s, trying to reconnect\n",
844 std::placeholders::_1),
846 std::placeholders::_1))) {
848 "tor: Re-initiating connection to Tor control port %s failed\n",
869 event_base_dispatch(gBase);
875 evthread_use_windows_threads();
877 evthread_use_pthreads();
879 gBase = event_base_new();
881 LogPrintf(
"tor: Unable to create event_base\n");
885 torControlThread = std::thread(
893 gBase, -1, EV_TIMEOUT,
894 [](evutil_socket_t,
short,
void *) { event_base_loopbreak(gBase); },
901 torControlThread.join();
902 event_base_free(gBase);
void authchallenge_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHCHALLENGE result.
std::string SanitizeString(const std::string &str, int rule)
Remove unsafe chars.
static void TorControlThread()
static unsigned short GetDefaultPort()
static std::thread torControlThread
CService LookupNumeric(const std::string &name, int portDefault)
Resolve a service string with a numeric IP to its first corresponding service.
static const std::string TOR_SAFE_CLIENTKEY
For computing clientHash in SAFECOOKIE.
#define LogPrint(category,...)
bool AddLocal(const CService &addr, int nScore)
FILE * fopen(const fs::path &p, const char *mode)
struct bufferevent * b_conn
Connection to control socket.
std::function< void(TorControlConnection &)> disconnected
Callback when connection lost.
static const int TOR_COOKIE_SIZE
Tor cookie size (from control-spec.txt)
std::map< std::string, std::string > ParseTorReplyMapping(const std::string &s)
Parse reply arguments in the form 'METHODS=COOKIE,SAFECOOKIE COOKIEFILE=".../control_auth_cookie"'.
std::vector< uint8_t > clientNonce
ClientNonce for SAFECOOKIE auth.
static struct event_base * gBase
Reply from Tor, can be single or multi-line.
bool Connect(const std::string &target, const ConnectionCB &connected, const ConnectionCB &disconnected)
Connect to a Tor control port.
A hasher class for HMAC-SHA-256.
void protocolinfo_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for PROTOCOLINFO result.
void Reconnect()
Reconnect, after getting disconnected.
static bool WriteBinaryFile(const fs::path &filename, const std::string &data)
Write contents of std::string to a file.
std::vector< std::string > lines
static const int TOR_NONCE_SIZE
Size of client/server nonce for SAFECOOKIE.
static void LogPrintf(const char *fmt, const Args &... args)
std::function< void(TorControlConnection &)> connected
Callback when ready for use.
unsigned short GetListenPort()
std::pair< std::string, std::string > SplitTorReplyLine(const std::string &s)
std::deque< ReplyHandlerCB > reply_handlers
Response handlers.
void SetReachable(enum Network net, bool reachable)
Mark a network as reachable or unreachable (no automatic connects to it)
void GetRandBytes(uint8_t *buf, int num) noexcept
Overall design of the RNG and entropy sources.
void disconnected_cb(TorControlConnection &conn)
Callback after connection lost or failed connection attempt.
static void readcb(struct bufferevent *bev, void *ctx)
Libevent handlers: internal.
fs::path GetPrivateKeyFile()
Get name fo file to store private key in.
struct event * reconnect_ev
TorControlConnection(struct event_base *base)
Create a new TorControlConnection.
const std::string DEFAULT_TOR_CONTROL
Default control port.
std::function< void(TorControlConnection &)> ConnectionCB
std::vector< uint8_t > cookie
Cookie for SAFECOOKIE auth.
static const float RECONNECT_TIMEOUT_START
Exponential backoff configuration - initial timeout in seconds.
TorControlConnection conn
static const float RECONNECT_TIMEOUT_EXP
Exponential backoff configuration - growth factor.
int atoi(const std::string &str)
A combination of a network address (CNetAddr) and a (TCP) port.
TorController(struct event_base *base, const std::string &target)
static void reconnect_cb(evutil_socket_t fd, short what, void *arg)
Callback for reconnect timer.
const fs::path & GetDataDir(bool fNetSpecific)
void add_onion_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for ADD_ONION result.
void TraceThread(const char *name, Callable func)
boost::signals2::signal< void(TorControlConnection &, const TorControlReply &)> async_handler
Response handlers for async replies.
bool SetProxy(enum Network net, const proxyType &addrProxy)
struct timeval MillisToTimeval(int64_t nTimeout)
Convert milliseconds to a struct timeval for e.g.
void auth_cb(TorControlConnection &conn, const TorControlReply &reply)
Callback for AUTHENTICATE result.
void Disconnect()
Disconnect from Tor control port.
const CChainParams & Params()
Return the currently selected parameters.
static void eventcb(struct bufferevent *bev, short what, void *ctx)
static const size_t OUTPUT_SIZE
std::string GetArg(const std::string &strArg, const std::string &strDefault) const
Return string argument or default value.
std::vector< uint8_t > ParseHex(const char *psz)
bool Command(const std::string &cmd, const ReplyHandlerCB &reply_handler)
Send a command, register a handler for the reply.
std::string HexStr(const T itbegin, const T itend)
void connected_cb(TorControlConnection &conn)
Callback after successful connection.
std::string ToString() const
Controller that connects to Tor control socket, authenticate, then create and maintain an ephemeral h...
void InterruptTorControl()
Low-level handling for Tor control connection.
static const int MAX_LINE_LENGTH
Maximum length for lines received on TorControlConnection.
static const std::string TOR_SAFE_SERVERKEY
For computing serverHash in SAFECOOKIE.
static std::pair< bool, std::string > ReadBinaryFile(const fs::path &filename, size_t maxsize=std::numeric_limits< size_t >::max())
Read full contents of a file and return them in a std::string.
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)
Compute Tor SAFECOOKIE response.
TorControlReply message
Message being received.
void RemoveLocal(const CService &addr)
std::function< void(TorControlConnection &, const TorControlReply &)> ReplyHandlerCB
struct event_base * base
Libevent event base.