2 * Copyright 2001-2005 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* isapi_shib.cpp - Shibboleth ISAPI filter
23 #include "config_win32.h"
26 #include <saml/saml.h>
27 #include <shib/shib.h>
28 #include <shib/shib-threads.h>
29 #include <shib-target/shib-target.h>
42 using namespace shibboleth;
43 using namespace shibtarget;
47 static const XMLCh name[] = { chLatin_n, chLatin_a, chLatin_m, chLatin_e, chNull };
48 static const XMLCh port[] = { chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
49 static const XMLCh sslport[] = { chLatin_s, chLatin_s, chLatin_l, chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
50 static const XMLCh scheme[] = { chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_e, chNull };
51 static const XMLCh id[] = { chLatin_i, chLatin_d, chNull };
52 static const XMLCh Implementation[] =
53 { chLatin_I, chLatin_m, chLatin_p, chLatin_l, chLatin_e, chLatin_m, chLatin_e, chLatin_n, chLatin_t, chLatin_a, chLatin_t, chLatin_i, chLatin_o, chLatin_n, chNull };
54 static const XMLCh ISAPI[] = { chLatin_I, chLatin_S, chLatin_A, chLatin_P, chLatin_I, chNull };
55 static const XMLCh Alias[] = { chLatin_A, chLatin_l, chLatin_i, chLatin_a, chLatin_s, chNull };
56 static const XMLCh normalizeRequest[] =
57 { chLatin_n, chLatin_o, chLatin_r, chLatin_m, chLatin_a, chLatin_l, chLatin_i, chLatin_z, chLatin_e,
58 chLatin_R, chLatin_e, chLatin_q, chLatin_u, chLatin_e, chLatin_s, chLatin_t, chNull
60 static const XMLCh Site[] = { chLatin_S, chLatin_i, chLatin_t, chLatin_e, chNull };
63 site_t(const DOMElement* e)
65 auto_ptr_char n(e->getAttributeNS(NULL,name));
66 auto_ptr_char s(e->getAttributeNS(NULL,scheme));
67 auto_ptr_char p(e->getAttributeNS(NULL,port));
68 auto_ptr_char p2(e->getAttributeNS(NULL,sslport));
69 if (n.get()) m_name=n.get();
70 if (s.get()) m_scheme=s.get();
71 if (p.get()) m_port=p.get();
72 if (p2.get()) m_sslport=p2.get();
73 DOMNodeList* nlist=e->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,Alias);
74 for (unsigned int i=0; nlist && i<nlist->getLength(); i++) {
75 if (nlist->item(i)->hasChildNodes()) {
76 auto_ptr_char alias(nlist->item(i)->getFirstChild()->getNodeValue());
77 m_aliases.insert(alias.get());
81 string m_scheme,m_port,m_sslport,m_name;
82 set<string> m_aliases;
86 ShibTargetConfig* g_Config = NULL;
87 map<string,site_t> g_Sites;
88 bool g_bNormalizeRequest = true;
92 LPCSTR lpUNCServerName,
98 LPCSTR messages[] = {message, NULL};
100 HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth ISAPI Filter");
101 BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL);
102 return (DeregisterEventSource(hElog) && res);
105 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
107 if (fdwReason==DLL_PROCESS_ATTACH)
112 extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer)
119 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
120 "Extension mode startup not possible, is the DLL loaded as a filter?");
124 pVer->dwExtensionVersion=HSE_VERSION;
125 strncpy(pVer->lpszExtensionDesc,"Shibboleth ISAPI Extension",HSE_MAX_EXT_DLL_NAME_LEN-1);
129 extern "C" BOOL WINAPI TerminateExtension(DWORD)
131 return TRUE; // cleanup should happen when filter unloads
134 extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
139 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
140 "Reentrant filter initialization, ignoring...");
148 LPCSTR schemadir=getenv("SHIBSCHEMAS");
150 schemadir=SHIB_SCHEMAS;
151 LPCSTR config=getenv("SHIBCONFIG");
154 g_Config=&ShibTargetConfig::getConfig();
155 g_Config->setFeatures(
156 ShibTargetConfig::Listener |
157 ShibTargetConfig::Metadata |
158 ShibTargetConfig::AAP |
159 ShibTargetConfig::RequestMapper |
160 ShibTargetConfig::LocalExtensions |
161 ShibTargetConfig::Logging
163 if (!g_Config->init(schemadir)) {
165 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
166 "Filter startup failed during library initialization, check native log for help.");
169 else if (!g_Config->load(config)) {
171 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
172 "Filter startup failed to load configuration, check native log for help.");
176 // Access the implementation-specifics for site mappings.
177 IConfig* conf=g_Config->getINI();
179 const IPropertySet* props=conf->getPropertySet("Local");
181 const DOMElement* impl=saml::XML::getFirstChildElement(
182 props->getElement(),shibtarget::XML::SHIBTARGET_NS,Implementation
184 if (impl && (impl=saml::XML::getFirstChildElement(impl,shibtarget::XML::SHIBTARGET_NS,ISAPI))) {
185 const XMLCh* flag=impl->getAttributeNS(NULL,normalizeRequest);
186 g_bNormalizeRequest=(!flag || !*flag || *flag==chDigit_1 || *flag==chLatin_t);
187 impl=saml::XML::getFirstChildElement(impl,shibtarget::XML::SHIBTARGET_NS,Site);
189 auto_ptr_char id(impl->getAttributeNS(NULL,id));
191 g_Sites.insert(pair<string,site_t>(id.get(),site_t(impl)));
192 impl=saml::XML::getNextSiblingElement(impl,shibtarget::XML::SHIBTARGET_NS,Site);
200 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Filter startup failed with an exception.");
205 pVer->dwFilterVersion=HTTP_FILTER_REVISION;
206 strncpy(pVer->lpszFilterDesc,"Shibboleth ISAPI Filter",SF_MAX_FILTER_DESC_LEN);
207 pVer->dwFlags=(SF_NOTIFY_ORDER_HIGH |
208 SF_NOTIFY_SECURE_PORT |
209 SF_NOTIFY_NONSECURE_PORT |
210 SF_NOTIFY_PREPROC_HEADERS |
212 LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter initialized...");
216 extern "C" BOOL WINAPI TerminateFilter(DWORD)
219 g_Config->shutdown();
221 LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter shut down...");
225 /* Next up, some suck-free versions of various APIs.
227 You DON'T require people to guess the buffer size and THEN tell them the right size.
228 Returning an LPCSTR is apparently way beyond their ken. Not to mention the fact that
229 constant strings aren't typed as such, making it just that much harder. These versions
230 are now updated to use a special growable buffer object, modeled after the standard
231 string class. The standard string won't work because they left out the option to
232 pre-allocate a non-constant buffer.
238 dynabuf() { bufptr=NULL; buflen=0; }
239 dynabuf(size_t s) { bufptr=new char[buflen=s]; *bufptr=0; }
240 ~dynabuf() { delete[] bufptr; }
241 size_t length() const { return bufptr ? strlen(bufptr) : 0; }
242 size_t size() const { return buflen; }
243 bool empty() const { return length()==0; }
244 void reserve(size_t s, bool keep=false);
245 void erase() { if (bufptr) memset(bufptr,0,buflen); }
246 operator char*() { return bufptr; }
247 bool operator ==(const char* s) const;
248 bool operator !=(const char* s) const { return !(*this==s); }
254 void dynabuf::reserve(size_t s, bool keep)
261 p[buflen]=bufptr[buflen];
267 bool dynabuf::operator==(const char* s) const
269 if (buflen==NULL || s==NULL)
270 return (buflen==NULL && s==NULL);
272 return strcmp(bufptr,s)==0;
275 void GetServerVariable(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
276 throw (bad_alloc, DWORD)
282 while (!pfc->GetServerVariable(pfc,lpszVariable,s,&size))
284 // Grumble. Check the error.
285 DWORD e=GetLastError();
286 if (e==ERROR_INSUFFICIENT_BUFFER)
291 if (bRequired && s.empty())
295 void GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
296 throw (bad_alloc, DWORD)
302 while (!lpECB->GetServerVariable(lpECB->ConnID,lpszVariable,s,&size))
304 // Grumble. Check the error.
305 DWORD e=GetLastError();
306 if (e==ERROR_INSUFFICIENT_BUFFER)
311 if (bRequired && s.empty())
315 void GetHeader(PHTTP_FILTER_PREPROC_HEADERS pn, PHTTP_FILTER_CONTEXT pfc,
316 LPSTR lpszName, dynabuf& s, DWORD size=80, bool bRequired=true)
317 throw (bad_alloc, DWORD)
323 while (!pn->GetHeader(pfc,lpszName,s,&size))
325 // Grumble. Check the error.
326 DWORD e=GetLastError();
327 if (e==ERROR_INSUFFICIENT_BUFFER)
332 if (bRequired && s.empty())
336 /****************************************************************************/
339 class ShibTargetIsapiF : public ShibTarget
341 PHTTP_FILTER_CONTEXT m_pfc;
342 PHTTP_FILTER_PREPROC_HEADERS m_pn;
345 ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const site_t& site) {
347 // URL path always come from IIS.
349 GetHeader(pn,pfc,"url",url,256,false);
351 // Port may come from IIS or from site def.
353 if (!g_bNormalizeRequest || (pfc->fIsSecurePort && site.m_sslport.empty()) || (!pfc->fIsSecurePort && site.m_port.empty()))
354 GetServerVariable(pfc,"SERVER_PORT",port,10);
355 else if (pfc->fIsSecurePort) {
356 strncpy(port,site.m_sslport.c_str(),10);
357 static_cast<char*>(port)[10]=0;
360 strncpy(port,site.m_port.c_str(),10);
361 static_cast<char*>(port)[10]=0;
364 // Scheme may come from site def or be derived from IIS.
365 const char* scheme=site.m_scheme.c_str();
366 if (!scheme || !*scheme || !g_bNormalizeRequest)
367 scheme=pfc->fIsSecurePort ? "https" : "http";
369 // Get the rest of the server variables.
370 dynabuf remote_addr(16),method(5),content_type(32),hostname(32);
371 GetServerVariable(pfc,"SERVER_NAME",hostname,32);
372 GetServerVariable(pfc,"REMOTE_ADDR",remote_addr,16);
373 GetServerVariable(pfc,"REQUEST_METHOD",method,5,false);
374 GetServerVariable(pfc,"CONTENT_TYPE",content_type,32,false);
376 // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name.
377 const char* host=hostname;
378 if (site.m_name!=host && site.m_aliases.find(host)==site.m_aliases.end())
379 host=site.m_name.c_str();
381 init(scheme, host, atoi(port), url, content_type, remote_addr, method);
386 ~ShibTargetIsapiF() { }
388 virtual void log(ShibLogLevel level, const string &msg) {
389 ShibTarget::log(level,msg);
390 if (level == LogLevelError)
391 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str());
393 virtual string getCookies() const {
395 GetHeader(m_pn, m_pfc, "Cookie:", buf, 128, false);
396 return buf.empty() ? "" : buf;
399 virtual void clearHeader(const string &name) {
400 string hdr = name + ":";
401 m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()), "");
403 virtual void setHeader(const string &name, const string &value) {
404 string hdr = name + ":";
405 m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()),
406 const_cast<char*>(value.c_str()));
408 virtual string getHeader(const string &name) {
409 string hdr = name + ":";
411 GetHeader(m_pn, m_pfc, const_cast<char*>(hdr.c_str()), buf, 1024, false);
414 virtual void setRemoteUser(const string &user) {
415 setHeader(string("remote-user"), user);
417 virtual string getRemoteUser(void) {
418 return getHeader(string("remote-user"));
420 virtual void* sendPage(
423 const string& content_type="text/html",
424 const Iterator<header_t>& headers=EMPTY(header_t)) {
425 string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n";
426 while (headers.hasNext()) {
427 const header_t& h=headers.next();
428 hdr += h.first + ": " + h.second + "\r\n";
431 const char* codestr="200 OK";
433 case 403: codestr="403 Forbidden"; break;
434 case 404: codestr="404 Not Found"; break;
435 case 500: codestr="500 Server Error"; break;
437 m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, (void*)codestr, (DWORD)hdr.c_str(), 0);
438 DWORD resplen = msg.size();
439 m_pfc->WriteClient(m_pfc, (LPVOID)msg.c_str(), &resplen, 0);
440 return (void*)SF_STATUS_REQ_FINISHED;
442 virtual void* sendRedirect(const string& url) {
443 // XXX: Don't support the httpRedirect option, yet.
444 string hdrs=m_cookie + string("Location: ") + url + "\r\n"
445 "Content-Type: text/html\r\n"
446 "Content-Length: 40\r\n"
447 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
448 "Cache-Control: private,no-store,no-cache\r\n\r\n";
449 m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER,
450 "302 Please Wait", (DWORD)hdrs.c_str(), 0);
451 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
453 m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0);
454 return reinterpret_cast<void*>(SF_STATUS_REQ_FINISHED);
456 // XXX: We might not ever hit the 'decline' status in this filter.
457 //virtual void* returnDecline(void) { }
458 virtual void* returnOK(void) { return (void*) SF_STATUS_REQ_NEXT_NOTIFICATION; }
460 // The filter never processes the POST, so stub these methods.
461 virtual void setCookie(const string &name, const string &value) {
462 // Set the cookie for later. Use it during the redirect.
463 m_cookie += "Set-Cookie: " + name + "=" + value + "\r\n";
465 virtual string getArgs(void) { throw runtime_error("getArgs not implemented"); }
466 virtual string getPostData(void) { throw runtime_error("getPostData not implemented"); }
469 DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg)
471 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
472 static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
473 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0);
474 static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Filter Error</TITLE></HEAD><BODY>"
475 "<H1>Shibboleth Filter Error</H1>";
476 DWORD resplen=strlen(xmsg);
477 pfc->WriteClient(pfc,(LPVOID)xmsg,&resplen,0);
479 pfc->WriteClient(pfc,(LPVOID)msg,&resplen,0);
480 static const char* xmsg2="</BODY></HTML>";
481 resplen=strlen(xmsg2);
482 pfc->WriteClient(pfc,(LPVOID)xmsg2,&resplen,0);
483 return SF_STATUS_REQ_FINISHED;
486 extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
488 // Is this a log notification?
489 if (notificationType==SF_NOTIFY_LOG)
491 if (pfc->pFilterContext)
492 ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=static_cast<LPCSTR>(pfc->pFilterContext);
493 return SF_STATUS_REQ_NEXT_NOTIFICATION;
496 PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification;
499 // Determine web site number. This can't really fail, I don't think.
501 GetServerVariable(pfc,"INSTANCE_ID",buf,10);
503 // Match site instance to host name, skip if no match.
504 map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
505 if (map_i==g_Sites.end())
506 return SF_STATUS_REQ_NEXT_NOTIFICATION;
508 ostringstream threadid;
509 threadid << "[" << getpid() << "] isapi_shib" << '\0';
510 saml::NDC ndc(threadid.str().c_str());
512 ShibTargetIsapiF stf(pfc, pn, map_i->second);
514 // "false" because we don't override the Shib settings
515 pair<bool,void*> res = stf.doCheckAuthN();
516 if (res.first) return (DWORD)res.second;
518 // "false" because we don't override the Shib settings
519 res = stf.doExportAssertions();
520 if (res.first) return (DWORD)res.second;
522 res = stf.doCheckAuthZ();
523 if (res.first) return (DWORD)res.second;
525 return SF_STATUS_REQ_NEXT_NOTIFICATION;
528 return WriteClientError(pfc,"Out of Memory");
531 if (e==ERROR_NO_DATA)
532 return WriteClientError(pfc,"A required variable or header was empty.");
534 return WriteClientError(pfc,"Server detected unexpected IIS error.");
538 return WriteClientError(pfc,"Server caught an unknown exception.");
542 return WriteClientError(pfc,"Server reached unreachable code, save my walrus!");
546 /****************************************************************************/
549 DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg)
551 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
552 static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
553 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
554 static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Error</TITLE></HEAD><BODY><H1>Shibboleth Error</H1>";
555 DWORD resplen=strlen(xmsg);
556 lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC);
558 lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC);
559 static const char* xmsg2="</BODY></HTML>";
560 resplen=strlen(xmsg2);
561 lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC);
562 return HSE_STATUS_SUCCESS;
566 class ShibTargetIsapiE : public ShibTarget
568 LPEXTENSION_CONTROL_BLOCK m_lpECB;
572 ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site) {
574 GetServerVariable(lpECB,"HTTPS",ssl,5);
575 bool SSL=(ssl=="on" || ssl=="ON");
577 // URL path always come from IIS.
579 GetServerVariable(lpECB,"URL",url,255);
581 // Port may come from IIS or from site def.
583 if (!g_bNormalizeRequest || (SSL && site.m_sslport.empty()) || (!SSL && site.m_port.empty()))
584 GetServerVariable(lpECB,"SERVER_PORT",port,10);
586 strncpy(port,site.m_sslport.c_str(),10);
587 static_cast<char*>(port)[10]=0;
590 strncpy(port,site.m_port.c_str(),10);
591 static_cast<char*>(port)[10]=0;
594 // Scheme may come from site def or be derived from IIS.
595 const char* scheme=site.m_scheme.c_str();
596 if (!scheme || !*scheme || !g_bNormalizeRequest) {
597 scheme = SSL ? "https" : "http";
600 // Get the other server variables.
601 dynabuf remote_addr(16),hostname(32);
602 GetServerVariable(lpECB, "REMOTE_ADDR", remote_addr, 16);
603 GetServerVariable(lpECB, "SERVER_NAME", hostname, 32);
605 // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name.
606 const char* host=hostname;
607 if (site.m_name!=host && site.m_aliases.find(host)==site.m_aliases.end())
608 host=site.m_name.c_str();
611 * IIS screws us over on PATH_INFO (the hits keep on coming). We need to figure out if
612 * the server is set up for proper PATH_INFO handling, or "IIS sucks rabid weasels mode",
613 * which is the default. No perfect way to tell, but we can take a good guess by checking
614 * whether the URL is a substring of the PATH_INFO:
616 * e.g. for /Shibboleth.sso/SAML/POST
618 * Bad mode (default):
619 * URL: /Shibboleth.sso
620 * PathInfo: /Shibboleth.sso/SAML/POST
623 * URL: /Shibboleth.sso
624 * PathInfo: /SAML/POST
629 // Clearly we're only in bad mode if path info exists at all.
630 if (lpECB->lpszPathInfo && *(lpECB->lpszPathInfo)) {
631 if (strstr(lpECB->lpszPathInfo,url))
632 // Pretty good chance we're in bad mode, unless the PathInfo repeats the path itself.
633 fullurl=lpECB->lpszPathInfo;
636 fullurl+=lpECB->lpszPathInfo;
640 // For consistency with Apache, let's add the query string.
641 if (lpECB->lpszQueryString && *(lpECB->lpszQueryString)) {
643 fullurl+=lpECB->lpszQueryString;
645 init(scheme, host, atoi(port), fullurl.c_str(), lpECB->lpszContentType, remote_addr, lpECB->lpszMethod);
649 ~ShibTargetIsapiE() { }
651 virtual void log(ShibLogLevel level, const string &msg) {
652 ShibTarget::log(level,msg);
653 if (level == LogLevelError)
654 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str());
656 virtual string getCookies() const {
658 GetServerVariable(m_lpECB, "HTTP_COOKIE", buf, 128, false);
659 return buf.empty() ? "" : buf;
661 virtual void setCookie(const string &name, const string &value) {
662 // Set the cookie for later. Use it during the redirect.
663 m_cookie += "Set-Cookie: " + name + "=" + value + "\r\n";
665 virtual string getArgs(void) {
666 return string(m_lpECB->lpszQueryString ? m_lpECB->lpszQueryString : "");
668 virtual string getPostData(void) {
669 if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB?
670 throw FatalProfileException("Blocked too-large a submission to profile endpoint.");
671 else if (m_lpECB->cbTotalBytes != m_lpECB->cbAvailable) {
674 DWORD datalen=m_lpECB->cbTotalBytes;
677 BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen);
679 throw FatalProfileException("Error reading profile submission from browser.");
680 cgistr.append(buf, buflen);
686 return string(reinterpret_cast<char*>(m_lpECB->lpbData),m_lpECB->cbAvailable);
688 virtual void* sendPage(
691 const string& content_type="text/html",
692 const Iterator<header_t>& headers=EMPTY(header_t)) {
693 string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n";
694 for (unsigned int k = 0; k < headers.size(); k++) {
695 hdr += headers[k].first + ": " + headers[k].second + "\r\n";
698 const char* codestr="200 OK";
700 case 403: codestr="403 Forbidden"; break;
701 case 404: codestr="404 Not Found"; break;
702 case 500: codestr="500 Server Error"; break;
704 m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, (void*)codestr, 0, (LPDWORD)hdr.c_str());
705 DWORD resplen = msg.size();
706 m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)msg.c_str(), &resplen, HSE_IO_SYNC);
707 return (void*)HSE_STATUS_SUCCESS;
709 virtual void* sendRedirect(const string& url) {
710 // XXX: Don't support the httpRedirect option, yet.
711 string hdrs = m_cookie + "Location: " + url + "\r\n"
712 "Content-Type: text/html\r\n"
713 "Content-Length: 40\r\n"
714 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
715 "Cache-Control: private,no-store,no-cache\r\n\r\n";
716 m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
717 "302 Moved", 0, (LPDWORD)hdrs.c_str());
718 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
720 m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)redmsg, &resplen, HSE_IO_SYNC);
721 return (void*)HSE_STATUS_SUCCESS;
723 // Decline happens in the POST processor if this isn't the shire url
724 // Note that it can also happen with HTAccess, but we don't support that, yet.
725 virtual void* returnDecline(void) {
727 WriteClientError(m_lpECB, "ISAPI extension can only be invoked to process Shibboleth protocol requests."
728 "Make sure the mapped file extension doesn't match actual content.");
730 virtual void* returnOK(void) { return (void*) HSE_STATUS_SUCCESS; }
732 // Not used in the extension.
733 virtual void clearHeader(const string &name) { throw runtime_error("clearHeader not implemented"); }
734 virtual void setHeader(const string &name, const string &value) { throw runtime_error("setHeader not implemented"); }
735 virtual string getHeader(const string &name) { throw runtime_error("getHeader not implemented"); }
736 virtual void setRemoteUser(const string &user) { throw runtime_error("setRemoteUser not implemented"); }
737 virtual string getRemoteUser(void) { throw runtime_error("getRemoteUser not implemented"); }
740 extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
743 const IApplication* application=NULL;
745 ostringstream threadid;
746 threadid << "[" << getpid() << "] isapi_shib_extension" << '\0';
747 saml::NDC ndc(threadid.str().c_str());
749 // Determine web site number. This can't really fail, I don't think.
751 GetServerVariable(lpECB,"INSTANCE_ID",buf,10);
753 // Match site instance to host name, skip if no match.
754 map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
755 if (map_i==g_Sites.end())
756 return WriteClientError(lpECB, "Shibboleth Extension not configured for this web site.");
758 ShibTargetIsapiE ste(lpECB, map_i->second);
759 pair<bool,void*> res = ste.doHandler();
760 if (res.first) return (DWORD)res.second;
762 return WriteClientError(lpECB, "Shibboleth Extension failed to process request");
766 return WriteClientError(lpECB,"Out of Memory");
769 if (e==ERROR_NO_DATA)
770 return WriteClientError(lpECB,"A required variable or header was empty.");
772 return WriteClientError(lpECB,"Server detected unexpected IIS error.");
776 return WriteClientError(lpECB,"Server caught an unknown exception.");
780 // If we get here we've got an error.
781 return HSE_STATUS_ERROR;