Sanitize semicolon in http-server queries

This commit is contained in:
Leon Etienne 2022-12-10 22:49:58 +00:00
parent 2055cf574c
commit 7ef2432796

View File

@ -1,306 +1,307 @@
#include "HttpServer.h" #include "HttpServer.h"
#include "external_dependencies/leonetienne/stringtools/StringTools.h" #include "external_dependencies/leonetienne/stringtools/StringTools.h"
using namespace Logging; using namespace Logging;
using namespace Rest; using namespace Rest;
using namespace JasonPP; using namespace JasonPP;
HttpServer::HttpServer() HttpServer::HttpServer()
{ {
pMgr = new mg_mgr(); pMgr = new mg_mgr();
pNc = nullptr; pNc = nullptr;
log = new Logger("HttpServer"); log = new Logger("HttpServer");
return; return;
} }
HttpServer::~HttpServer() HttpServer::~HttpServer()
{ {
delete pMgr; delete pMgr;
delete log; delete log;
log = nullptr; log = nullptr;
pMgr = nullptr; pMgr = nullptr;
return; return;
} }
void HttpServer::PostInit() void HttpServer::PostInit()
{ {
isBootedSuccessfully = InitWebServer(); isBootedSuccessfully = InitWebServer();
return; return;
} }
bool HttpServer::InitWebServer() bool HttpServer::InitWebServer()
{ {
mg_mgr_init(pMgr, NULL); mg_mgr_init(pMgr, NULL);
log->cout << "Starting http-server on port " << XGConfig::httpServer.port << "..."; log->cout << "Starting http-server on port " << XGConfig::httpServer.port << "...";
log->Flush(); log->Flush();
pNc = mg_bind(pMgr, XGConfig::httpServer.port.c_str(), this->EventHandler); pNc = mg_bind(pMgr, XGConfig::httpServer.port.c_str(), this->EventHandler);
if (pNc == NULL) if (pNc == NULL)
{ {
log->cout << log->Err() << "Failed to boot the http-server! - Unable to bind listener! (port: " << XGConfig::httpServer.port << ")"; log->cout << log->Err() << "Failed to boot the http-server! - Unable to bind listener! (port: " << XGConfig::httpServer.port << ")";
log->Flush(); log->Flush();
return false; return false;
} }
mg_set_protocol_http_websocket(pNc); mg_set_protocol_http_websocket(pNc);
frontend_serve_opts.document_root = XGConfig::httpServer.rootdir.c_str(); frontend_serve_opts.document_root = XGConfig::httpServer.rootdir.c_str();
frontend_serve_opts.enable_directory_listing = "no"; frontend_serve_opts.enable_directory_listing = "no";
log->cout << "Started http-server successfully!"; log->cout << "Started http-server successfully!";
log->Flush(); log->Flush();
isBootedSuccessfully = true; isBootedSuccessfully = true;
return true; return true;
} }
void HttpServer::Update() void HttpServer::Update()
{ {
mg_mgr_poll(pMgr, XGConfig::httpServer.polling_rate); mg_mgr_poll(pMgr, XGConfig::httpServer.polling_rate);
return; return;
} }
void HttpServer::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode) void HttpServer::ServeStringToConnection(struct mg_connection* c, std::string str, int httpStatusCode)
{ {
mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *"); mg_send_head(c, httpStatusCode, str.length(), "content-type: application/json\nAccess-Control-Allow-Origin: *");
mg_printf(c, "%s", str.c_str()); mg_printf(c, "%s", str.c_str());
return; return;
} }
void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p) void HttpServer::EventHandler(mg_connection* pNc, int ev, void* p)
{ {
switch (ev) switch (ev)
{ {
case MG_EV_HTTP_REQUEST: case MG_EV_HTTP_REQUEST:
// Reset standby timer // Reset standby timer
XGControl::last_query_time = time(0); XGControl::last_query_time = time(0);
http_message* hpm = (http_message*)p; http_message* hpm = (http_message*)p;
std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len); std::string requestedUri = FixUnterminatedString(hpm->uri.p, hpm->uri.len);
// Get clients ip address // Get clients ip address
std::string peer_addr; std::string peer_addr;
{ {
char buf[32]; char buf[32];
mg_sock_addr_to_str(&pNc->sa, buf, sizeof(buf), MG_SOCK_STRINGIFY_IP); mg_sock_addr_to_str(&pNc->sa, buf, sizeof(buf), MG_SOCK_STRINGIFY_IP);
peer_addr = buf; peer_addr = buf;
} }
std::string denialReason; std::string denialReason;
if (IsConnectionAllowed(peer_addr, denialReason)) if (IsConnectionAllowed(peer_addr, denialReason))
{ {
try try
{ {
if (requestedUri == "/api") if (requestedUri == "/api")
{ {
ProcessAPIRequest(pNc, ev, p, peer_addr); ProcessAPIRequest(pNc, ev, p, peer_addr);
} }
else if (requestedUri.substr(0, 12) == "/downloadlog") else if (requestedUri.substr(0, 12) == "/downloadlog")
{ {
ServeDownloadLog(pNc, ev, p, requestedUri); ServeDownloadLog(pNc, ev, p, requestedUri);
} }
else if (requestedUri.substr(0, 9) == "/download") else if (requestedUri.substr(0, 9) == "/download")
{ {
ServeDownloadeableResource(pNc, ev, p, requestedUri); ServeDownloadeableResource(pNc, ev, p, requestedUri);
} }
else else
{ {
// Just serve the files requested // Just serve the files requested
mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts); mg_serve_http(pNc, (struct http_message*)p, frontend_serve_opts);
} }
} }
catch (std::exception& e) catch (std::exception& e)
{ {
Json j; Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, e.what())); j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, e.what()));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR); ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
} }
catch (...) catch (...)
{ {
Json j; Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, "Das not good")); j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR, "Das not good"));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR); ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::INTERNAL_SERVER_ERROR);
} }
break; break;
} }
else // Client is not allowed, serve error json else // Client is not allowed, serve error json
{ {
Json j; Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::UNAUTHORIZED, denialReason)); j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::UNAUTHORIZED, denialReason));
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::UNAUTHORIZED); ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::UNAUTHORIZED);
} }
} }
return; return;
} }
std::string HttpServer::SanitizeString(std::string in) { std::string HttpServer::SanitizeString(std::string in) {
in = StringTools::Replace(in, '`', "\\\\`"); in = StringTools::Replace(in, '`', "\\\\`");
in = StringTools::Replace(in, '|', "\\\\|"); in = StringTools::Replace(in, '|', "\\\\|");
in = StringTools::Replace(in, '$', "\\\\$"); in = StringTools::Replace(in, '$', "\\\\$");
in = StringTools::Replace(in, "&&", "\\\\&\\\\&"); in = StringTools::Replace(in, "&&", "\\\\&\\\\&");
in = StringTools::Replace(in, ";", "\\\\;");
return in;
} return in;
}
void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p, std::string peerAddress)
{ void HttpServer::ProcessAPIRequest(mg_connection* pNc, int ev, void* p, std::string peerAddress)
// Get struct with http message informations {
http_message* hpm = (http_message*)p; // Get struct with http message informations
http_message* hpm = (http_message*)p;
// Get the transmitted message body
std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len); // Get the transmitted message body
std::string requestBodyRaw = FixUnterminatedString(hpm->body.p, hpm->body.len);
// Sanitize it
requestBodyRaw = SanitizeString(requestBodyRaw); // Sanitize it
requestBodyRaw = SanitizeString(requestBodyRaw);
// Check for the body being valid json
if (IsJsonValid(requestBodyRaw)) // Check for the body being valid json
{ if (IsJsonValid(requestBodyRaw))
Json requestBody; {
requestBody.Parse(requestBodyRaw); Json requestBody;
requestBody.Parse(requestBodyRaw);
JsonBlock responseBody;
HTTP_STATUS_CODE returnCode; JsonBlock responseBody;
RestQueryHandler::ProcessQuery(peerAddress, requestBody, responseBody, returnCode); HTTP_STATUS_CODE returnCode;
RestQueryHandler::ProcessQuery(peerAddress, requestBody, responseBody, returnCode);
Json response(responseBody);
ServeStringToConnection(pNc, response.Render(), (int)returnCode); Json response(responseBody);
} ServeStringToConnection(pNc, response.Render(), (int)returnCode);
else // return error message for invalid json }
{ else // return error message for invalid json
Json errorJson; {
errorJson.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked")); Json errorJson;
ServeStringToConnection(pNc, errorJson.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST); errorJson.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Received json is fucked"));
} ServeStringToConnection(pNc, errorJson.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
} return;
}
void HttpServer::ServeDownloadeableResource(mg_connection* pNc, int ev, void* p, std::string uri)
{ void HttpServer::ServeDownloadeableResource(mg_connection* pNc, int ev, void* p, std::string uri)
std::string fileId = uri.substr(10, uri.length() - 10); {
std::string fileId = uri.substr(10, uri.length() - 10);
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
{ if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId); {
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
if (entry.status == Downloader::DOWNLOAD_STATUS::FINISHED)
{ if (entry.status == Downloader::DOWNLOAD_STATUS::FINISHED)
std::stringstream ss; {
std::string downloadedFilename = entry.title + (entry.mode == Downloader::DOWNLOAD_MODE::AUDIO ? ".mp3" : ".mp4"); std::stringstream ss;
std::string downloadedFilename = entry.title + (entry.mode == Downloader::DOWNLOAD_MODE::AUDIO ? ".mp3" : ".mp4");
ss << "Access-Control-Allow-Origin: *\nContent-Disposition: attachment; filename=\"" << downloadedFilename << "\"\nPragma: public\nCache-Control: must-revalidate, post-check=0, pre-check=0";
mg_http_serve_file(pNc, (http_message*)p, entry.downloaded_filename.c_str(), mg_mk_str("application/octet-stream"), mg_mk_str(ss.str().c_str())); ss << "Access-Control-Allow-Origin: *\nContent-Disposition: attachment; filename=\"" << downloadedFilename << "\"\nPragma: public\nCache-Control: must-revalidate, post-check=0, pre-check=0";
} mg_http_serve_file(pNc, (http_message*)p, entry.downloaded_filename.c_str(), mg_mk_str("application/octet-stream"), mg_mk_str(ss.str().c_str()));
else }
{ else
Json j; {
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "File download not ready!")); Json j;
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST); j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "File download not ready!"));
} ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
} }
else }
{ else
Json j; {
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!")); Json j;
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST); j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
} ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
} return;
}
void HttpServer::ServeDownloadLog(mg_connection* pNc, int ev, void* p, std::string uri)
{ void HttpServer::ServeDownloadLog(mg_connection* pNc, int ev, void* p, std::string uri)
std::string fileId = uri.substr(13, uri.length() - 13); {
std::string fileId = uri.substr(13, uri.length() - 13);
if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
{ if (Downloader::DownloadManager::DoesTubioIDExist(fileId))
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId); {
Downloader::DownloadEntry& entry = Downloader::DownloadManager::GetDownloadEntryByTubioID(fileId);
std::stringstream ss;
std::string logFilename = std::string("./dlcache/dlprogbuf/") + fileId + ".buf"; std::stringstream ss;
std::string logFilename = std::string("./dlcache/dlprogbuf/") + fileId + ".buf";
ss << "Access-Control-Allow-Origin: *\nContent-Type: text/plain; Cache-Control: must-revalidate, post-check=0, pre-check=0";
mg_http_serve_file(pNc, (http_message*)p, logFilename.c_str(), mg_mk_str("text/plain"), mg_mk_str(ss.str().c_str())); ss << "Access-Control-Allow-Origin: *\nContent-Type: text/plain; Cache-Control: must-revalidate, post-check=0, pre-check=0";
} mg_http_serve_file(pNc, (http_message*)p, logFilename.c_str(), mg_mk_str("text/plain"), mg_mk_str(ss.str().c_str()));
else }
{ else
Json j; {
std::stringstream ss; Json j;
j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!")); std::stringstream ss;
ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST); j.CloneFrom(RestResponseTemplates::GetByCode(HTTP_STATUS_CODE::BAD_REQUEST, "Invalid tubio id!"));
} ServeStringToConnection(pNc, j.Render(), (int)HTTP_STATUS_CODE::BAD_REQUEST);
}
return;
} return;
}
bool HttpServer::IsConnectionAllowed(std::string peer_address, std::string& denialReason)
{ bool HttpServer::IsConnectionAllowed(std::string peer_address, std::string& denialReason)
// Localhost is always allowed! {
if (peer_address == "127.0.0.1") return true; // Localhost is always allowed!
if (peer_address == "127.0.0.1") return true;
// Peer is not localhost, but only localhost is allowed!
else if (XGConfig::access.only_allow_localhost) // Peer is not localhost, but only localhost is allowed!
{ else if (XGConfig::access.only_allow_localhost)
denialReason = "Only localhost allowed!"; {
return false; denialReason = "Only localhost allowed!";
} return false;
}
// Let's check if the whitelist is active
else if (XGConfig::access.enable_whitelist) // Let's check if the whitelist is active
{ else if (XGConfig::access.enable_whitelist)
// It is. Let's check if our peer is whitelisted {
for (std::size_t i = 0; i < XGConfig::access.whitelist.size(); i++) // It is. Let's check if our peer is whitelisted
{ for (std::size_t i = 0; i < XGConfig::access.whitelist.size(); i++)
// Whitelist is enabled, and peer is whitelisted {
if (XGConfig::access.whitelist[i] == peer_address) return true; // Whitelist is enabled, and peer is whitelisted
} if (XGConfig::access.whitelist[i] == peer_address) return true;
}
// Whitelist is enabled, but peer is NOT whitelisted
denialReason = std::string("Not whitelisted! Ask your tubio administrator to whitelist '") + peer_address + "' in order to gain access."; // Whitelist is enabled, but peer is NOT whitelisted
return false; denialReason = std::string("Not whitelisted! Ask your tubio administrator to whitelist '") + peer_address + "' in order to gain access.";
} return false;
else // Whitelist is NOT enabled and only_allow_localhost is FALSE! }
{ else // Whitelist is NOT enabled and only_allow_localhost is FALSE!
return true; {
} return true;
}
// Should never come to this point
denialReason = "Access denied"; // Should never come to this point
return false; denialReason = "Access denied";
} return false;
}
void HttpServer::OnExit()
{ void HttpServer::OnExit()
log->cout << "Shutting down http-server..."; {
log->Flush(); log->cout << "Shutting down http-server...";
log->Flush();
mg_mgr_free(pMgr);
mg_mgr_free(pMgr);
return;
} return;
}
std::string HttpServer::FixUnterminatedString(const char* cstr, const std::size_t len)
{ std::string HttpServer::FixUnterminatedString(const char* cstr, const std::size_t len)
std::stringstream ss; {
for (std::size_t i = 0; i < len; i++) std::stringstream ss;
{ for (std::size_t i = 0; i < len; i++)
ss << *(cstr + i); {
} ss << *(cstr + i);
}
return ss.str();
} return ss.str();
}
mg_serve_http_opts HttpServer::frontend_serve_opts;
mg_serve_http_opts HttpServer::frontend_serve_opts;