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