1 /*
2 * (C) 2003-2020 Anope Team
3 * Contact us at team@anope.org
4 *
5 * Please read COPYING and README for further details.
6 */
7
8 #include "../webcpanel.h"
9
10 class WebpanelRequest : public IdentifyRequest
11 {
12 HTTPReply reply;
13 HTTPMessage message;
14 Reference<HTTPProvider> server;
15 Anope::string page_name;
16 Reference<HTTPClient> client;
17 TemplateFileServer::Replacements replacements;
18
19 public:
WebpanelRequest(Module * o,HTTPReply & r,HTTPMessage & m,HTTPProvider * s,const Anope::string & p_n,HTTPClient * c,TemplateFileServer::Replacements & re,const Anope::string & user,const Anope::string & pass)20 WebpanelRequest(Module *o, HTTPReply &r, HTTPMessage &m, HTTPProvider *s, const Anope::string &p_n, HTTPClient *c, TemplateFileServer::Replacements &re, const Anope::string &user, const Anope::string &pass) : IdentifyRequest(o, user, pass), reply(r), message(m), server(s), page_name(p_n), client(c), replacements(re) { }
21
OnSuccess()22 void OnSuccess() anope_override
23 {
24 if (!client || !server)
25 return;
26 NickAlias *na = NickAlias::Find(this->GetAccount());
27 if (!na)
28 {
29 this->OnFail();
30 return;
31 }
32
33 if (na->nc->HasExt("NS_SUSPENDED"))
34 {
35 this->OnFail();
36 return;
37 }
38
39 // Rate limit logins to 1/sec
40 time_t *last_login = na->nc->GetExt<time_t>("webcpanel_last_login");
41 if (last_login != NULL && Anope::CurTime == *last_login)
42 {
43 this->OnFail();
44 return;
45 }
46
47 Anope::string id;
48 for (int i = 0; i < 64; ++i)
49 {
50 char c;
51 do
52 c = 48 + (rand() % 75);
53 while (!isalnum(c));
54 id += c;
55 }
56
57 na->Extend<Anope::string>("webcpanel_id", id);
58 na->Extend<Anope::string>("webcpanel_ip", client->GetIP());
59 na->nc->Extend<time_t>("webcpanel_last_login", Anope::CurTime);
60
61 {
62 HTTPReply::cookie c;
63 c.push_back(std::make_pair("account", na->nick));
64 c.push_back(std::make_pair("Path", "/"));
65 reply.cookies.push_back(c);
66 }
67
68 {
69 HTTPReply::cookie c;
70 c.push_back(std::make_pair("id", id));
71 c.push_back(std::make_pair("Path", "/"));
72 reply.cookies.push_back(c);
73 }
74
75 reply.error = HTTP_FOUND;
76 reply.headers["Location"] = Anope::string("http") + (server->IsSSL() ? "s" : "") + "://" + message.headers["Host"] + "/nickserv/info";
77
78 client->SendReply(&reply);
79 }
80
OnFail()81 void OnFail() anope_override
82 {
83 if (!client || !server)
84 return;
85 replacements["INVALID_LOGIN"] = "Invalid username or password";
86 TemplateFileServer page("login.html");
87 page.Serve(server, page_name, client, message, reply, replacements);
88
89 client->SendReply(&reply);
90 }
91 };
92
OnRequest(HTTPProvider * server,const Anope::string & page_name,HTTPClient * client,HTTPMessage & message,HTTPReply & reply)93 bool WebCPanel::Index::OnRequest(HTTPProvider *server, const Anope::string &page_name, HTTPClient *client, HTTPMessage &message, HTTPReply &reply)
94 {
95 TemplateFileServer::Replacements replacements;
96 const Anope::string &user = message.post_data["username"], &pass = message.post_data["password"];
97
98 replacements["TITLE"] = page_title;
99
100 if (!user.empty() && !pass.empty())
101 {
102 // Rate limit check.
103 Anope::string ip = client->clientaddr.addr();
104
105 Anope::hash_map<time_t>::iterator it = last_login_attempt.find(ip);
106 if (it != last_login_attempt.end())
107 {
108 time_t last_time = it->second;
109
110 if (last_time == Anope::CurTime)
111 {
112 replacements["INVALID_LOGIN"] = "Rate limited";
113 TemplateFileServer page("login.html");
114 page.Serve(server, page_name, client, message, reply, replacements);
115 return true;
116 }
117 }
118
119 // don't let ip hash grow too long
120 if (Anope::CurTime > last_clear + FLUSH_TIME)
121 {
122 last_login_attempt.clear();
123 last_clear = Anope::CurTime;
124 }
125
126 last_login_attempt[ip] = Anope::CurTime;
127
128 WebpanelRequest *req = new WebpanelRequest(me, reply, message, server, page_name, client, replacements, user, pass);
129 FOREACH_MOD(OnCheckAuthentication, (NULL, req));
130 req->Dispatch();
131 return false;
132 }
133
134 TemplateFileServer page("login.html");
135 page.Serve(server, page_name, client, message, reply, replacements);
136 return true;
137 }
138