1 /**
2  * Licensed to the University Corporation for Advanced Internet
3  * Development, Inc. (UCAID) under one or more contributor license
4  * agreements. See the NOTICE file distributed with this work for
5  * additional information regarding copyright ownership.
6  *
7  * UCAID licenses this file to you under the Apache License,
8  * Version 2.0 (the "License"); you may not use this file except
9  * in compliance with the License. You may obtain a copy of the
10  * License at
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
17  * either express or implied. See the License for the specific
18  * language governing permissions and limitations under the License.
19  */
20 
21 /**
22  * isapi_shib.cpp
23  *
24  * Shibboleth ISAPI filter.
25  */
26 
27 #define SHIBSP_LITE
28 #include "config_win32.h"
29 
30 #define _CRT_NONSTDC_NO_DEPRECATE 1
31 #define _CRT_SECURE_NO_DEPRECATE 1
32 #define _CRT_RAND_S
33 
34 #include <shibsp/exceptions.h>
35 #include <shibsp/AbstractSPRequest.h>
36 #include <shibsp/SPConfig.h>
37 #include <shibsp/ServiceProvider.h>
38 
39 #include <set>
40 #include <fstream>
41 #include <stdexcept>
42 #include <process.h>
43 #include <boost/lexical_cast.hpp>
44 #include <xmltooling/unicode.h>
45 #include <xmltooling/XMLToolingConfig.h>
46 #include <xmltooling/util/NDC.h>
47 #include <xmltooling/util/XMLConstants.h>
48 #include <xmltooling/util/XMLHelper.h>
49 #include <xmltooling/logging.h>
50 
51 #include <xercesc/util/Base64.hpp>
52 #include <xercesc/util/XMLUniDefs.hpp>
53 
54 #include <windows.h>
55 #include <httpfilt.h>
56 #include <httpext.h>
57 
58 using namespace shibsp;
59 using namespace xmltooling;
60 using namespace xercesc;
61 using namespace boost;
62 using namespace std;
63 
64 using xmltooling::logging::Category;
65 using xmltooling::logging::Priority;
66 
67 // globals
68 namespace {
69     static const XMLCh path[] =             UNICODE_LITERAL_4(p,a,t,h);
70     static const XMLCh validate[] =         UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
71     static const XMLCh name[] =             UNICODE_LITERAL_4(n,a,m,e);
72     static const XMLCh port[] =             UNICODE_LITERAL_4(p,o,r,t);
73     static const XMLCh sslport[] =          UNICODE_LITERAL_7(s,s,l,p,o,r,t);
74     static const XMLCh scheme[] =           UNICODE_LITERAL_6(s,c,h,e,m,e);
75     static const XMLCh id[] =               UNICODE_LITERAL_2(i,d);
76     static const XMLCh useHeaders[] =       UNICODE_LITERAL_10(u, s, e, H, e, a, d, e, r, s);
77     static const XMLCh useVariables[] =     UNICODE_LITERAL_12(u, s, e, V, a, r, i, a, b, l, e, s);
78     static const XMLCh Alias[] =            UNICODE_LITERAL_5(A,l,i,a,s);
79     static const XMLCh Site[] =             UNICODE_LITERAL_4(S,i,t,e);
80 
81     struct site_t {
site_t__anond950c34c0111::site_t82         site_t(const DOMElement* e)
83             : m_name(XMLHelper::getAttrString(e, "", name)),
84                 m_scheme(XMLHelper::getAttrString(e, "", scheme)),
85                 m_port(XMLHelper::getAttrString(e, "", port)),
86                 m_sslport(XMLHelper::getAttrString(e, "", sslport))
87         {
88             e = XMLHelper::getFirstChildElement(e, Alias);
89             while (e) {
90                 if (e->hasChildNodes()) {
91                     auto_ptr_char alias(XMLHelper::getTextContent(e));
92                     m_aliases.insert(alias.get());
93                 }
94                 e = XMLHelper::getNextSiblingElement(e, Alias);
95             }
96         }
97         string m_scheme,m_port,m_sslport,m_name;
98         set<string> m_aliases;
99     };
100 
101     HINSTANCE g_hinstDLL;
102     SPConfig* g_Config = nullptr;
103     map<string,site_t> g_Sites;
104     bool g_bNormalizeRequest = true;
105     string g_unsetHeaderValue,g_spoofKey;
106     bool g_checkSpoofing = true;
107     bool g_catchAll = false;
108     bool g_bSafeHeaderNames = false;
109     vector<string> g_NoCerts;
110 }
111 
_my_invalid_parameter_handler(const wchar_t * expression,const wchar_t * function,const wchar_t * file,unsigned int line,uintptr_t pReserved)112 void _my_invalid_parameter_handler(
113    const wchar_t * expression,
114    const wchar_t * function,
115    const wchar_t * file,
116    unsigned int line,
117    uintptr_t pReserved
118    )
119 {
120     return;
121 }
122 
DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID)123 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
124 {
125     if (fdwReason == DLL_PROCESS_ATTACH)
126         g_hinstDLL = hinstDLL;
127     return TRUE;
128 }
129 
GetExtensionVersion(HSE_VERSION_INFO * pVer)130 extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer)
131 {
132     if (!pVer)
133         return FALSE;
134 
135     if (!g_Config) {
136         Category::getInstance(SHIBSP_LOGCAT ".ISAPI").fatal("extension mode startup not possible, is the DLL loaded as a filter?");
137         return FALSE;
138     }
139 
140     pVer->dwExtensionVersion = HSE_VERSION;
141     strncpy(pVer->lpszExtensionDesc, "Shibboleth ISAPI Extension", HSE_MAX_EXT_DLL_NAME_LEN-1);
142     return TRUE;
143 }
144 
TerminateExtension(DWORD)145 extern "C" BOOL WINAPI TerminateExtension(DWORD)
146 {
147     return TRUE;    // cleanup should happen when filter unloads
148 }
149 
GetFilterVersion(PHTTP_FILTER_VERSION pVer)150 extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
151 {
152     Category& log = Category::getInstance(SHIBSP_LOGCAT ".ISAPI");
153 
154     if (!pVer)
155         return FALSE;
156     else if (g_Config) {
157         log.warn("reentrant ISAPI filter initialization, ignoring...");
158         return TRUE;
159     }
160 
161     g_Config = &SPConfig::getConfig();
162     g_Config->deprecation().warn("ISAPI extension is replaced by IIS 7+ module");
163     g_Config->setFeatures(
164         SPConfig::Listener |
165         SPConfig::Caching |
166         SPConfig::RequestMapping |
167         SPConfig::InProcess |
168         SPConfig::Logging |
169         SPConfig::Handlers
170         );
171     if (!g_Config->init()) {
172         g_Config = nullptr;
173         log.fatal("ISAPI filter startup failed during library initialization, check native log for help");
174         return FALSE;
175     }
176 
177     try {
178         if (!g_Config->instantiate(nullptr, true))
179             throw runtime_error("unknown error");
180     }
181     catch (const std::exception& ex) {
182         log.fatal("ISAPI filter startup failed: %s", ex.what());
183         g_Config->term();
184         g_Config=nullptr;
185         return FALSE;
186     }
187 
188     // Access implementation-specifics and site mappings.
189     ServiceProvider* sp = g_Config->getServiceProvider();
190     Locker locker(sp);
191     const PropertySet* props = sp->getPropertySet("InProcess");
192     if (props) {
193         pair<bool,bool> flag = props->getBool("checkSpoofing");
194         g_checkSpoofing = !flag.first || flag.second;
195         flag = props->getBool("catchAll");
196         g_catchAll = flag.first && flag.second;
197 
198         pair<bool,const char*> unsetValue = props->getString("unsetHeaderValue");
199         if (unsetValue.first)
200             g_unsetHeaderValue = unsetValue.second;
201         if (g_checkSpoofing) {
202             unsetValue = props->getString("spoofKey");
203             if (unsetValue.first)
204                 g_spoofKey = unsetValue.second;
205             else {
206                 _invalid_parameter_handler old = _set_invalid_parameter_handler(_my_invalid_parameter_handler);
207                 unsigned int randkey=0,randkey2=0,randkey3=0,randkey4=0;
208                 if (rand_s(&randkey) == 0 && rand_s(&randkey2) == 0 && rand_s(&randkey3) == 0 && rand_s(&randkey4) == 0) {
209                     _set_invalid_parameter_handler(old);
210                     g_spoofKey = lexical_cast<string>(randkey) + lexical_cast<string>(randkey2) +
211                         lexical_cast<string>(randkey3) + lexical_cast<string>(randkey4);
212                 }
213                 else {
214                     _set_invalid_parameter_handler(old);
215                     log.fatal("ISAPI filter failed to generate a random anti-spoofing key");
216                     locker.assign();    // pops lock on SP config
217                     g_Config->term();
218                     g_Config = nullptr;
219                     return FALSE;
220                 }
221             }
222         }
223 
224         props = props->getPropertySet("ISAPI");
225         if (props) {
226             flag = props->getBool("normalizeRequest");
227             g_bNormalizeRequest = !flag.first || flag.second;
228             flag = props->getBool("safeHeaderNames");
229             g_bSafeHeaderNames = flag.first && flag.second;
230             if (props->getString("useHeaders").first)
231                 log.warn("useHeaders attribute not supported by ISAPI filter, ignored");
232             if (props->getString("useVariables").first)
233                 log.warn("useVariables attribute not supported by ISAPI filter, ignored");
234 
235             const DOMElement* child = XMLHelper::getFirstChildElement(props->getElement(), Site);
236             while (child) {
237                 string id(XMLHelper::getAttrString(child, "", id));
238                 if (!id.empty()) {
239                     g_Sites.insert(make_pair(id, site_t(child)));
240                     if (!XMLHelper::getAttrString(child, "", useHeaders).empty())
241                         log.warn("useHeaders attribute not valid for this filter");
242                     if (!XMLHelper::getAttrString(child, "", useVariables).empty())
243                         log.warn("useVariables attribute not valid for this filter");
244                 }
245                 child = XMLHelper::getNextSiblingElement(child, Site);
246             }
247 
248             if (nullptr != props->getPropertySet("Roles"))
249                 log.warn("<Roles> element not valid for this filter");
250         }
251     }
252 
253     pVer->dwFilterVersion = HTTP_FILTER_REVISION;
254     strncpy(pVer->lpszFilterDesc, "Shibboleth ISAPI Filter", SF_MAX_FILTER_DESC_LEN);
255     pVer->dwFlags=(SF_NOTIFY_ORDER_HIGH |
256                    SF_NOTIFY_SECURE_PORT |
257                    SF_NOTIFY_NONSECURE_PORT |
258                    SF_NOTIFY_PREPROC_HEADERS |
259                    SF_NOTIFY_LOG);
260     log.info("ISAPI filter initialized");
261     return TRUE;
262 }
263 
TerminateFilter(DWORD)264 extern "C" BOOL WINAPI TerminateFilter(DWORD)
265 {
266     if (g_Config)
267         g_Config->term();
268     g_Config = nullptr;
269     Category::getInstance(SHIBSP_LOGCAT ".ISAPI").info("ISAPI filter shutting down");
270     return TRUE;
271 }
272 
273 /* Next up, some suck-free versions of various APIs.
274 
275    You DON'T require people to guess the buffer size and THEN tell them the right size.
276    Returning an LPCSTR is apparently way beyond their ken. Not to mention the fact that
277    constant strings aren't typed as such, making it just that much harder. These versions
278    are now updated to use a special growable buffer object, modeled after the standard
279    string class. The standard string won't work because they left out the option to
280    pre-allocate a non-constant buffer.
281 */
282 
283 class dynabuf
284 {
285 public:
dynabuf()286     dynabuf() { bufptr=nullptr; buflen=0; }
dynabuf(size_t s)287     dynabuf(size_t s) { bufptr=new char[buflen=s]; *bufptr=0; }
~dynabuf()288     ~dynabuf() { delete[] bufptr; }
length() const289     size_t length() const { return bufptr ? strlen(bufptr) : 0; }
size() const290     size_t size() const { return buflen; }
empty() const291     bool empty() const { return length()==0; }
292     void reserve(size_t s, bool keep=false);
erase()293     void erase() { if (bufptr) memset(bufptr,0,buflen); }
operator char*()294     operator char*() { return bufptr; }
295     bool operator ==(const char* s) const;
operator !=(const char * s) const296     bool operator !=(const char* s) const { return !(*this==s); }
297 private:
298     char* bufptr;
299     size_t buflen;
300 };
301 
reserve(size_t s,bool keep)302 void dynabuf::reserve(size_t s, bool keep)
303 {
304     if (s<=buflen)
305         return;
306     char* p=new char[s];
307     if (keep)
308         while (buflen--)
309             p[buflen]=bufptr[buflen];
310     buflen=s;
311     delete[] bufptr;
312     bufptr=p;
313 }
314 
operator ==(const char * s) const315 bool dynabuf::operator==(const char* s) const
316 {
317     if (buflen==0 || s==nullptr)
318         return (buflen==0 && s==nullptr);
319     else
320         return strcmp(bufptr,s)==0;
321 }
322 
323 /****************************************************************************/
324 // ISAPI Filter
325 
326 class ShibTargetIsapiF : public AbstractSPRequest
327 {
328   PHTTP_FILTER_CONTEXT m_pfc;
329   PHTTP_FILTER_PREPROC_HEADERS m_pn;
330   multimap<string,string> m_headers;
331   int m_port;
332   string m_scheme,m_hostname;
333   mutable string m_remote_addr,m_content_type,m_method;
334   dynabuf m_allhttp;
335   bool m_firsttime;
336 
337 public:
ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc,PHTTP_FILTER_PREPROC_HEADERS pn,const site_t & site)338   ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const site_t& site)
339       : AbstractSPRequest(SHIBSP_LOGCAT ".ISAPI"), m_pfc(pfc), m_pn(pn), m_allhttp(4096), m_firsttime(true) {
340 
341     static char _url[] = "url";
342     static char _SERVER_PORT[] = "SERVER_PORT";
343     static char _SERVER_NAME[] = "SERVER_NAME";
344     static char _ShibSpoofCheck[] = "ShibSpoofCheck:";
345 
346     // URL path always come from IIS.
347     dynabuf var(256);
348     GetHeader(_url,var,256,false);
349     setRequestURI(var);
350 
351     // Port may come from IIS or from site def.
352     if (!g_bNormalizeRequest || (pfc->fIsSecurePort && site.m_sslport.empty()) || (!pfc->fIsSecurePort && site.m_port.empty())) {
353         GetServerVariable(_SERVER_PORT,var,10);
354         if (var.empty()) {
355             m_port = pfc->fIsSecurePort ? 443 : 80;
356         }
357         else {
358             m_port = atoi(var);
359         }
360     }
361     else if (pfc->fIsSecurePort) {
362         m_port = atoi(site.m_sslport.c_str());
363     }
364     else {
365         m_port = atoi(site.m_port.c_str());
366     }
367 
368     // Scheme may come from site def or be derived from IIS.
369     m_scheme=site.m_scheme;
370     if (m_scheme.empty() || !g_bNormalizeRequest)
371         m_scheme=pfc->fIsSecurePort ? "https" : "http";
372 
373     GetServerVariable(_SERVER_NAME,var,32);
374 
375     // Make sure SERVER_NAME is "authorized" for use on this site. If not, or empty, set to canonical name.
376     if (var.empty()) {
377         m_hostname = site.m_name;
378     }
379     else {
380         m_hostname = var;
381         if (site.m_name != m_hostname && site.m_aliases.find(m_hostname) == site.m_aliases.end())
382             m_hostname = site.m_name;
383     }
384 
385     if (!g_spoofKey.empty()) {
386         GetHeader(_ShibSpoofCheck, var, 32, false);
387         if (!var.empty() && g_spoofKey == (char*)var)
388             m_firsttime = false;
389     }
390 
391     if (!m_firsttime)
392         log(SPDebug, "ISAPI filter running more than once");
393   }
~ShibTargetIsapiF()394   ~ShibTargetIsapiF() { }
395 
getScheme() const396   const char* getScheme() const {
397     return m_scheme.c_str();
398   }
getHostname() const399   const char* getHostname() const {
400     return m_hostname.c_str();
401   }
getPort() const402   int getPort() const {
403     return m_port;
404   }
getQueryString() const405   const char* getQueryString() const {
406       const char* uri = getRequestURI();
407       uri = (uri ? strchr(uri, '?') : nullptr);
408       return uri ? (uri + 1) : nullptr;
409   }
getMethod() const410   const char* getMethod() const {
411     static char _HTTP_METHOD[] = "HTTP_METHOD";
412     if (m_method.empty()) {
413         dynabuf var(5);
414         GetServerVariable(_HTTP_METHOD,var,5,false);
415         if (!var.empty())
416             m_method = var;
417     }
418     return m_method.c_str();
419   }
getContentType() const420   string getContentType() const {
421     static char _HTTP_CONTENT_TYPE[] = "HTTP_CONTENT_TYPE";
422     if (m_content_type.empty()) {
423         dynabuf var(32);
424         GetServerVariable(_HTTP_CONTENT_TYPE,var,32,false);
425         if (!var.empty())
426             m_content_type = var;
427     }
428     return m_content_type;
429   }
getRemoteAddr() const430   string getRemoteAddr() const {
431     static char _REMOTE_ADDR[] = "REMOTE_ADDR";
432     m_remote_addr = AbstractSPRequest::getRemoteAddr();
433     if (m_remote_addr.empty()) {
434         dynabuf var(16);
435         GetServerVariable(_REMOTE_ADDR,var,16,false);
436         if (!var.empty())
437             m_remote_addr = var;
438     }
439     return m_remote_addr;
440   }
makeSafeHeader(const char * rawname) const441   string makeSafeHeader(const char* rawname) const {
442       string hdr;
443       for (; *rawname; ++rawname) {
444           if (isalnum(*rawname))
445               hdr += *rawname;
446       }
447       return (hdr + ':');
448   }
clearHeader(const char * rawname,const char * cginame)449   void clearHeader(const char* rawname, const char* cginame) {
450     static char _ALL_HTTP[] = "ALL_HTTP";
451     static char _REMOTE_USER[] = "remote-user:";
452     static char _REMOTE_USER2[] = "remote_user:";
453 
454     if (g_checkSpoofing && m_firsttime) {
455         if (m_allhttp.empty())
456 	        GetServerVariable(_ALL_HTTP, m_allhttp, 4096, false);
457         if (!m_allhttp.empty()) {
458             string hdr = g_bSafeHeaderNames ? ("HTTP_" + makeSafeHeader(cginame + 5)) : (string(cginame) + ':');
459             if (strstr(m_allhttp, hdr.c_str()))
460                 throw opensaml::SecurityPolicyException("Attempt to spoof header ($1) was detected.", params(1, hdr.c_str()));
461         }
462     }
463     if (g_bSafeHeaderNames) {
464         string hdr = makeSafeHeader(rawname);
465         m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()), const_cast<char*>(g_unsetHeaderValue.c_str()));
466     }
467     else if (!strcmp(rawname,"REMOTE_USER")) {
468         m_pn->SetHeader(m_pfc, _REMOTE_USER, const_cast<char*>(g_unsetHeaderValue.c_str()));
469         m_pn->SetHeader(m_pfc, _REMOTE_USER2, const_cast<char*>(g_unsetHeaderValue.c_str()));
470 	}
471 	else {
472         string hdr = string(rawname) + ':';
473         m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()), const_cast<char*>(g_unsetHeaderValue.c_str()));
474 	}
475   }
setHeader(const char * name,const char * value)476   void setHeader(const char* name, const char* value) {
477     string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':');
478     m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()), const_cast<char*>(value));
479   }
getSecureHeader(const char * name) const480   string getSecureHeader(const char* name) const {
481     string hdr = g_bSafeHeaderNames ? makeSafeHeader(name) : (string(name) + ':');
482     dynabuf buf(256);
483     GetHeader(const_cast<char*>(hdr.c_str()), buf, 256, false);
484     return string(buf.empty() ? "" : static_cast<char*>(buf));
485   }
getHeader(const char * name) const486   string getHeader(const char* name) const {
487     string hdr(name);
488     hdr += ':';
489     dynabuf buf(256);
490     GetHeader(const_cast<char*>(hdr.c_str()), buf, 256, false);
491     return string(buf.empty() ? "" : static_cast<char*>(buf));
492   }
setRemoteUser(const char * user)493   void setRemoteUser(const char* user) {
494     setHeader("remote-user", user);
495     if (!user || !*user)
496         m_pfc->pFilterContext = nullptr;
497     else if (m_pfc->pFilterContext = m_pfc->AllocMem(m_pfc, sizeof(char) * (strlen(user) + 1), 0))
498         strcpy(reinterpret_cast<char*>(m_pfc->pFilterContext), user);
499   }
getRemoteUser() const500   string getRemoteUser() const {
501     return getSecureHeader("remote-user");
502   }
setResponseHeader(const char * name,const char * value,bool replace=false)503   void setResponseHeader(const char* name, const char* value, bool replace=false) {
504     HTTPResponse::setResponseHeader(name, value, replace);
505     if (name && *name) {
506         // Set for later.
507         if (replace || !value)
508             m_headers.erase(name);
509         if (value && *value)
510             m_headers.insert(make_pair(name,value));
511     }
512   }
sendResponse(istream & in,long status)513   long sendResponse(istream& in, long status) {
514     string hdr = string("Connection: close\r\n");
515     for (multimap<string,string>::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i)
516         hdr += i->first + ": " + i->second + "\r\n";
517     hdr += "\r\n";
518     const char* codestr="200 OK";
519     switch (status) {
520         case XMLTOOLING_HTTP_STATUS_NOTMODIFIED:    codestr="304 Not Modified"; break;
521         case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED:   codestr="401 Authorization Required"; break;
522         case XMLTOOLING_HTTP_STATUS_FORBIDDEN:      codestr="403 Forbidden"; break;
523         case XMLTOOLING_HTTP_STATUS_NOTFOUND:       codestr="404 Not Found"; break;
524         case XMLTOOLING_HTTP_STATUS_ERROR:          codestr="500 Server Error"; break;
525     }
526     m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, (void*)codestr, (ULONG_PTR)hdr.c_str(), 0);
527     char buf[1024];
528     while (in) {
529         in.read(buf,1024);
530         DWORD resplen = in.gcount();
531         m_pfc->WriteClient(m_pfc, buf, &resplen, 0);
532     }
533     return SF_STATUS_REQ_FINISHED;
534   }
sendRedirect(const char * url)535   long sendRedirect(const char* url) {
536     static char _status[] = "302 Please Wait";
537     HTTPResponse::sendRedirect(url);
538     string hdr=string("Location: ") + url + "\r\n"
539       "Content-Type: text/html\r\n"
540       "Content-Length: 40\r\n"
541       "Expires: Wed, 01 Jan 1997 12:00:00 GMT\r\n"
542       "Cache-Control: private,no-store,no-cache,max-age=0\r\n";
543     for (multimap<string,string>::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i)
544         hdr += i->first + ": " + i->second + "\r\n";
545     hdr += "\r\n";
546     m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, _status, (ULONG_PTR)hdr.c_str(), 0);
547     static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
548     DWORD resplen=40;
549     m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0);
550     return SF_STATUS_REQ_FINISHED;
551   }
returnDecline()552   long returnDecline() {
553       return SF_STATUS_REQ_NEXT_NOTIFICATION;
554   }
returnOK()555   long returnOK() {
556     return SF_STATUS_REQ_NEXT_NOTIFICATION;
557   }
558 
getClientCertificates() const559   const vector<string>& getClientCertificates() const {
560       return g_NoCerts;
561   }
562 
563   // The filter never processes the POST, so stub these methods.
getContentLength() const564   long getContentLength() const { throw IOException("The request's Content-Length is not available to an ISAPI filter."); }
getRequestBody() const565   const char* getRequestBody() const { throw IOException("The request body is not available to an ISAPI filter."); }
566 
GetServerVariable(LPSTR lpszVariable,dynabuf & s,DWORD size=80,bool bRequired=true) const567   void GetServerVariable(LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) const {
568     s.reserve(size);
569     s.erase();
570     size=s.size();
571 
572     while (!m_pfc->GetServerVariable(m_pfc,lpszVariable,s,&size)) {
573         // Grumble. Check the error.
574         DWORD e = GetLastError();
575         if (e == ERROR_INSUFFICIENT_BUFFER)
576             s.reserve(size);
577         else
578             break;
579     }
580     if (bRequired && s.empty())
581         log(SPRequest::SPError, string("missing required server variable: ") + lpszVariable);
582   }
583 
GetHeader(LPSTR lpszName,dynabuf & s,DWORD size=80,bool bRequired=true) const584   void GetHeader(LPSTR lpszName, dynabuf& s, DWORD size=80, bool bRequired=true) const {
585     s.reserve(size);
586     s.erase();
587     size=s.size();
588 
589     while (!m_pn->GetHeader(m_pfc,lpszName,s,&size)) {
590         // Grumble. Check the error.
591         DWORD e = GetLastError();
592         if (e == ERROR_INSUFFICIENT_BUFFER)
593             s.reserve(size);
594         else
595             break;
596     }
597     if (bRequired && s.empty())
598         log(SPRequest::SPError, string("missing required header: ") + lpszName);
599   }
600 };
601 
WriteClientError(PHTTP_FILTER_CONTEXT pfc,const char * msg)602 DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg)
603 {
604     static char _status[] = "200 OK";
605     static char ctype[] = "Connection: close\r\nContent-Type: text/html\r\n\r\n";
606     pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,_status,(ULONG_PTR)ctype,0);
607     static char xmsg[] = "<HTML><HEAD><TITLE>Shibboleth Filter Error</TITLE></HEAD><BODY>"
608                             "<H1>Shibboleth Filter Error</H1>";
609     DWORD resplen=strlen(xmsg);
610     pfc->WriteClient(pfc,xmsg,&resplen,0);
611     resplen=strlen(msg);
612     pfc->WriteClient(pfc,const_cast<char*>(msg),&resplen,0);
613     static char xmsg2[] = "</BODY></HTML>";
614     resplen=strlen(xmsg2);
615     pfc->WriteClient(pfc,xmsg2,&resplen,0);
616     return SF_STATUS_REQ_FINISHED;
617 }
618 
GetServerVariable(PHTTP_FILTER_CONTEXT pfc,LPSTR lpszVariable,dynabuf & s,DWORD size=80,bool bRequired=true)619 void GetServerVariable(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
620 {
621     s.reserve(size);
622     s.erase();
623     size=s.size();
624 
625     while (!pfc->GetServerVariable(pfc,lpszVariable,s,&size)) {
626         // Grumble. Check the error.
627         DWORD e=GetLastError();
628         if (e==ERROR_INSUFFICIENT_BUFFER)
629             s.reserve(size);
630         else
631             break;
632     }
633     if (bRequired && s.empty()) {
634         Category::getInstance(SHIBSP_LOGCAT ".ISAPI").error("missing server variable: %s", lpszVariable);
635     }
636 }
637 
638 
HttpFilterProc(PHTTP_FILTER_CONTEXT pfc,DWORD notificationType,LPVOID pvNotification)639 extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
640 {
641     static char _INSTANCE_ID[] = "INSTANCE_ID";
642     static char _ShibSpoofCheck[] = "ShibSpoofCheck:";
643 
644     // Is this a log notification?
645     if (notificationType == SF_NOTIFY_LOG) {
646         if (pfc->pFilterContext)
647         	((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName = reinterpret_cast<char*>(pfc->pFilterContext);
648         return SF_STATUS_REQ_NEXT_NOTIFICATION;
649     }
650 
651     PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification;
652     try {
653         // Determine web site number. This can't really fail, I don't think.
654         dynabuf buf(128);
655         GetServerVariable(pfc,_INSTANCE_ID,buf,10);
656         if (buf.empty())
657             return WriteClientError(pfc, "Shibboleth Filter failed to obtain INSTANCE_ID server variable.");
658 
659         // Match site instance to host name, skip if no match.
660         map<string,site_t>::const_iterator map_i = g_Sites.find(static_cast<char*>(buf));
661         if (map_i == g_Sites.end())
662             return SF_STATUS_REQ_NEXT_NOTIFICATION;
663 
664         string threadid("[");
665         threadid += lexical_cast<string>(getpid()) + "] isapi_shib";
666         xmltooling::NDC ndc(threadid.c_str());
667 
668         ShibTargetIsapiF stf(pfc, pn, map_i->second);
669 
670         pair<bool,long> res = stf.getServiceProvider().doAuthentication(stf);
671         if (!g_spoofKey.empty())
672             pn->SetHeader(pfc, _ShibSpoofCheck, const_cast<char*>(g_spoofKey.c_str()));
673         if (res.first) return res.second;
674 
675         res = stf.getServiceProvider().doExport(stf);
676         if (res.first) return res.second;
677 
678         res = stf.getServiceProvider().doAuthorization(stf);
679         if (res.first) return res.second;
680 
681         return SF_STATUS_REQ_NEXT_NOTIFICATION;
682     }
683     catch(const bad_alloc&) {
684         return WriteClientError(pfc, "Out of Memory");
685     }
686     catch(long e) {
687         if (e==ERROR_NO_DATA)
688             return WriteClientError(pfc, "A required variable or header was empty.");
689         else
690             return WriteClientError(pfc, "Shibboleth Filter detected unexpected IIS error.");
691     }
692     catch (const std::exception& e) {
693         Category::getInstance(SHIBSP_LOGCAT ".ISAPI").error("ISAPI filter caught an exception: %s", e.what());
694         return WriteClientError(pfc, "Shibboleth Filter caught an exception, check Event Log for details.");
695     }
696     catch(...) {
697         Category::getInstance(SHIBSP_LOGCAT ".ISAPI").crit("ISAPI extension caught an unknown exception");
698         if (g_catchAll)
699             return WriteClientError(pfc, "Shibboleth Filter threw an unknown exception.");
700         throw;
701     }
702     return WriteClientError(pfc, "Shibboleth Filter reached unreachable code, save my walrus!");
703 }
704 
705 
706 /****************************************************************************/
707 // ISAPI Extension
708 
WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB,const char * msg)709 DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg)
710 {
711     static char _status[] = "200 OK";
712     static char ctype[] = "Connection: close\r\nContent-Type: text/html\r\n\r\n";
713     lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,_status,0,(LPDWORD)ctype);
714     static char xmsg[] = "<HTML><HEAD><TITLE>Shibboleth Error</TITLE></HEAD><BODY><H1>Shibboleth Error</H1>";
715     DWORD resplen=strlen(xmsg);
716     lpECB->WriteClient(lpECB->ConnID,xmsg,&resplen,HSE_IO_SYNC);
717     resplen=strlen(msg);
718     lpECB->WriteClient(lpECB->ConnID,const_cast<char*>(msg),&resplen,HSE_IO_SYNC);
719     static char xmsg2[] = "</BODY></HTML>";
720     resplen=strlen(xmsg2);
721     lpECB->WriteClient(lpECB->ConnID,xmsg2,&resplen,HSE_IO_SYNC);
722     return HSE_STATUS_SUCCESS;
723 }
724 
725 
726 class ShibTargetIsapiE : public AbstractSPRequest
727 {
728   LPEXTENSION_CONTROL_BLOCK m_lpECB;
729   multimap<string,string> m_headers;
730   mutable vector<string> m_certs;
731   mutable string m_body;
732   mutable bool m_gotBody;
733   int m_port;
734   string m_scheme,m_hostname,m_uri;
735   mutable string m_remote_addr,m_remote_user;
736 
737 public:
ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB,const site_t & site)738   ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site)
739       : AbstractSPRequest(SHIBSP_LOGCAT ".ISAPI"), m_lpECB(lpECB), m_gotBody(false) {
740     static char _HTTPS[] = "HTTPS";
741     static char _URL[] = "URL";
742     static char _SERVER_PORT[] = "SERVER_PORT";
743     static char _SERVER_NAME[] = "SERVER_NAME";
744 
745     dynabuf ssl(5);
746     GetServerVariable(_HTTPS,ssl,5);
747     bool SSL=(ssl=="on" || ssl=="ON");
748 
749     // Scheme may come from site def or be derived from IIS.
750     m_scheme = site.m_scheme;
751     if (m_scheme.empty() || !g_bNormalizeRequest)
752         m_scheme = SSL ? "https" : "http";
753 
754     // URL path always come from IIS.
755     dynabuf url(256);
756     GetServerVariable(_URL,url,255);
757 
758     // Port may come from IIS or from site def.
759     if (!g_bNormalizeRequest || (SSL && site.m_sslport.empty()) || (!SSL && site.m_port.empty())) {
760         dynabuf port(11);
761         GetServerVariable(_SERVER_PORT,port,10);
762         if (port.empty()) {
763             m_port = SSL ? 443 : 80;
764         }
765         else {
766             m_port = atoi(port);
767         }
768     }
769     else if (SSL) {
770         m_port = atoi(site.m_sslport.c_str());
771     }
772     else {
773         m_port = atoi(site.m_port.c_str());
774     }
775 
776     dynabuf var(32);
777     GetServerVariable(_SERVER_NAME, var, 32);
778     if (var.empty()) {
779         m_hostname = site.m_name;
780     }
781     else {
782         // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name.
783         m_hostname=var;
784         if (site.m_name != m_hostname && site.m_aliases.find(m_hostname) == site.m_aliases.end())
785             m_hostname = site.m_name;
786     }
787 
788     /*
789      * IIS screws us over on PATH_INFO (the hits keep on coming). We need to figure out if
790      * the server is set up for proper PATH_INFO handling, or "IIS sucks rabid weasels mode",
791      * which is the default. No perfect way to tell, but we can take a good guess by checking
792      * whether the URL is a substring of the PATH_INFO:
793      *
794      * e.g. for /Shibboleth.sso/SAML/POST
795      *
796      *  Bad mode (default):
797      *      URL:        /Shibboleth.sso
798      *      PathInfo:   /Shibboleth.sso/SAML/POST
799      *
800      *  Good mode:
801      *      URL:        /Shibboleth.sso
802      *      PathInfo:   /SAML/POST
803      */
804 
805     string uri;
806 
807     // Clearly we're only in bad mode if path info exists at all.
808     if (lpECB->lpszPathInfo && *(lpECB->lpszPathInfo)) {
809         if (strstr(lpECB->lpszPathInfo,url))
810             // Pretty good chance we're in bad mode, unless the PathInfo repeats the path itself.
811             uri = lpECB->lpszPathInfo;
812         else {
813             if (!url.empty())
814                 uri = url;
815             uri += lpECB->lpszPathInfo;
816         }
817     }
818     else if (!url.empty()) {
819         uri = url;
820     }
821 
822     // For consistency with Apache, let's add the query string.
823     if (lpECB->lpszQueryString && *(lpECB->lpszQueryString)) {
824         uri += '?';
825         uri += lpECB->lpszQueryString;
826     }
827 
828     setRequestURI(uri.c_str());
829   }
~ShibTargetIsapiE()830   ~ShibTargetIsapiE() {}
831 
getScheme() const832   const char* getScheme() const {
833     return m_scheme.c_str();
834   }
getHostname() const835   const char* getHostname() const {
836     return m_hostname.c_str();
837   }
getPort() const838   int getPort() const {
839     return m_port;
840   }
getMethod() const841   const char* getMethod() const {
842     return m_lpECB->lpszMethod;
843   }
getContentType() const844   string getContentType() const {
845     return m_lpECB->lpszContentType ? m_lpECB->lpszContentType : "";
846   }
getContentLength() const847   long getContentLength() const {
848       return m_lpECB->cbTotalBytes;
849   }
getRemoteUser() const850   string getRemoteUser() const {
851     static char _REMOTE_USER[] = "REMOTE_USER";
852     if (m_remote_user.empty()) {
853         dynabuf var(16);
854         GetServerVariable(_REMOTE_USER, var, 32, false);
855         if (!var.empty())
856             m_remote_user = var;
857     }
858     return m_remote_user;
859   }
getRemoteAddr() const860   string getRemoteAddr() const {
861       static char _REMOTE_ADDR[] = "REMOTE_ADDR";
862     m_remote_addr = AbstractSPRequest::getRemoteAddr();
863     if (m_remote_addr.empty()) {
864         dynabuf var(16);
865         GetServerVariable(_REMOTE_ADDR, var, 16, false);
866         if (!var.empty())
867             m_remote_addr = var;
868     }
869     return m_remote_addr;
870   }
getHeader(const char * name) const871   string getHeader(const char* name) const {
872     string hdr("HTTP_");
873     for (; *name; ++name) {
874         if (*name == '-')
875             hdr += '_';
876         else
877             hdr += toupper(*name);
878     }
879     dynabuf buf(128);
880     GetServerVariable(const_cast<char*>(hdr.c_str()), buf, 128, false);
881     return buf.empty() ? "" : static_cast<char*>(buf);
882   }
setResponseHeader(const char * name,const char * value,bool replace=false)883   void setResponseHeader(const char* name, const char* value, bool replace = false) {
884       HTTPResponse::setResponseHeader(name, value, replace);
885       if (name && *name) {
886           // Set for later.
887           if (replace || !value)
888               m_headers.erase(name);
889           if (value && *value)
890               m_headers.insert(make_pair(name, value));
891       }
892   }
getQueryString() const893   const char* getQueryString() const {
894     return m_lpECB->lpszQueryString;
895   }
getRequestBody() const896   const char* getRequestBody() const {
897     if (m_gotBody)
898         return m_body.c_str();
899     if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB?
900         throw opensaml::SecurityPolicyException("Size of request body exceeded 1M size limit.");
901     else if (m_lpECB->cbTotalBytes > m_lpECB->cbAvailable) {
902       m_gotBody=true;
903       DWORD datalen=m_lpECB->cbTotalBytes;
904       if (m_lpECB->cbAvailable > 0) {
905         m_body.assign(reinterpret_cast<char*>(m_lpECB->lpbData),m_lpECB->cbAvailable);
906         datalen-=m_lpECB->cbAvailable;
907       }
908       char buf[8192];
909       while (datalen) {
910         DWORD buflen=8192;
911         BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen);
912         if (!ret) {
913             char message[65];
914             _snprintf(message, 64, "Error reading request body from browser (%x).", GetLastError());
915             throw IOException(message);
916         }
917         else if (!buflen)
918             throw IOException("Socket closed while reading request body from browser.");
919         m_body.append(buf, buflen);
920         datalen-=buflen;
921       }
922     }
923     else if (m_lpECB->cbAvailable) {
924         m_gotBody=true;
925         m_body.assign(reinterpret_cast<char*>(m_lpECB->lpbData),m_lpECB->cbAvailable);
926     }
927     return m_body.c_str();
928   }
sendResponse(istream & in,long status)929   long sendResponse(istream& in, long status) {
930     string hdr = string("Connection: close\r\n");
931     for (multimap<string,string>::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i)
932         hdr += i->first + ": " + i->second + "\r\n";
933     hdr += "\r\n";
934 
935     static char okstr[] = "200 OK";
936     static char notmodstr[] = "304 Not Modified";
937     static char authzstr[] = "401 Authorization Required";
938     static char forbiddenstr[] = "403 Forbidden";
939     static char notfoundstr[] = "404 Not Found";
940     static char errorstr[] = "500 Server Error";
941 
942     char* str = nullptr;
943 
944     switch (status) {
945         case XMLTOOLING_HTTP_STATUS_NOTMODIFIED:    str = notmodstr; break;
946         case XMLTOOLING_HTTP_STATUS_UNAUTHORIZED:   str = authzstr; break;
947         case XMLTOOLING_HTTP_STATUS_FORBIDDEN:      str = forbiddenstr; break;
948         case XMLTOOLING_HTTP_STATUS_NOTFOUND:       str = notfoundstr; break;
949         case XMLTOOLING_HTTP_STATUS_ERROR:          str = errorstr; break;
950 
951         default: str = okstr;
952     }
953     m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, str, 0, (LPDWORD)hdr.c_str());
954     char buf[1024];
955     while (in) {
956         in.read(buf,1024);
957         DWORD resplen = in.gcount();
958         m_lpECB->WriteClient(m_lpECB->ConnID, buf, &resplen, HSE_IO_SYNC);
959     }
960     return HSE_STATUS_SUCCESS;
961   }
sendRedirect(const char * url)962   long sendRedirect(const char* url) {
963     static char _status[] = "302 Moved";
964 
965     HTTPResponse::sendRedirect(url);
966     string hdr=string("Location: ") + url + "\r\n"
967       "Content-Type: text/html\r\n"
968       "Content-Length: 40\r\n"
969       "Expires: Wed, 01 Jan 1997 12:00:00 GMT\r\n"
970       "Cache-Control: private,no-store,no-cache,max-age=0\r\n";
971     for (multimap<string,string>::const_iterator i = m_headers.begin(); i != m_headers.end(); ++i)
972         hdr += i->first + ": " + i->second + "\r\n";
973     hdr += "\r\n";
974     m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, _status, 0, (LPDWORD)hdr.c_str());
975     static char redmsg[] = "<HTML><BODY>Redirecting...</BODY></HTML>";
976     DWORD resplen=40;
977     m_lpECB->WriteClient(m_lpECB->ConnID, redmsg, &resplen, HSE_IO_SYNC);
978     return HSE_STATUS_SUCCESS;
979   }
980   // Decline happens in the POST processor if this isn't the handler url
981   // Note that it can also happen with HTAccess, but we don't support that, yet.
returnDecline()982   long returnDecline() {
983     return WriteClientError(
984         m_lpECB,
985         "ISAPI extension can only be invoked to process Shibboleth protocol requests."
986 		"Make sure the mapped file extension doesn't match actual content."
987         );
988   }
returnOK()989   long returnOK() {
990       return HSE_STATUS_SUCCESS;
991   }
992 
getClientCertificates() const993   const vector<string>& getClientCertificates() const {
994       if (m_certs.empty()) {
995         char CertificateBuf[8192];
996         CERT_CONTEXT_EX ccex;
997         ccex.cbAllocated = sizeof(CertificateBuf);
998         ccex.CertContext.pbCertEncoded = (BYTE*)CertificateBuf;
999         DWORD dwSize = sizeof(ccex);
1000 
1001         if (m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_GET_CERT_INFO_EX, (LPVOID)&ccex, (LPDWORD)dwSize, nullptr)) {
1002             if (ccex.CertContext.cbCertEncoded) {
1003                 XMLSize_t outlen;
1004                 XMLByte* serialized = Base64::encode(reinterpret_cast<XMLByte*>(CertificateBuf), ccex.CertContext.cbCertEncoded, &outlen);
1005                 m_certs.push_back(reinterpret_cast<char*>(serialized));
1006                 XMLString::release((char**)&serialized);
1007             }
1008         }
1009       }
1010       return m_certs;
1011   }
1012 
1013   // Not used in the extension.
clearHeader(const char * rawname,const char * cginame)1014   void clearHeader(const char* rawname, const char* cginame) { throw runtime_error("clearHeader not implemented"); }
setHeader(const char * name,const char * value)1015   void setHeader(const char* name, const char* value) { throw runtime_error("setHeader not implemented"); }
setRemoteUser(const char * user)1016   void setRemoteUser(const char* user) { throw runtime_error("setRemoteUser not implemented"); }
1017 
GetServerVariable(LPSTR lpszVariable,dynabuf & s,DWORD size=80,bool bRequired=true) const1018   void GetServerVariable(LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true) const {
1019     s.reserve(size);
1020     s.erase();
1021     size=s.size();
1022 
1023     while (!m_lpECB->GetServerVariable(m_lpECB->ConnID,lpszVariable,s,&size)) {
1024         // Grumble. Check the error.
1025         DWORD e=GetLastError();
1026         if (e==ERROR_INSUFFICIENT_BUFFER)
1027             s.reserve(size);
1028         else
1029             break;
1030     }
1031     if (bRequired && s.empty()) {
1032         log(SPRequest::SPError, string("missing required server variable: ") + lpszVariable);
1033     }
1034   }
1035 };
1036 
GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB,LPSTR lpszVariable,dynabuf & s,DWORD size=80,bool bRequired=true)1037 void GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
1038 {
1039     s.reserve(size);
1040     s.erase();
1041     size=s.size();
1042 
1043     while (!lpECB->GetServerVariable(lpECB->ConnID,lpszVariable,s,&size)) {
1044         // Grumble. Check the error.
1045         DWORD e=GetLastError();
1046         if (e==ERROR_INSUFFICIENT_BUFFER)
1047             s.reserve(size);
1048         else
1049             break;
1050     }
1051     if (bRequired && s.empty()) {
1052         Category::getInstance(SHIBSP_LOGCAT ".ISAPI").error("missing required server variable: %s", lpszVariable);
1053     }
1054 }
1055 
HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)1056 extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
1057 {
1058     static char _INSTANCE_ID[] = "INSTANCE_ID";
1059 
1060     try {
1061         string threadid("[");
1062         threadid += lexical_cast<string>(getpid()) + "] isapi_shib_extension";
1063         xmltooling::NDC ndc(threadid.c_str());
1064 
1065         // Determine web site number. This can't really fail, I don't think.
1066         dynabuf buf(128);
1067         GetServerVariable(lpECB,_INSTANCE_ID,buf,10);
1068         if (buf.empty())
1069             return WriteClientError(lpECB, "Shibboleth Extension failed to obtain INSTANCE_ID server variable.");
1070 
1071         // Match site instance to host name, skip if no match.
1072         map<string,site_t>::const_iterator map_i = g_Sites.find(static_cast<char*>(buf));
1073         if (map_i == g_Sites.end())
1074             return WriteClientError(lpECB, "Shibboleth Extension not configured for web site (check ISAPI mappings in SP configuration).");
1075 
1076         ShibTargetIsapiE ste(lpECB, map_i->second);
1077         pair<bool,long> res = ste.getServiceProvider().doHandler(ste);
1078         if (res.first) return res.second;
1079 
1080         return WriteClientError(lpECB, "Shibboleth Extension failed to process request");
1081 
1082     }
1083     catch(const bad_alloc&) {
1084         return WriteClientError(lpECB, "Out of Memory");
1085     }
1086     catch(long e) {
1087         if (e==ERROR_NO_DATA)
1088             return WriteClientError(lpECB, "A required variable or header was empty.");
1089         else
1090             return WriteClientError(lpECB, "Server detected unexpected IIS error.");
1091     }
1092     catch (const std::exception& e) {
1093         Category::getInstance(SHIBSP_LOGCAT ".ISAPI").error("ISAPI extension caught an exception: %s", e.what());
1094         return WriteClientError(lpECB, "Shibboleth Extension caught an exception, check native log for details.");
1095     }
1096     catch(...) {
1097         Category::getInstance(SHIBSP_LOGCAT ".ISAPI").crit("ISAPI filter caught an unknown exception");
1098         if (g_catchAll)
1099             return WriteClientError(lpECB, "Shibboleth Extension threw an unknown exception.");
1100         throw;
1101     }
1102 
1103     // If we get here we've got an error.
1104     return HSE_STATUS_ERROR;
1105 }
1106