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