From 9cca01d159bb9583f9079a85424cd410db2f6c60 Mon Sep 17 00:00:00 2001 From: EinMByte Date: Sat, 1 Aug 2015 23:10:10 +0200 Subject: [PATCH] Error handling and authentication for I2PControl. --- i2pcontrol/I2PControl.cpp | 121 ++++++++++++++++++++++++++++++++------ i2pcontrol/I2PControl.h | 36 +++++++++++- 2 files changed, 137 insertions(+), 20 deletions(-) diff --git a/i2pcontrol/I2PControl.cpp b/i2pcontrol/I2PControl.cpp index 86d85f5e..ae895606 100644 --- a/i2pcontrol/I2PControl.cpp +++ b/i2pcontrol/I2PControl.cpp @@ -17,8 +17,8 @@ namespace i2p { namespace client { -I2PControlSession::Response::Response(const std::string& id, const std::string& version) - : id(id), version(version), parameters() +I2PControlSession::Response::Response(const std::string& version) + : id(), version(version), error(ErrorCode::None), parameters() { } @@ -32,10 +32,42 @@ std::string I2PControlSession::Response::toJsonString() const oss << ','; oss << '"' << it->first << "\":" << it->second; } - oss << "},\"jsonrpc\":\"" << version << "\"}"; + oss << "},\"jsonrpc\":\"" << version << '"'; + if(error != ErrorCode::None) + oss << ",\"error\":{\"code\":" << static_cast(error) + << ",\"message\":\"" << getErrorMsg() << "\"" << "}"; + oss << "}"; return oss.str(); } +std::string I2PControlSession::Response::getErrorMsg() const +{ + switch(error) { + case ErrorCode::MethodNotFound: + return "Method not found."; + case ErrorCode::InvalidParameters: + return "Invalid parameters."; + case ErrorCode::InvalidRequest: + return "Invalid request."; + case ErrorCode::ParseError: + return "Json parse error."; + case ErrorCode::InvalidPassword: + return "Invalid password."; + case ErrorCode::NoToken: + return "No authentication token given."; + case ErrorCode::NonexistantToken: + return "Nonexistant authentication token given."; + case ErrorCode::ExpiredToken: + return "Exipred authentication token given."; + case ErrorCode::UnspecifiedVersion: + return "Version not specified."; + case ErrorCode::UnsupportedVersion: + return "Version not supported."; + default: + return ""; + }; +} + void I2PControlSession::Response::setParam(const std::string& param, const std::string& value) { parameters[param] = value.empty() ? "null" : "\"" + value + "\""; @@ -53,8 +85,18 @@ void I2PControlSession::Response::setParam(const std::string& param, double valu parameters[param] = oss.str(); } +void I2PControlSession::Response::setError(ErrorCode code) +{ + error = code; +} + +void I2PControlSession::Response::setId(const std::string& identifier) +{ + id = identifier; +} + I2PControlSession::I2PControlSession(boost::asio::io_service& ios) - : password(I2P_CONTROL_DEFAULT_PASSWORD), service(ios), shutdownTimer(ios) + : password(I2P_CONTROL_DEFAULT_PASSWORD), tokens(), service(ios), shutdownTimer(ios) { // Method handlers methodHandlers[I2P_CONTROL_METHOD_AUTHENTICATE] = &I2PControlSession::handleAuthenticate; @@ -85,22 +127,54 @@ I2PControlSession::Response I2PControlSession::handleRequest(std::stringstream& boost::property_tree::ptree pt; boost::property_tree::read_json(request, pt); - std::string method = pt.get(I2P_CONTROL_PROPERTY_METHOD); - auto it = methodHandlers.find(method); - if(it == methodHandlers.end()) { // Not found - LogPrint(eLogWarning, "Unknown I2PControl method ", method); - return Response("error"); // TODO: indicate the error through i2pcontrol + Response response; + try { + response.setId(pt.get(I2P_CONTROL_PROPERTY_ID)); + + std::string method = pt.get(I2P_CONTROL_PROPERTY_METHOD); + auto it = methodHandlers.find(method); + if(it == methodHandlers.end()) { // Not found + LogPrint(eLogWarning, "Unknown I2PControl method ", method); + response.setError(ErrorCode::MethodNotFound); + return response; + } + + PropertyTree params = pt.get_child(I2P_CONTROL_PROPERTY_PARAMS); + if(method != I2P_CONTROL_METHOD_AUTHENTICATE && !authenticate(params, response)) { + LogPrint(eLogWarning, "I2PControl invalid token presented"); + return response; + } + // Call the appropriate handler + (this->*(it->second))(params, response); + + } catch(const boost::property_tree::ptree_error& error) { + response.setError(ErrorCode::ParseError); + } catch(...) { + response.setError(ErrorCode::InternalError); } - Response response(pt.get(I2P_CONTROL_PROPERTY_ID)); - // Call the appropriate handler - (this->*(it->second))(pt.get_child(I2P_CONTROL_PROPERTY_PARAMS), response); return response; } +bool I2PControlSession::authenticate(const PropertyTree& pt, Response& response) +{ + try { + std::string token = pt.get(I2P_CONTROL_PARAM_TOKEN); + if(tokens.find(token) == tokens.end()) { + response.setError(ErrorCode::NonexistantToken); + return false; + } + } catch(const boost::property_tree::ptree_error& error) { + response.setError(ErrorCode::NoToken); + return false; + } + + return true; +} + void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& response) { - int api = pt.get(I2P_CONTROL_PARAM_API); + const int api = pt.get(I2P_CONTROL_PARAM_API); const std::string given_pass = pt.get(I2P_CONTROL_PARAM_PASSWORD); LogPrint(eLogDebug, "I2PControl Authenticate API = ", api, " Password = ", password); if(given_pass != password) { @@ -108,12 +182,14 @@ void I2PControlSession::handleAuthenticate(const PropertyTree& pt, Response& res eLogError, "I2PControl Authenticate Invalid password ", password, " expected ", password ); + response.setError(ErrorCode::InvalidPassword); return; } + // TODO: generate a secure token const std::string token = std::to_string(i2p::util::GetSecondsSinceEpoch()); response.setParam(I2P_CONTROL_PARAM_API, api); response.setParam(I2P_CONTROL_PARAM_TOKEN, token); - // TODO: store tokens to do something useful with them + tokens.insert(token); } void I2PControlSession::handleEcho(const PropertyTree& pt, Response& response) @@ -134,13 +210,16 @@ void I2PControlSession::handleRouterInfo(const PropertyTree& pt, Response& respo { LogPrint(eLogDebug, "I2PControl RouterInfo"); for(const auto& pair : pt) { + if(pair.first == I2P_CONTROL_PARAM_TOKEN) + continue; LogPrint(eLogDebug, pair.first); auto it = routerInfoHandlers.find(pair.first); - LogPrint(eLogDebug, "Still going"); - if(it != routerInfoHandlers.end()) + if(it != routerInfoHandlers.end()) { (this->*(it->second))(response); - else + } else { LogPrint(eLogError, "I2PControl RouterInfo unknown request ", pair.first); + response.setError(ErrorCode::InvalidRequest); + } } } @@ -148,12 +227,16 @@ void I2PControlSession::handleRouterManager(const PropertyTree& pt, Response& re { LogPrint(eLogDebug, "I2PControl RouterManager"); for(const auto& pair : pt) { + if(pair.first == I2P_CONTROL_PARAM_TOKEN) + continue; LogPrint(eLogDebug, pair.first); auto it = routerManagerHandlers.find(pair.first); - if(it != routerManagerHandlers.end()) + if(it != routerManagerHandlers.end()) { (this->*(it->second))(response); - else + } else { LogPrint(eLogError, "I2PControl RouterManager unknown request ", pair.first); + response.setError(ErrorCode::InvalidRequest); + } } } diff --git a/i2pcontrol/I2PControl.h b/i2pcontrol/I2PControl.h index 1011d4d5..eae0a6f3 100644 --- a/i2pcontrol/I2PControl.h +++ b/i2pcontrol/I2PControl.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -55,16 +56,36 @@ const char I2P_CONTROL_ROUTER_MANAGER_RESEED[] = "Reseed"; /** * "Null" I2P control implementation, does not do actual networking. + * @note authentication tokens are per-session */ class I2PControlSession { public: + enum class ErrorCode { + None = 0, + // JSON-RPC2 + MethodNotFound = 32601, + InvalidParameters = 32602, + InvalidRequest = 32600, + InternalError = 32603, + ParseError = 32700, + // I2PControl specific + InvalidPassword = 32001, + NoToken = 32002, + NonexistantToken = 32003, + ExpiredToken = 32004, + UnspecifiedVersion = 32005, + UnsupportedVersion = 32006 + }; + class Response { std::string id; std::string version; + ErrorCode error; std::map parameters; + public: - Response(const std::string& id, const std::string& version = "2.0"); + Response(const std::string& version = "2.0"); std::string toJsonString() const; /** @@ -74,6 +95,11 @@ public: void setParam(const std::string& param, const std::string& value); void setParam(const std::string& param, int value); void setParam(const std::string& param, double value); + + void setError(ErrorCode code); + void setId(const std::string& identifier); + + std::string getErrorMsg() const; }; /** @@ -95,6 +121,13 @@ private: ); typedef void (I2PControlSession::*RequestHandler)(Response& results); + + /** + * Tries to authenticate by checking whether the given token is valid. + * Sets the appropriate error code in the given response. + */ + bool authenticate(const PropertyTree& pt, Response& response); + // Method handlers void handleAuthenticate(const PropertyTree& pt, Response& response); void handleEcho(const PropertyTree& pt, Response& response); @@ -120,6 +153,7 @@ private: void handleReseed(Response& response); std::string password; + std::set tokens; std::map methodHandlers; std::map routerInfoHandlers;