diff options
-rw-r--r-- | modules/webcpanel/pages/index.cpp | 33 | ||||
-rw-r--r-- | modules/webcpanel/pages/index.h | 7 | ||||
-rw-r--r-- | modules/webcpanel/webcpanel.cpp | 4 |
3 files changed, 42 insertions, 2 deletions
diff --git a/modules/webcpanel/pages/index.cpp b/modules/webcpanel/pages/index.cpp index 8fdd11352..227a348fd 100644 --- a/modules/webcpanel/pages/index.cpp +++ b/modules/webcpanel/pages/index.cpp @@ -36,6 +36,14 @@ class WebpanelRequest : public IdentifyRequest return; } + // Rate limit logins to 1/sec + time_t *last_login = na->nc->GetExt<time_t>("webcpanel_last_login"); + if (last_login != NULL && Anope::CurTime == *last_login) + { + this->OnFail(); + return; + } + Anope::string id; for (int i = 0; i < 64; ++i) { @@ -48,6 +56,7 @@ class WebpanelRequest : public IdentifyRequest na->Extend<Anope::string>("webcpanel_id", id); na->Extend<Anope::string>("webcpanel_ip", client->GetIP()); + na->nc->Extend<time_t>("webcpanel_last_login", Anope::CurTime); { HTTPReply::cookie c; @@ -91,6 +100,30 @@ bool WebCPanel::Index::OnRequest(HTTPProvider *server, const Anope::string &page if (!user.empty() && !pass.empty()) { // Rate limit check. + Anope::string ip = client->clientaddr.addr(); + + Anope::hash_map<time_t>::iterator it = last_login_attempt.find(ip); + if (it != last_login_attempt.end()) + { + time_t last_time = it->second; + + if (last_time == Anope::CurTime) + { + replacements["INVALID_LOGIN"] = "Rate limited"; + TemplateFileServer page("login.html"); + page.Serve(server, page_name, client, message, reply, replacements); + return true; + } + } + + // don't let ip hash grow too long + if (Anope::CurTime > last_clear + FLUSH_TIME) + { + last_login_attempt.clear(); + last_clear = Anope::CurTime; + } + + last_login_attempt[ip] = Anope::CurTime; WebpanelRequest *req = new WebpanelRequest(me, reply, message, server, page_name, client, replacements, user, pass); FOREACH_MOD(OnCheckAuthentication, (NULL, req)); diff --git a/modules/webcpanel/pages/index.h b/modules/webcpanel/pages/index.h index 76e3d12a1..771cbf8fc 100644 --- a/modules/webcpanel/pages/index.h +++ b/modules/webcpanel/pages/index.h @@ -12,8 +12,13 @@ namespace WebCPanel class Index : public WebPanelPage { + static const int FLUSH_TIME = 60; + + Anope::hash_map<time_t> last_login_attempt; + time_t last_clear; + public: - Index(const Anope::string &u) : WebPanelPage(u) { } + Index(const Anope::string &u) : WebPanelPage(u), last_clear(0) { } bool OnRequest(HTTPProvider *, const Anope::string &, HTTPClient *, HTTPMessage &, HTTPReply &) anope_override; }; diff --git a/modules/webcpanel/webcpanel.cpp b/modules/webcpanel/webcpanel.cpp index 14080191e..aa65b4f6c 100644 --- a/modules/webcpanel/webcpanel.cpp +++ b/modules/webcpanel/webcpanel.cpp @@ -15,6 +15,7 @@ class ModuleWebCPanel : public Module ServiceReference<HTTPProvider> provider; Panel panel; PrimitiveExtensibleItem<Anope::string> id, ip; + PrimitiveExtensibleItem<time_t> last_login; StaticFileServer style_css, logo_png, cubes_png, favicon_ico; @@ -44,7 +45,8 @@ class ModuleWebCPanel : public Module public: ModuleWebCPanel(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, EXTRA | VENDOR), - panel(this, "webcpanel"), id(this, "webcpanel_id"), ip(this, "webcpanel_ip"), + panel(this, "webcpanel"), + id(this, "webcpanel_id"), ip(this, "webcpanel_ip"), last_login(this, "webcpanel_last_login"), style_css("style.css", "/static/style.css", "text/css"), logo_png("logo.png", "/static/logo.png", "image/png"), cubes_png("cubes.png", "/static/cubes.png", "image/png"), favicon_ico("favicon.ico", "/favicon.ico", "image/x-icon"), index("/"), logout("/logout"), _register("/register"), confirm("/confirm"), nickserv_info("NickServ", "/nickserv/info"), nickserv_cert("NickServ", "/nickserv/cert"), nickserv_access("NickServ", "/nickserv/access"), nickserv_alist("NickServ", "/nickserv/alist"), |