00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "inspircd.h"
00015 #include "httpd.h"
00016 #include "protocol.h"
00017
00018
00019
00020
00021 class HTTPACL : public Extensible
00022 {
00023 public:
00024 std::string path;
00025 std::string username;
00026 std::string password;
00027 std::string whitelist;
00028 std::string blacklist;
00029
00030 HTTPACL(const std::string &set_path, const std::string &set_username, const std::string &set_password,
00031 const std::string &set_whitelist, const std::string &set_blacklist)
00032 : path(set_path), username(set_username), password(set_password), whitelist(set_whitelist),
00033 blacklist(set_blacklist) { }
00034
00035 ~HTTPACL() { }
00036 };
00037
00038 class ModuleHTTPAccessList : public Module
00039 {
00040
00041 std::string stylesheet;
00042 bool changed;
00043 std::vector<HTTPACL> acl_list;
00044
00045 public:
00046
00047 void ReadConfig()
00048 {
00049 acl_list.clear();
00050 ConfigReader c(ServerInstance);
00051 int n_items = c.Enumerate("httpdacl");
00052 for (int i = 0; i < n_items; ++i)
00053 {
00054 std::string path = c.ReadValue("httpdacl", "path", i);
00055 std::string types = c.ReadValue("httpdacl", "types", i);
00056 irc::commasepstream sep(types);
00057 std::string type;
00058 std::string username;
00059 std::string password;
00060 std::string whitelist;
00061 std::string blacklist;
00062
00063 while (sep.GetToken(type))
00064 {
00065 if (type == "password")
00066 {
00067 username = c.ReadValue("httpdacl", "username", i);
00068 password = c.ReadValue("httpdacl", "password", i);
00069 }
00070 else if (type == "whitelist")
00071 {
00072 whitelist = c.ReadValue("httpdacl", "whitelist", i);
00073 }
00074 else if (type == "blacklist")
00075 {
00076 blacklist = c.ReadValue("httpdacl", "blacklist", i);
00077 }
00078 else
00079 {
00080 throw ModuleException("Invalid HTTP ACL type '" + type + "'");
00081 }
00082 }
00083
00084 ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Read ACL: path=%s pass=%s whitelist=%s blacklist=%s", path.c_str(),
00085 password.c_str(), whitelist.c_str(), blacklist.c_str());
00086
00087 acl_list.push_back(HTTPACL(path, username, password, whitelist, blacklist));
00088 }
00089 }
00090
00091 ModuleHTTPAccessList(InspIRCd* Me) : Module(Me)
00092 {
00093 ReadConfig();
00094 Implementation eventlist[] = { I_OnEvent, I_OnRequest };
00095 ServerInstance->Modules->Attach(eventlist, this, 2);
00096 }
00097
00098 void BlockAccess(HTTPRequest* http, Event* event, int returnval, const std::string &extraheaderkey = "", const std::string &extraheaderval="")
00099 {
00100 ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "BlockAccess (%d)", returnval);
00101
00102 std::stringstream data("Access to this resource is denied by an access control list. Please contact your IRC administrator.");
00103 HTTPDocument response(http->sock, &data, returnval);
00104 response.headers.SetHeader("X-Powered-By", "m_httpd_acl.so");
00105 if (!extraheaderkey.empty())
00106 response.headers.SetHeader(extraheaderkey, extraheaderval);
00107 Request req((char*)&response, (Module*)this, event->GetSource());
00108 req.Send();
00109 }
00110
00111 bool IsBase64(unsigned char c)
00112 {
00113 return (isalnum(c) || (c == '+') || (c == '/'));
00114 }
00115
00116 std::string Base64Decode(const std::string &base64)
00117 {
00118 const std::string base64_chars("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
00119 int inputlen = base64.length();
00120 int i = 0, j = 0, input = 0;
00121 unsigned char longbuf[4], shortbuf[3];
00122 std::string retval;
00123
00124 if (inputlen == 0)
00125 return "";
00126
00127 while (inputlen-- && (base64[input] != '=') && IsBase64(base64[input]))
00128 {
00129 longbuf[i++] = base64[input];
00130 input++;
00131 if (i == 4)
00132 {
00133 for (i = 0; i < 4; ++i)
00134 longbuf[i] = base64_chars.find(longbuf[i]);
00135
00136 shortbuf[0] = (longbuf[0] << 2) + ((longbuf[1] & 0x30) >> 4);
00137 shortbuf[1] = ((longbuf[1] & 0xf) << 4) + ((longbuf[2] & 0x3c) >> 2);
00138 shortbuf[2] = ((longbuf[2] & 0x3) << 6) + longbuf[3];
00139
00140 for (i = 0; i < 3; ++i)
00141 retval += shortbuf[i];
00142
00143 i = 0;
00144 }
00145 }
00146
00147 if (i)
00148 {
00149 for (j = i; j < 4; ++j)
00150 longbuf[j] = 0;
00151
00152 for (j = 0; j < 4; ++j)
00153 longbuf[j] = base64_chars.find(longbuf[j]);
00154
00155 shortbuf[0] = (longbuf[0] << 2) + ((longbuf[1] & 0x30) >> 4);
00156 shortbuf[1] = ((longbuf[1] & 0xf) << 4) + ((longbuf[2] & 0x3c) >> 2);
00157 shortbuf[2] = ((longbuf[2] & 0x3) << 6) + longbuf[3];
00158
00159 for (j = 0; j < i - 1; ++j)
00160 retval += shortbuf[j];
00161 }
00162
00163 return retval;
00164 }
00165
00166 void OnEvent(Event* event)
00167 {
00168 if (event->GetEventID() == "httpd_acl")
00169 {
00170 ServerInstance->Logs->Log("m_http_stats", DEBUG,"Handling httpd acl event");
00171 HTTPRequest* http = (HTTPRequest*)event->GetData();
00172
00173 for (std::vector<HTTPACL>::const_iterator this_acl = acl_list.begin(); this_acl != acl_list.end(); ++this_acl)
00174 {
00175 if (InspIRCd::Match(http->GetURI(), this_acl->path))
00176 {
00177 if (!this_acl->blacklist.empty())
00178 {
00179
00180 irc::commasepstream sep(this_acl->blacklist);
00181 std::string entry;
00182
00183 while (sep.GetToken(entry))
00184 {
00185 if (InspIRCd::Match(http->GetIP(), entry))
00186 {
00187 ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to blacklisted resource %s (matched by pattern %s) from ip %s (matched by entry %s)",
00188 http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), entry.c_str());
00189 BlockAccess(http, event, 403);
00190 return;
00191 }
00192 }
00193 }
00194 if (!this_acl->whitelist.empty())
00195 {
00196
00197 irc::commasepstream sep(this_acl->whitelist);
00198 std::string entry;
00199 bool allow_access = false;
00200
00201 while (sep.GetToken(entry))
00202 {
00203 if (InspIRCd::Match(http->GetIP(), entry))
00204 allow_access = true;
00205 }
00206
00207 if (!allow_access)
00208 {
00209 ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Denying access to whitelisted resource %s (matched by pattern %s) from ip %s (Not in whitelist)",
00210 http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str());
00211 BlockAccess(http, event, 403);
00212 return;
00213 }
00214 }
00215 if (!this_acl->password.empty() && !this_acl->username.empty())
00216 {
00217
00218 ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "Checking HTTP auth password for resource %s (matched by pattern %s) from ip %s, against username %s",
00219 http->GetURI().c_str(), this_acl->path.c_str(), http->GetIP().c_str(), this_acl->username.c_str());
00220
00221 if (http->headers->IsSet("Authorization"))
00222 {
00223
00224 std::string authorization = http->headers->GetHeader("Authorization");
00225 irc::spacesepstream sep(authorization);
00226 std::string authtype;
00227 std::string base64;
00228
00229 sep.GetToken(authtype);
00230 if (authtype == "Basic")
00231 {
00232 std::string user;
00233 std::string pass;
00234
00235 sep.GetToken(base64);
00236 std::string userpass = Base64Decode(base64);
00237 ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: %s (%s)", userpass.c_str(), base64.c_str());
00238
00239 irc::sepstream userpasspair(userpass, ':');
00240 if (userpasspair.GetToken(user))
00241 {
00242 userpasspair.GetToken(pass);
00243
00244
00245 if (user == this_acl->username && pass == this_acl->password)
00246 {
00247 ServerInstance->Logs->Log("m_httpd_acl", DEBUG, "HTTP authorization: password and username match");
00248 return;
00249 }
00250 else
00251
00252 BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\"");
00253 }
00254 else
00255
00256 BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\"");
00257 }
00258 else
00259
00260 BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\"");
00261 }
00262 else
00263 {
00264
00265 BlockAccess(http, event, 401, "WWW-Authenticate", "Basic realm=\"Restricted Object\"");
00266 }
00267 }
00268
00269
00270 return;
00271 }
00272 }
00273 }
00274 }
00275
00276 const char* OnRequest(Request* request)
00277 {
00278 return NULL;
00279 }
00280
00281 virtual ~ModuleHTTPAccessList()
00282 {
00283 }
00284
00285 virtual Version GetVersion()
00286 {
00287 return Version("$Id: m_httpd_acl.cpp 10291 2008-08-25 20:35:51Z w00t $", VF_VENDOR, API_VERSION);
00288 }
00289 };
00290
00291 MODULE_INIT(ModuleHTTPAccessList)