diff --git a/daemon/I2PControl.cpp b/daemon/I2PControl.cpp index f9c91465..13ecb372 100644 --- a/daemon/I2PControl.cpp +++ b/daemon/I2PControl.cpp @@ -11,6 +11,8 @@ #include #include #include +#include +#include // Use global placeholders from boost introduced when local_time.hpp is loaded #define BOOST_BIND_GLOBAL_PLACEHOLDERS @@ -18,6 +20,7 @@ #include #include "FS.h" +#include "Base.h" #include "Log.h" #include "Config.h" #include "NetDb.hpp" @@ -278,7 +281,7 @@ namespace client { auto params = pt.get_child ("params"); if (method != "Authenticate" && - !m_Tokens.count (params.get ("Token", ""))) + !IsValidToken (params.get ("Token", ""))) { LogPrint (eLogWarning, "I2PControl: Invalid or missing token for method ", method); response << "{\"id\":null,\"error\":"; @@ -352,18 +355,42 @@ namespace client { int api = params.get ("API"); auto password = params.get ("Password"); - LogPrint (eLogDebug, "I2PControl: Authenticate API=", api, " Password=", password); - if (password != m_Password) { - LogPrint (eLogError, "I2PControl: Authenticate - Invalid password: ", password); + LogPrint (eLogDebug, "I2PControl: Authenticate API=", api); + bool isValid = password.size () == m_Password.size () && + CRYPTO_memcmp (password.data (), m_Password.data (), m_Password.size ()) == 0; + if (!isValid) { + LogPrint (eLogError, "I2PControl: Authenticate - Invalid password"); return; } InsertParam (results, "API", api); results << ","; - std::string token = boost::lexical_cast(i2p::util::GetSecondsSinceEpoch ()); - m_Tokens.insert (token); + uint8_t tokenBytes[32]; + RAND_bytes (tokenBytes, 32); + std::string token = i2p::data::ByteStreamToBase64 (tokenBytes, 32); + m_Tokens[token] = i2p::util::GetSecondsSinceEpoch () + I2P_CONTROL_TOKEN_LIFETIME; InsertParam (results, "Token", token); } + bool I2PControlService::IsValidToken (const std::string& token) + { + uint64_t ts = i2p::util::GetSecondsSinceEpoch (); + for (auto it = m_Tokens.begin (); it != m_Tokens.end ();) + { + if (ts > it->second) + it = m_Tokens.erase (it); // expired + else + ++it; + } + bool found = false; + for (const auto& it: m_Tokens) + { + if (it.first.size () == token.size () && + CRYPTO_memcmp (it.first.data (), token.data (), token.size ()) == 0) + found = true; // don't short-circuit; keep timing independent of match position + } + return found; + } + void I2PControlService::EchoHandler (const boost::property_tree::ptree& params, std::ostringstream& results) { auto echo = params.get ("Echo"); diff --git a/daemon/I2PControl.h b/daemon/I2PControl.h index 1ccee078..d39d3130 100644 --- a/daemon/I2PControl.h +++ b/daemon/I2PControl.h @@ -29,6 +29,7 @@ namespace client const size_t I2P_CONTROL_MAX_REQUEST_SIZE = 1024; typedef std::array I2PControlBuffer; + const uint64_t I2P_CONTROL_TOKEN_LIFETIME = 60*60; // 1 hour, in seconds const long I2P_CONTROL_CERTIFICATE_VALIDITY = 365*10; // 10 years const char I2P_CONTROL_CERTIFICATE_COMMON_NAME[] = "i2pd.i2pcontrol"; const char I2P_CONTROL_CERTIFICATE_ORGANIZATION[] = "Purple I2P"; @@ -95,7 +96,8 @@ namespace client #endif boost::asio::ssl::context m_SSLContext; boost::asio::steady_timer m_ShutdownTimer; - std::set m_Tokens; + std::map m_Tokens; // token -> expiration time in seconds since epoch + bool IsValidToken (const std::string& token); std::map m_MethodHandlers; std::map m_I2PControlHandlers;