diff options
author | brain <brain@e03df62e-2008-0410-955e-edbf42e46eb7> | 2008-05-12 20:34:52 +0000 |
---|---|---|
committer | brain <brain@e03df62e-2008-0410-955e-edbf42e46eb7> | 2008-05-12 20:34:52 +0000 |
commit | 0aa899ee2f7d82ea59f4c34fd29abaac29824903 (patch) | |
tree | 93e92c64d6b23677679a7e35326cc5d3f4f698f4 | |
parent | d5207987511ac58cb8e7496128b8811c93c5180e (diff) |
Add HTTP auth
git-svn-id: http://svn.inspircd.org/repository/trunk/inspircd@9713 e03df62e-2008-0410-955e-edbf42e46eb7
-rw-r--r-- | docs/inspircd.conf.example | 2 | ||||
-rw-r--r-- | src/modules/m_httpd_acl.cpp | 154 |
2 files changed, 138 insertions, 18 deletions
diff --git a/docs/inspircd.conf.example b/docs/inspircd.conf.example index b4c49332d..f2287ab7e 100644 --- a/docs/inspircd.conf.example +++ b/docs/inspircd.conf.example @@ -1785,7 +1785,7 @@ # network and when the correct password is specified: # # <httpdacl path="/stats*" types="password,whitelist" -# password="mypasshere" whitelist="127.0.0.*,10.*"> +# username="secretstuff" password="mypasshere" whitelist="127.0.0.*,10.*"> # # Deny all connections to all but the main index page: # diff --git a/src/modules/m_httpd_acl.cpp b/src/modules/m_httpd_acl.cpp index 903e83f26..36eab758b 100644 --- a/src/modules/m_httpd_acl.cpp +++ b/src/modules/m_httpd_acl.cpp @@ -23,13 +23,14 @@ class ACL : public Extensible { public: std::string path; + std::string username; std::string password; std::string whitelist; std::string blacklist; - ACL(const std::string &set_path, const std::string &set_password, + ACL(const std::string &set_path, const std::string &set_username, const std::string &set_password, const std::string &set_whitelist, const std::string &set_blacklist) - : path(set_path), password(set_password), whitelist(set_whitelist), + : path(set_path), username(set_username), password(set_password), whitelist(set_whitelist), blacklist(set_blacklist) { } ~ACL() { } @@ -48,13 +49,14 @@ class ModuleHTTPAccessList : public Module { acl_list.clear(); ConfigReader c(ServerInstance); - int n_items = c.Enumerate("httpacl"); + int n_items = c.Enumerate("httpdacl"); for (int i = 0; i < n_items; ++i) { - std::string path = c.ReadValue("httpacl", "path", i); - std::string types = c.ReadValue("httpacl", "types", i); + std::string path = c.ReadValue("httpdacl", "path", i); + std::string types = c.ReadValue("httpdacl", "types", i); irc::commasepstream sep(types); std::string type; + std::string username; std::string password; std::string whitelist; std::string blacklist; @@ -63,15 +65,16 @@ class ModuleHTTPAccessList : public Module { if (type == "password") { - password = c.ReadValue("httpacl", "password", i); + username = c.ReadValue("httpdacl", "username", i); + password = c.ReadValue("httpdacl", "password", i); } else if (type == "whitelist") { - whitelist = c.ReadValue("httpacl", "whitelist", i); + whitelist = c.ReadValue("httpdacl", "whitelist", i); } else if (type == "blacklist") { - blacklist = c.ReadValue("httpacl", "blacklist", i); + blacklist = c.ReadValue("httpdacl", "blacklist", i); } else { @@ -79,34 +82,95 @@ class ModuleHTTPAccessList : public Module } } - acl_list.push_back(ACL(path, password, whitelist, blacklist)); + ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(), + password.c_str(), whitelist.c_str(), blacklist.c_str()); + + acl_list.push_back(ACL(path, username, password, whitelist, blacklist)); } } ModuleHTTPAccessList(InspIRCd* Me) : Module(Me) { ReadConfig(); - this->changed = true; Implementation eventlist[] = { I_OnEvent, I_OnRequest }; ServerInstance->Modules->Attach(eventlist, this, 2); } - void BlockAccess(HTTPRequest* http, Event* event) + void BlockAccess(HTTPRequest* http, Event* event, int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="") { std::stringstream data("Access to this resource is denied by an access control list. Please contact your IRC administrator."); - HTTPDocument response(http->sock, &data, 403); + HTTPDocument response(http->sock, &data, returnval); response.headers.SetHeader("X-Powered-By", "m_httpd_acl.so"); + if (!extraheaderkey.empty()) + response.headers.SetHeader(extraheaderkey, extraheaderval); Request req((char*)&response, (Module*)this, event->GetSource()); req.Send(); } + bool IsBase64(unsigned char c) + { + return (isalnum(c) || (c == '+') || (c == '/')); + } + + std::string Base64Decode(const std::string &base64) + { + const std::string base64_chars("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); + + int inputlen = base64.length(); + + if (inputlen == 0) + return ""; + + int i = 0, j = 0, input = 0; + + unsigned char longbuf[4], shortbuf[3]; + std::string ret; + + while (inputlen-- && ( base64[input] != '=') && IsBase64(base64[input])) + { + longbuf[i++] = base64[input]; input++; + if (i ==4) + { + for (i = 0; i <4; i++) + longbuf[i] = base64_chars.find(longbuf[i]); + + shortbuf[0] = (longbuf[0] << 2) + ((longbuf[1] & 0x30) >> 4); + shortbuf[1] = ((longbuf[1] & 0xf) << 4) + ((longbuf[2] & 0x3c) >> 2); + shortbuf[2] = ((longbuf[2] & 0x3) << 6) + longbuf[3]; + + for (i = 0; (i < 3); i++) + ret += shortbuf[i]; + + i = 0; + } + } + + if (i) + { + for (j = i; j <4; j++) + longbuf[j] = 0; + + for (j = 0; j <4; j++) + longbuf[j] = base64_chars.find(longbuf[j]); + + shortbuf[0] = (longbuf[0] << 2) + ((longbuf[1] & 0x30) >> 4); + shortbuf[1] = ((longbuf[1] & 0xf) << 4) + ((longbuf[2] & 0x3c) >> 2); + shortbuf[2] = ((longbuf[2] & 0x3) << 6) + longbuf[3]; + + for (j = 0; (j < i - 1); j++) + ret += shortbuf[j]; + } + + return ret; + } + void OnEvent(Event* event) { std::stringstream data(""); - if (event->GetEventID() == "httpd_url") + if (event->GetEventID() == "httpd_acl") { - ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd event"); + ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd acl event"); HTTPRequest* http = (HTTPRequest*)event->GetData(); for (std::vector<ACL>::const_iterator this_acl = acl_list.begin(); this_acl != acl_list.end(); ++this_acl) @@ -123,7 +187,9 @@ class ModuleHTTPAccessList : public Module { if (match(http->GetIP(), entry)) { - BlockAccess(http, event); + ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)", + http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str()); + BlockAccess(http, event, 403); return; } } @@ -143,14 +209,68 @@ class ModuleHTTPAccessList : public Module if (!allow_access) { - BlockAccess(http, event); + ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)", + http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str()); + BlockAccess(http, event, 403); return; } } - if (!this_acl->password.empty()) + if (!this_acl->password.empty() && !this_acl->username.empty()) { /* Password auth, first look to see if we have a basic authentication header */ + ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s", + http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str()); + + if (http->headers->IsSet("Authorization")) + { + /* Password has been given, validate it */ + std::string authorization = http->headers->GetHeader("Authorization"); + irc::spacesepstream sep(authorization); + std::string authtype; + std::string base64; + + sep.GetToken(authtype); + if (authtype == "Basic") + { + std::string user; + std::string pass; + + sep.GetToken(base64); + std::string userpass = Base64Decode(base64); + ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str()); + + irc::sepstream userpasspair(userpass, ':'); + if (userpasspair.GetToken(user)) + { + userpasspair.GetToken(pass); + + /* Access granted if username and password are correct */ + if (user == this_acl->username && pass == this_acl->password) + { + ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: password and username match"); + return; + } + else + /* Invalid password */ + BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\""); + } + else + /* Malformed user:pass pair */ + BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\""); + } + else + /* Unsupported authentication type */ + BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\""); + } + else + { + /* No password given at all, access denied */ + BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\""); + } } + + /* A path may only match one ACL (the first it finds in the config file) */ + return; } } } |