// globals
namespace {
- static const XMLCh name[] = { chLatin_h, chLatin_o, chLatin_s, chLatin_t, chNull };
+ static const XMLCh name[] = { chLatin_n, chLatin_a, chLatin_m, chLatin_e, chNull };
static const XMLCh port[] = { chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
static const XMLCh scheme[] = { chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_e, chNull };
static const XMLCh id[] = { chLatin_i, chLatin_d, chNull };
{
if (!pVer)
return FALSE;
+ else if (g_Config) {
+ LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
+ "Reentrant filter initialization, ignoring...");
+ return TRUE;
+ }
+#ifndef _DEBUG
try
{
+#endif
LPCSTR schemadir=getenv("SHIBSCHEMAS");
if (!schemadir)
schemadir=SHIB_SCHEMAS;
ShibTargetConfig::Metadata |
ShibTargetConfig::AAP |
ShibTargetConfig::RequestMapper |
- ShibTargetConfig::SHIREExtensions |
+ ShibTargetConfig::LocalExtensions |
ShibTargetConfig::Logging
);
if (!g_Config->init(schemadir,config)) {
// Access the implementation-specifics for site mappings.
IConfig* conf=g_Config->getINI();
Locker locker(conf);
- const IPropertySet* props=conf->getPropertySet("SHIRE");
+ const IPropertySet* props=conf->getPropertySet("Local");
if (props) {
const DOMElement* impl=saml::XML::getFirstChildElement(
props->getElement(),ShibTargetConfig::SHIBTARGET_NS,Implementation
}
}
}
+#ifndef _DEBUG
}
catch (...)
{
LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Filter startup failed with an exception.");
return FALSE;
}
+#endif
pVer->dwFilterVersion=HTTP_FILTER_REVISION;
strncpy(pVer->lpszFilterDesc,"Shibboleth ISAPI Filter",SF_MAX_FILTER_DESC_LEN);
throw ERROR_NO_DATA;
}
-IRequestMapper::Settings map_request(
- PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, IRequestMapper* mapper, const site_t& site, string& target
- )
+/****************************************************************************/
+// ISAPI Filter
+
+class ShibTargetIsapiF : public ShibTarget
{
+public:
+ ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn,
+ const site_t& site) {
+
// URL path always come from IIS.
dynabuf url(256);
GetHeader(pn,pfc,"url",url,256,false);
const char* scheme=site.m_scheme.c_str();
if (!scheme || !*scheme || !g_bNormalizeRequest)
scheme=pfc->fIsSecurePort ? "https" : "http";
-
- // Start with path.
- if (!url.empty())
- target=static_cast<char*>(url);
-
- // If port is non-default, prepend it.
- if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
- target = ':' + static_cast<char*>(port) + target;
- if (g_bNormalizeRequest) {
- target = string(scheme) + "://" + site.m_name + target;
- }
- else {
- dynabuf name(64);
- GetServerVariable(pfc,"SERVER_NAME",name,64);
- target = string(scheme) + "://" + static_cast<char*>(name) + target;
+ // Get the remote address
+ dynabuf remote_addr(16);
+ GetServerVariable(pfc,"REMOTE_ADDR",remote_addr,16);
+
+ // XXX: How do I get the content type and HTTP Method from this context?
+
+ // TODO: Need to allow for use of SERVER_NAME
+
+ init(g_Config, string(scheme), site.m_name, atoi(port),
+ string(url), string(""), // XXX: content type
+ string(remote_addr), string("") // XXX: http method
+ );
+
+ m_pfc = pfc;
+ m_pn = pn;
+ }
+ ~ShibTargetIsapiF() { }
+
+ virtual void log(ShibLogLevel level, const string &msg) {
+ LogEvent(NULL, (level == LogLevelDebug ? EVENTLOG_INFORMATION_TYPE :
+ (level == LogLevelInfo ? EVENTLOG_INFORMATION_TYPE :
+ (level == LogLevelWarn ? EVENTLOG_WARNING_TYPE : EVENTLOG_ERROR_TYPE))),
+ 2100, NULL, msg.c_str());
+ }
+ virtual string getCookies(void) {
+ dynabuf buf(128);
+ GetHeader(m_pn, m_pfc, "Cookie:", buf, 128, false);
+ return buf.empty() ? "" : buf;
+ }
+
+ virtual void clearHeader(const string &name) {
+ string hdr = name + ":";
+ m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()), "");
+ }
+ virtual void setHeader(const string &name, const string &value) {
+ string hdr = name + ":";
+ m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()),
+ const_cast<char*>(value.c_str()));
+ }
+ virtual string getHeader(const string &name) {
+ string hdr = name + ":";
+ dynabuf buf(1024);
+ GetHeader(m_pn, m_pfc, const_cast<char*>(hdr.c_str()), buf, 1024, false);
+ return string(buf);
+ }
+ virtual void setRemoteUser(const string &user) {
+ setHeader(string("remote-user"), user);
+ }
+ virtual string getRemoteUser(void) {
+ return getHeader(string("remote-user"));
+ }
+ virtual void* sendPage(const string &msg, const string content_type,
+ const Iterator<header_t>& headers=EMPTY(header_t), int code=200) {
+ string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n";
+ while (headers.hasNext()) {
+ const header_t& h=headers.next();
+ hdr += h.first + ": " + h.second + "\r\n";
}
- return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
-}
+ hdr += "\r\n";
+ // XXX Need to handle "code"
+ m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, "200 OK", (DWORD)hdr.c_str(), 0);
+ DWORD resplen = msg.size();
+ m_pfc->WriteClient(m_pfc, (LPVOID)msg.c_str(), &resplen, 0);
+ return (void*)SF_STATUS_REQ_FINISHED;
+ }
+ virtual void* sendRedirect(const string url) {
+ // XXX: Don't support the httpRedirect option, yet.
+ string hdrs=string("Location: ") + url + "\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 40\r\n"
+ "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
+ "Cache-Control: private,no-store,no-cache\r\n\r\n";
+ m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER,
+ "302 Please Wait", (DWORD)hdrs.c_str(), 0);
+ static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
+ DWORD resplen=40;
+ m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0);
+ return reinterpret_cast<void*>(SF_STATUS_REQ_FINISHED);
+ }
+ // XXX: We might not ever hit the 'decline' status in this filter.
+ //virtual void* returnDecline(void) { }
+ virtual void* returnOK(void) { return (void*) SF_STATUS_REQ_NEXT_NOTIFICATION; }
+
+ // The filter never processes the POST, so stub these methods.
+ virtual void setCookie(const string &name, const string &value) { throw runtime_error("setCookie not implemented"); }
+ virtual string getArgs(void) { throw runtime_error("getArgs not implemented"); }
+ virtual string getPostData(void) { throw runtime_error("getPostData not implemented"); }
+
+ PHTTP_FILTER_CONTEXT m_pfc;
+ PHTTP_FILTER_PREPROC_HEADERS m_pn;
+};
DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg)
{
LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
- static const char* ctype="Content-Type: text/html\r\n";
- pfc->AddResponseHeaders(pfc,const_cast<char*>(ctype),0);
- pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",0,0);
+ static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
+ pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0);
static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Filter Error</TITLE></HEAD><BODY>"
"<H1>Shibboleth Filter Error</H1>";
DWORD resplen=strlen(xmsg);
return SF_STATUS_REQ_FINISHED;
}
+extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
+{
+ // Is this a log notification?
+ if (notificationType==SF_NOTIFY_LOG)
+ {
+ if (pfc->pFilterContext)
+ ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=static_cast<LPCSTR>(pfc->pFilterContext);
+ return SF_STATUS_REQ_NEXT_NOTIFICATION;
+ }
+
+ PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification;
+ try
+ {
+ // Determine web site number. This can't really fail, I don't think.
+ dynabuf buf(128);
+ GetServerVariable(pfc,"INSTANCE_ID",buf,10);
+
+ // Match site instance to host name, skip if no match.
+ map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
+ if (map_i==g_Sites.end())
+ return SF_STATUS_REQ_NEXT_NOTIFICATION;
+
+ ostringstream threadid;
+ threadid << "[" << getpid() << "] isapi_shib" << '\0';
+ saml::NDC ndc(threadid.str().c_str());
+
+ ShibTargetIsapiF stf(pfc, pn, map_i->second);
+
+ // "false" because we don't override the Shib settings
+ pair<bool,void*> res = stf.doCheckAuthN();
+ if (res.first) return (DWORD)res.second;
+
+ // "false" because we don't override the Shib settings
+ res = stf.doExportAssertions();
+ if (res.first) return (DWORD)res.second;
+
+ res = stf.doCheckAuthZ();
+ if (res.first) return (DWORD)res.second;
+
+ return SF_STATUS_REQ_NEXT_NOTIFICATION;
+ }
+ catch(bad_alloc) {
+ return WriteClientError(pfc,"Out of Memory");
+ }
+ catch(DWORD e) {
+ if (e==ERROR_NO_DATA)
+ return WriteClientError(pfc,"A required variable or header was empty.");
+ else
+ return WriteClientError(pfc,"Server detected unexpected IIS error.");
+ }
+#ifndef _DEBUG
+ catch(...) {
+ return WriteClientError(pfc,"Server caught an unknown exception.");
+ }
+#endif
+
+ return WriteClientError(pfc,"Server reached unreachable code, save my walrus!");
+}
+
+
+#if 0
+IRequestMapper::Settings map_request(
+ PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, IRequestMapper* mapper, const site_t& site, string& target
+ )
+{
+ // URL path always come from IIS.
+ dynabuf url(256);
+ GetHeader(pn,pfc,"url",url,256,false);
+
+ // Port may come from IIS or from site def.
+ dynabuf port(11);
+ if (site.m_port.empty() || !g_bNormalizeRequest)
+ GetServerVariable(pfc,"SERVER_PORT",port,10);
+ else {
+ strncpy(port,site.m_port.c_str(),10);
+ static_cast<char*>(port)[10]=0;
+ }
+
+ // Scheme may come from site def or be derived from IIS.
+ const char* scheme=site.m_scheme.c_str();
+ if (!scheme || !*scheme || !g_bNormalizeRequest)
+ scheme=pfc->fIsSecurePort ? "https" : "http";
+
+ // Start with scheme and hostname.
+ if (g_bNormalizeRequest) {
+ target = string(scheme) + "://" + site.m_name;
+ }
+ else {
+ dynabuf name(64);
+ GetServerVariable(pfc,"SERVER_NAME",name,64);
+ target = string(scheme) + "://" + static_cast<char*>(name);
+ }
+
+ // If port is non-default, append it.
+ if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
+ target = target + ':' + static_cast<char*>(port);
+
+ // Append path.
+ if (!url.empty())
+ target+=static_cast<char*>(url);
+
+ return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
+}
+
DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const IApplication* app, const char* page, ShibMLP& mlp)
{
const IPropertySet* props=app->getPropertySet("Errors");
if (!infile.fail()) {
const char* res = mlp.run(infile,props);
if (res) {
- static const char* ctype="Content-Type: text/html\r\n";
- pfc->AddResponseHeaders(pfc,const_cast<char*>(ctype),0);
- pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",0,0);
+ static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
+ pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0);
DWORD resplen=strlen(res);
pfc->WriteClient(pfc,(LPVOID)res,&resplen,0);
return SF_STATUS_REQ_FINISHED;
return WriteClientError(pfc,"Unable to open error template, check settings.");
}
+DWORD WriteRedirectPage(PHTTP_FILTER_CONTEXT pfc, const IApplication* app, const char* file, ShibMLP& mlp, const char* headers=NULL)
+{
+ ifstream infile(file);
+ if (!infile.fail()) {
+ const char* res = mlp.run(infile,app->getPropertySet("Errors"));
+ if (res) {
+ char buf[255];
+ sprintf(buf,"Content-Length: %u\r\nContent-Type: text/html\r\n\r\n",strlen(res));
+ if (headers) {
+ string h(headers);
+ h+=buf;
+ pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)h.c_str(),0);
+ }
+ else
+ pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)buf,0);
+ DWORD resplen=strlen(res);
+ pfc->WriteClient(pfc,(LPVOID)res,&resplen,0);
+ return SF_STATUS_REQ_FINISHED;
+ }
+ }
+ LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open redirect template.");
+ return WriteClientError(pfc,"Unable to open redirect template, check settings.");
+}
+
extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
{
// Is this a log notification?
// Declare SHIRE object for this request.
SHIRE shire(application);
+
+ const char* shireURL=shire.getShireURL(targeturl.c_str());
+ if (!shireURL)
+ return WriteClientError(pfc,"Unable to map request to proper shireURL setting, check configuration.");
// If the user is accessing the SHIRE acceptance point, pass it on.
- if (targeturl.find(shire.getShireURL(targeturl.c_str()))!=string::npos)
+ if (targeturl.find(shireURL)!=string::npos)
return SF_STATUS_REQ_NEXT_NOTIFICATION;
// Now check the policy for this request.
pair<bool,bool> requireSession=settings.first->getBool("requireSession");
pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
+ pair<bool,bool> httpRedirects=application->getPropertySet("Sessions")->getBool("httpRedirects");
+ pair<bool,const char*> redirectPage=application->getPropertySet("Sessions")->getString("redirectPage");
+ if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
+ return WriteClientError(pfc,"HTML-based redirection requires a redirectPage property.");
// Check for session cookie.
const char* session_id=NULL;
return SF_STATUS_REQ_NEXT_NOTIFICATION;
// No acceptable cookie, and we require a session. Generate an AuthnRequest.
- string loc("Location: ");
- loc+=shire.getAuthnRequest(targeturl.c_str());
- pfc->AddResponseHeaders(pfc,const_cast<char*>(loc.c_str()),0);
- pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",0,0);
- return SF_STATUS_REQ_FINISHED;
+ const char* areq = shire.getAuthnRequest(targeturl.c_str());
+ if (!httpRedirects.first || httpRedirects.second) {
+ string hdrs=string("Location: ") + areq + "\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 40\r\n"
+ "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
+ "Cache-Control: private,no-store,no-cache\r\n\r\n";
+ pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",(DWORD)hdrs.c_str(),0);
+ static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
+ DWORD resplen=40;
+ pfc->WriteClient(pfc,(LPVOID)redmsg,&resplen,0);
+ return SF_STATUS_REQ_FINISHED;
+ }
+ else {
+ ShibMLP markupProcessor;
+ markupProcessor.insert("requestURL",areq);
+ return WriteRedirectPage(pfc, application, redirectPage.second, markupProcessor);
+ }
}
// Make sure this session is still valid.
else if (status->isRetryable()) {
// Oops, session is invalid. Generate AuthnRequest.
delete status;
- string loc("Location: ");
- loc+=shire.getAuthnRequest(targeturl.c_str());
- pfc->AddResponseHeaders(pfc,const_cast<char*>(loc.c_str()),0);
- pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",0,0);
- return SF_STATUS_REQ_FINISHED;
+ const char* areq = shire.getAuthnRequest(targeturl.c_str());
+ if (!httpRedirects.first || httpRedirects.second) {
+ string hdrs=string("Location: ") + areq + "\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 40\r\n"
+ "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
+ "Cache-Control: private,no-store,no-cache\r\n\r\n";
+ pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",(DWORD)hdrs.c_str(),0);
+ static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
+ DWORD resplen=40;
+ pfc->WriteClient(pfc,(LPVOID)redmsg,&resplen,0);
+ return SF_STATUS_REQ_FINISHED;
+ }
+ else {
+ markupProcessor.insert("requestURL",areq);
+ return WriteRedirectPage(pfc, application, redirectPage.second, markupProcessor);
+ }
}
else {
// return the error page to the user
return WriteClientError(pfc,"Server reached unreachable code, save my walrus!");
}
+#endif // 0
+
+/****************************************************************************/
+// ISAPI Extension
+
+DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg)
+{
+ LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
+ static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
+ lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
+ static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Error</TITLE></HEAD><BODY><H1>Shibboleth Error</H1>";
+ DWORD resplen=strlen(xmsg);
+ lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC);
+ resplen=strlen(msg);
+ lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC);
+ static const char* xmsg2="</BODY></HTML>";
+ resplen=strlen(xmsg2);
+ lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC);
+ return HSE_STATUS_SUCCESS;
+}
+
+
+class ShibTargetIsapiE : public ShibTarget
+{
+public:
+ ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site)
+ {
+ dynabuf ssl(5);
+ GetServerVariable(lpECB,"HTTPS",ssl,5);
+ bool SSL=(ssl=="on" || ssl=="ON");
+
+ // URL path always come from IIS.
+ dynabuf url(256);
+ GetServerVariable(lpECB,"URL",url,255);
+
+ // Port may come from IIS or from site def.
+ dynabuf port(11);
+ if (site.m_port.empty() || !g_bNormalizeRequest)
+ GetServerVariable(lpECB,"SERVER_PORT",port,10);
+ else {
+ strncpy(port,site.m_port.c_str(),10);
+ static_cast<char*>(port)[10]=0;
+ }
+
+ // Scheme may come from site def or be derived from IIS.
+ const char* scheme=site.m_scheme.c_str();
+ if (!scheme || !*scheme || !g_bNormalizeRequest) {
+ scheme = SSL ? "https" : "http";
+ }
+
+ // Get the remote address
+ dynabuf remote_addr(16);
+ GetServerVariable(lpECB, "REMOTE_ADDR", remote_addr, 16);
+
+ init(g_Config, string(scheme), site.m_name, atoi(port),
+ string(url), string(lpECB->lpszContentType ? lpECB->lpszContentType : ""),
+ string(remote_addr), string(lpECB->lpszMethod)
+ );
+
+ m_lpECB = lpECB;
+ }
+ ~ShibTargetIsapiE() { }
+
+ virtual void log(ShibLogLevel level, const string &msg) {
+ LogEvent(NULL, (level == LogLevelDebug ? EVENTLOG_INFORMATION_TYPE :
+ (level == LogLevelInfo ? EVENTLOG_INFORMATION_TYPE :
+ (level == LogLevelWarn ? EVENTLOG_WARNING_TYPE : EVENTLOG_ERROR_TYPE))),
+ 2100, NULL, msg.c_str());
+ }
+ virtual void setCookie(const string &name, const string &value) {
+ // Set the cookie for later. Use it during the redirect.
+ m_cookie += "Set-Cookie: " + name + "=" + value + "\r\n";
+ }
+ virtual string getArgs(void) {
+ return string(m_lpECB->lpszQueryString ? m_lpECB->lpszQueryString : "");
+ }
+ virtual string getPostData(void) {
+ if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB?
+ throw ShibTargetException(SHIBRPC_OK,
+ "blocked too-large a post to SHIRE POST processor");
+ else if (m_lpECB->cbTotalBytes != m_lpECB->cbAvailable) {
+ string cgistr;
+ char buf[8192];
+ DWORD datalen=m_lpECB->cbTotalBytes;
+ while (datalen) {
+ DWORD buflen=8192;
+ BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen);
+ if (!ret || !buflen)
+ throw ShibTargetException(SHIBRPC_OK,
+ "error reading POST data from browser");
+ cgistr.append(buf, buflen);
+ datalen-=buflen;
+ }
+ return cgistr;
+ }
+ else
+ return string(reinterpret_cast<char*>(m_lpECB->lpbData),m_lpECB->cbAvailable);
+ }
+ virtual void* sendPage(const string &msg, const string content_type,
+ const Iterator<header_t>& headers=EMPTY(header_t), int code=200) {
+ string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n";
+ for (int k = 0; k < headers.size(); k++) {
+ hdr += headers[k].first + ": " + headers[k].second + "\r\n";
+ }
+ hdr += "\r\n";
+ // XXX Need to handle "code"
+ m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
+ "200 OK", 0, (LPDWORD)hdr.c_str());
+ DWORD resplen = msg.size();
+ m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)msg.c_str(), &resplen, HSE_IO_SYNC);
+ return (void*)HSE_STATUS_SUCCESS;
+ }
+ virtual void* sendRedirect(const string url) {
+ // XXX: Don't support the httpRedirect option, yet.
+ string hdrs = m_cookie + "Location: " + url + "\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 40\r\n"
+ "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
+ "Cache-Control: private,no-store,no-cache\r\n\r\n";
+ m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
+ "302 Moved", 0, (LPDWORD)hdrs.c_str());
+ static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
+ DWORD resplen=40;
+ m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)redmsg, &resplen, HSE_IO_SYNC);
+ return (void*)HSE_STATUS_SUCCESS;
+ }
+ // Decline happens in the POST processor if this isn't the shire url
+ // Note that it can also happen with HTAccess, but we don't suppor that, yet.
+ virtual void* returnDecline(void) {
+ return (void*)
+ WriteClientError(m_lpECB, "UISAPA extension can only be unvoked to process incoming sessions."
+ "Make sure the mapped file extension doesn't match actual content.");
+ }
+ virtual void* returnOK(void) { return (void*) HSE_STATUS_SUCCESS; }
+
+ // Not used in the extension.
+ virtual string getCookies(void) { throw runtime_error("getCookies not implemented"); }
+ virtual void clearHeader(const string &name) { throw runtime_error("clearHeader not implemented"); }
+ virtual void setHeader(const string &name, const string &value) { throw runtime_error("setHeader not implemented"); }
+ virtual string getHeader(const string &name) { throw runtime_error("getHeader not implemented"); }
+ virtual void setRemoteUser(const string &user) { throw runtime_error("setRemoteUser not implemented"); }
+ virtual string getRemoteUser(void) { throw runtime_error("getRemoteUser not implemented"); }
+
+ LPEXTENSION_CONTROL_BLOCK m_lpECB;
+ string m_cookie;
+};
+
+extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
+{
+ string targeturl;
+ const IApplication* application=NULL;
+ try
+ {
+ ostringstream threadid;
+ threadid << "[" << getpid() << "] shire_handler" << '\0';
+ saml::NDC ndc(threadid.str().c_str());
+
+ // Determine web site number. This can't really fail, I don't think.
+ dynabuf buf(128);
+ GetServerVariable(lpECB,"INSTANCE_ID",buf,10);
+
+ // Match site instance to host name, skip if no match.
+ map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
+ if (map_i==g_Sites.end())
+ return WriteClientError(lpECB, "Shibboleth Extension not configured for this web site.");
+
+ ShibTargetIsapiE ste(lpECB, map_i->second);
+ pair<bool,void*> res = ste.doHandleProfile();
+ if (res.first) return (DWORD)res.second;
+
+ return WriteClientError(lpECB, "Shibboleth Extension failed to process request");
+
+ }
+ catch (...) {
+ return WriteClientError(lpECB,
+ "Shibboleth Extension caught an unknown error.");
+ }
+
+ // If we get here we've got an error.
+ return HSE_STATUS_ERROR;
+}
+#if 0
IRequestMapper::Settings map_request(
LPEXTENSION_CONTROL_BLOCK lpECB, IRequestMapper* mapper, const site_t& site, string& target
)
{
dynabuf ssl(5);
GetServerVariable(lpECB,"HTTPS",ssl,5);
- bool SSL=(ssl=="on");
+ bool SSL=(ssl=="on" || ssl=="ON");
// URL path always come from IIS.
dynabuf url(256);
// Scheme may come from site def or be derived from IIS.
const char* scheme=site.m_scheme.c_str();
- if (!scheme || !*scheme || !g_bNormalizeRequest)
- scheme=lpECB->lpszMethod;
-
- // Start with path.
- if (!url.empty())
- target=static_cast<char*>(url);
-
- // If port is non-default, prepend it.
- if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
- target = ':' + static_cast<char*>(port) + target;
+ if (!scheme || !*scheme || !g_bNormalizeRequest) {
+ scheme = SSL ? "https" : "http";
+ }
+ // Start with scheme and hostname.
if (g_bNormalizeRequest) {
- target = string(scheme) + "://" + site.m_name + target;
+ target = string(scheme) + "://" + site.m_name;
}
else {
dynabuf name(64);
GetServerVariable(lpECB,"SERVER_NAME",name,64);
- target = string(scheme) + "://" + static_cast<char*>(name) + target;
+ target = string(scheme) + "://" + static_cast<char*>(name);
}
- return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
-}
+
+ // If port is non-default, append it.
+ if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
+ target = target + ':' + static_cast<char*>(port);
-DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg)
-{
- LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
- static const char* ctype="Content-Type: text/html\r\n";
- lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
- static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Error</TITLE></HEAD><BODY><H1>Shibboleth Error</H1>";
- DWORD resplen=strlen(xmsg);
- lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC);
- resplen=strlen(msg);
- lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC);
- static const char* xmsg2="</BODY></HTML>";
- resplen=strlen(xmsg2);
- lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC);
- return HSE_STATUS_SUCCESS;
+ // Append path.
+ if (!url.empty())
+ target+=static_cast<char*>(url);
+
+ return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
}
DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const IApplication* app, const char* page, ShibMLP& mlp)
if (!infile.fail()) {
const char* res = mlp.run(infile,props);
if (res) {
- static const char* ctype="Content-Type: text/html\r\n";
+ static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
DWORD resplen=strlen(res);
lpECB->WriteClient(lpECB->ConnID,(LPVOID)res,&resplen,0);
return WriteClientError(lpECB,"Unable to open error template, check settings.");
}
+DWORD WriteRedirectPage(LPEXTENSION_CONTROL_BLOCK lpECB, const IApplication* app, const char* file, ShibMLP& mlp, const char* headers=NULL)
+{
+ ifstream infile(file);
+ if (!infile.fail()) {
+ const char* res = mlp.run(infile,app->getPropertySet("Errors"));
+ if (res) {
+ char buf[255];
+ sprintf(buf,"Content-Length: %u\r\nContent-Type: text/html\r\n\r\n",strlen(res));
+ if (headers) {
+ string h(headers);
+ h+=buf;
+ lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)h.c_str());
+ }
+ else
+ lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)buf);
+ DWORD resplen=strlen(res);
+ lpECB->WriteClient(lpECB->ConnID,(LPVOID)res,&resplen,0);
+ return HSE_STATUS_SUCCESS;
+ }
+ }
+ LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open redirect template.");
+ return WriteClientError(lpECB,"Unable to open redirect template, check settings.");
+}
+
extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
{
string targeturl;
Locker locker(conf);
// Map request to application and content settings.
- string targeturl;
IRequestMapper* mapper=conf->getRequestMapper();
Locker locker2(mapper);
IRequestMapper::Settings settings=map_request(lpECB,mapper,map_i->second,targeturl);
return WriteClientError(lpECB,"Unable to map request to application session settings, check configuration.");
SHIRE shire(application);
+
+ const char* shireURL=shire.getShireURL(targeturl.c_str());
+ if (!shireURL)
+ return WriteClientError(lpECB,"Unable to map request to proper shireURL setting, check configuration.");
// Make sure we only process the SHIRE requests.
- if (!strstr(targeturl.c_str(),shire.getShireURL(targeturl.c_str())))
- return WriteClientError(lpECB,"The request's application and associated shireURL setting are inconsistent.");;
+ if (!strstr(targeturl.c_str(),shireURL))
+ return WriteClientError(lpECB,"ISAPI extension can only be invoked to process incoming sessions."
+ "Make sure the mapped file extension doesn't match actual content.");
pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
throw ShibTargetException(SHIBRPC_OK,"blocked non-SSL access to SHIRE POST processor");
}
+ pair<bool,bool> httpRedirects=sessionProps->getBool("httpRedirects");
+ pair<bool,const char*> redirectPage=sessionProps->getString("redirectPage");
+ if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
+ return WriteClientError(lpECB,"HTML-based redirection requires a redirectPage property.");
+
+ // Check for Mac web browser
+ /*
+ bool bSafari=false;
+ dynabuf agent(64);
+ GetServerVariable(lpECB,"HTTP_USER_AGENT",agent,64);
+ if (strstr(agent,"AppleWebKit/"))
+ bSafari=true;
+ */
+
// If this is a GET, we manufacture an AuthnRequest.
if (!stricmp(lpECB->lpszMethod,"GET")) {
const char* areq=lpECB->lpszQueryString ? shire.getLazyAuthnRequest(lpECB->lpszQueryString) : NULL;
if (!areq)
throw ShibTargetException(SHIBRPC_OK, "malformed arguments to request a new session");
- targeturl = string("Location: ") + areq + "\r\n"
- "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
- "Cache-Control: private,no-store,no-cache\r\n"
- "Connection: close\r\n";
- HSE_SEND_HEADER_EX_INFO hinfo;
- hinfo.pszStatus="302 Moved";
- hinfo.pszHeader=targeturl.c_str();
- hinfo.cchStatus=9;
- hinfo.cchHeader=targeturl.length();
- hinfo.fKeepConn=FALSE;
- if (lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER_EX,&hinfo,0,0))
+ if (!httpRedirects.first || httpRedirects.second) {
+ string hdrs=string("Location: ") + areq + "\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 40\r\n"
+ "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
+ "Cache-Control: private,no-store,no-cache\r\n\r\n";
+ lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)hdrs.c_str());
+ static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
+ DWORD resplen=40;
+ lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
return HSE_STATUS_SUCCESS;
- return HSE_STATUS_ERROR;
+ }
+ else {
+ ShibMLP markupProcessor;
+ markupProcessor.insert("requestURL",areq);
+ return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor);
+ }
}
else if (stricmp(lpECB->lpszMethod,"POST"))
throw ShibTargetException(SHIBRPC_OK,"blocked non-POST to SHIRE POST processor");
if (status->isRetryable()) {
delete status;
const char* loc=shire.getAuthnRequest(elements.second);
- DWORD len=strlen(loc);
- if (lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_URL_REDIRECT_RESP,(LPVOID)loc,&len,0))
+ if (!httpRedirects.first || httpRedirects.second) {
+ string hdrs=string("Location: ") + loc + "\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: 40\r\n"
+ "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
+ "Cache-Control: private,no-store,no-cache\r\n\r\n";
+ lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)hdrs.c_str());
+ static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
+ DWORD resplen=40;
+ lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
return HSE_STATUS_SUCCESS;
- return HSE_STATUS_ERROR;
+ }
+ else {
+ markupProcessor.insert("requestURL",loc);
+ return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor);
+ }
}
// Return this error to the user.
// We've got a good session, set the cookie and redirect to target.
cookie = string("Set-Cookie: ") + shib_cookie.first + '=' + cookie + shib_cookie.second + "\r\n"
- "Location: " + elements.second + "\r\n"
"Expires: 01-Jan-1997 12:00:00 GMT\r\n"
- "Cache-Control: private,no-store,no-cache\r\n"
- "Connection: close\r\n";
- HSE_SEND_HEADER_EX_INFO hinfo;
- hinfo.pszStatus="302 Moved";
- hinfo.pszHeader=cookie.c_str();
- hinfo.cchStatus=9;
- hinfo.cchHeader=cookie.length();
- hinfo.fKeepConn=FALSE;
- if (lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER_EX,&hinfo,0,0))
+ "Cache-Control: private,no-store,no-cache\r\n";
+ if (!httpRedirects.first || httpRedirects.second) {
+ cookie=cookie + "Content-Type: text/html\r\nLocation: " + elements.second + "\r\nContent-Length: 40\r\n\r\n";
+ lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)cookie.c_str());
+ static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
+ DWORD resplen=40;
+ lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
return HSE_STATUS_SUCCESS;
+ }
+ else {
+ markupProcessor.insert("requestURL",elements.second);
+ return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor, cookie.c_str());
+ }
}
catch (ShibTargetException &e) {
if (application) {
}
}
#endif
-
+
return HSE_STATUS_ERROR;
}
+#endif // 0