838859ae23882913714c3e35af755b06af4fdb2a
[shibboleth/sp.git] / isapi_shib / isapi_shib.cpp
1 /*
2  * The Shibboleth License, Version 1.
3  * Copyright (c) 2002
4  * University Corporation for Advanced Internet Development, Inc.
5  * All rights reserved
6  *
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions are met:
10  *
11  * Redistributions of source code must retain the above copyright notice, this
12  * list of conditions and the following disclaimer.
13  *
14  * Redistributions in binary form must reproduce the above copyright notice,
15  * this list of conditions and the following disclaimer in the documentation
16  * and/or other materials provided with the distribution, if any, must include
17  * the following acknowledgment: "This product includes software developed by
18  * the University Corporation for Advanced Internet Development
19  * <http://www.ucaid.edu>Internet2 Project. Alternately, this acknowledegement
20  * may appear in the software itself, if and wherever such third-party
21  * acknowledgments normally appear.
22  *
23  * Neither the name of Shibboleth nor the names of its contributors, nor
24  * Internet2, nor the University Corporation for Advanced Internet Development,
25  * Inc., nor UCAID may be used to endorse or promote products derived from this
26  * software without specific prior written permission. For written permission,
27  * please contact shibboleth@shibboleth.org
28  *
29  * Products derived from this software may not be called Shibboleth, Internet2,
30  * UCAID, or the University Corporation for Advanced Internet Development, nor
31  * may Shibboleth appear in their name, without prior written permission of the
32  * University Corporation for Advanced Internet Development.
33  *
34  *
35  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
36  * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
38  * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK
39  * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE.
40  * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY
41  * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT,
42  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
43  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
44  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
45  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
47  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 /* isapi_shib.cpp - Shibboleth ISAPI filter
51
52    Scott Cantor
53    8/23/02
54 */
55
56 #include "config_win32.h"
57
58 // SAML Runtime
59 #include <saml/saml.h>
60 #include <shib/shib.h>
61 #include <shib/shib-threads.h>
62 #include <shib-target/shib-target.h>
63
64 #include <ctime>
65 #include <fstream>
66 #include <sstream>
67 #include <stdexcept>
68 #include <process.h>
69
70 #include <httpfilt.h>
71 #include <httpext.h>
72
73 using namespace std;
74 using namespace saml;
75 using namespace shibboleth;
76 using namespace shibtarget;
77
78 // globals
79 namespace {
80     static const XMLCh name[] = { chLatin_n, chLatin_a, chLatin_m, chLatin_e, chNull };
81     static const XMLCh port[] = { chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
82     static const XMLCh sslport[] = { chLatin_s, chLatin_s, chLatin_l, chLatin_p, chLatin_o, chLatin_r, chLatin_t, chNull };
83     static const XMLCh scheme[] = { chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_e, chNull };
84     static const XMLCh id[] = { chLatin_i, chLatin_d, chNull };
85     static const XMLCh Implementation[] =
86     { 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 };
87     static const XMLCh ISAPI[] = { chLatin_I, chLatin_S, chLatin_A, chLatin_P, chLatin_I, chNull };
88     static const XMLCh Alias[] = { chLatin_A, chLatin_l, chLatin_i, chLatin_a, chLatin_s, chNull };
89     static const XMLCh normalizeRequest[] =
90     { chLatin_n, chLatin_o, chLatin_r, chLatin_m, chLatin_a, chLatin_l, chLatin_i, chLatin_z, chLatin_e,
91       chLatin_R, chLatin_e, chLatin_q, chLatin_u, chLatin_e, chLatin_s, chLatin_t, chNull
92     };
93     static const XMLCh Site[] = { chLatin_S, chLatin_i, chLatin_t, chLatin_e, chNull };
94
95     struct site_t {
96         site_t(const DOMElement* e)
97         {
98             auto_ptr_char n(e->getAttributeNS(NULL,name));
99             auto_ptr_char s(e->getAttributeNS(NULL,scheme));
100             auto_ptr_char p(e->getAttributeNS(NULL,port));
101             auto_ptr_char p2(e->getAttributeNS(NULL,sslport));
102             if (n.get()) m_name=n.get();
103             if (s.get()) m_scheme=s.get();
104             if (p.get()) m_port=p.get();
105             if (p2.get()) m_sslport=p2.get();
106             DOMNodeList* nlist=e->getElementsByTagNameNS(shibtarget::XML::SHIBTARGET_NS,Alias);
107             for (int i=0; nlist && i<nlist->getLength(); i++) {
108                 if (nlist->item(i)->hasChildNodes()) {
109                     auto_ptr_char alias(nlist->item(i)->getFirstChild()->getNodeValue());
110                     m_aliases.insert(alias.get());
111                 }
112             }
113         }
114         string m_scheme,m_port,m_sslport,m_name;
115         set<string> m_aliases;
116     };
117     
118     HINSTANCE g_hinstDLL;
119     ShibTargetConfig* g_Config = NULL;
120     map<string,site_t> g_Sites;
121     bool g_bNormalizeRequest = true;
122 }
123
124 BOOL LogEvent(
125     LPCSTR  lpUNCServerName,
126     WORD  wType,
127     DWORD  dwEventID,
128     PSID  lpUserSid,
129     LPCSTR  message)
130 {
131     LPCSTR  messages[] = {message, NULL};
132     
133     HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth ISAPI Filter");
134     BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL);
135     return (DeregisterEventSource(hElog) && res);
136 }
137
138 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
139 {
140     if (fdwReason==DLL_PROCESS_ATTACH)
141         g_hinstDLL=hinstDLL;
142     return TRUE;
143 }
144
145 extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer)
146 {
147     if (!pVer)
148         return FALSE;
149         
150     if (!g_Config)
151     {
152         LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
153                 "Extension mode startup not possible, is the DLL loaded as a filter?");
154         return FALSE;
155     }
156
157     pVer->dwExtensionVersion=HSE_VERSION;
158     strncpy(pVer->lpszExtensionDesc,"Shibboleth ISAPI Extension",HSE_MAX_EXT_DLL_NAME_LEN-1);
159     return TRUE;
160 }
161
162 extern "C" BOOL WINAPI TerminateExtension(DWORD)
163 {
164     return TRUE;    // cleanup should happen when filter unloads
165 }
166
167 extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
168 {
169     if (!pVer)
170         return FALSE;
171     else if (g_Config) {
172         LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
173                 "Reentrant filter initialization, ignoring...");
174         return TRUE;
175     }
176
177 #ifndef _DEBUG
178     try
179     {
180 #endif
181         LPCSTR schemadir=getenv("SHIBSCHEMAS");
182         if (!schemadir)
183             schemadir=SHIB_SCHEMAS;
184         LPCSTR config=getenv("SHIBCONFIG");
185         if (!config)
186             config=SHIB_CONFIG;
187         g_Config=&ShibTargetConfig::getConfig();
188         g_Config->setFeatures(
189             ShibTargetConfig::Listener |
190             ShibTargetConfig::Metadata |
191             ShibTargetConfig::AAP |
192             ShibTargetConfig::RequestMapper |
193             ShibTargetConfig::LocalExtensions |
194             ShibTargetConfig::Logging
195             );
196         if (!g_Config->init(schemadir)) {
197             g_Config=NULL;
198             LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
199                     "Filter startup failed during library initialization, check native log for help.");
200             return FALSE;
201         }
202         else if (!g_Config->load(config)) {
203             g_Config=NULL;
204             LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
205                     "Filter startup failed to load configuration, check native log for help.");
206             return FALSE;
207         }
208         
209         // Access the implementation-specifics for site mappings.
210         IConfig* conf=g_Config->getINI();
211         Locker locker(conf);
212         const IPropertySet* props=conf->getPropertySet("Local");
213         if (props) {
214             const DOMElement* impl=saml::XML::getFirstChildElement(
215                 props->getElement(),shibtarget::XML::SHIBTARGET_NS,Implementation
216                 );
217             if (impl && (impl=saml::XML::getFirstChildElement(impl,shibtarget::XML::SHIBTARGET_NS,ISAPI))) {
218                 const XMLCh* flag=impl->getAttributeNS(NULL,normalizeRequest);
219                 g_bNormalizeRequest=(!flag || !*flag || *flag==chDigit_1 || *flag==chLatin_t);
220                 impl=saml::XML::getFirstChildElement(impl,shibtarget::XML::SHIBTARGET_NS,Site);
221                 while (impl) {
222                     auto_ptr_char id(impl->getAttributeNS(NULL,id));
223                     if (id.get())
224                         g_Sites.insert(pair<string,site_t>(id.get(),site_t(impl)));
225                     impl=saml::XML::getNextSiblingElement(impl,shibtarget::XML::SHIBTARGET_NS,Site);
226                 }
227             }
228         }
229 #ifndef _DEBUG
230     }
231     catch (...)
232     {
233         LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Filter startup failed with an exception.");
234         return FALSE;
235     }
236 #endif
237
238     pVer->dwFilterVersion=HTTP_FILTER_REVISION;
239     strncpy(pVer->lpszFilterDesc,"Shibboleth ISAPI Filter",SF_MAX_FILTER_DESC_LEN);
240     pVer->dwFlags=(SF_NOTIFY_ORDER_HIGH |
241                    SF_NOTIFY_SECURE_PORT |
242                    SF_NOTIFY_NONSECURE_PORT |
243                    SF_NOTIFY_PREPROC_HEADERS |
244                    SF_NOTIFY_LOG);
245     LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter initialized...");
246     return TRUE;
247 }
248
249 extern "C" BOOL WINAPI TerminateFilter(DWORD)
250 {
251     if (g_Config)
252         g_Config->shutdown();
253     g_Config = NULL;
254     LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter shut down...");
255     return TRUE;
256 }
257
258 /* Next up, some suck-free versions of various APIs.
259
260    You DON'T require people to guess the buffer size and THEN tell them the right size.
261    Returning an LPCSTR is apparently way beyond their ken. Not to mention the fact that
262    constant strings aren't typed as such, making it just that much harder. These versions
263    are now updated to use a special growable buffer object, modeled after the standard
264    string class. The standard string won't work because they left out the option to
265    pre-allocate a non-constant buffer.
266 */
267
268 class dynabuf
269 {
270 public:
271     dynabuf() { bufptr=NULL; buflen=0; }
272     dynabuf(size_t s) { bufptr=new char[buflen=s]; *bufptr=0; }
273     ~dynabuf() { delete[] bufptr; }
274     size_t length() const { return bufptr ? strlen(bufptr) : 0; }
275     size_t size() const { return buflen; }
276     bool empty() const { return length()==0; }
277     void reserve(size_t s, bool keep=false);
278     void erase() { if (bufptr) memset(bufptr,0,buflen); }
279     operator char*() { return bufptr; }
280     bool operator ==(const char* s) const;
281     bool operator !=(const char* s) const { return !(*this==s); }
282 private:
283     char* bufptr;
284     size_t buflen;
285 };
286
287 void dynabuf::reserve(size_t s, bool keep)
288 {
289     if (s<=buflen)
290         return;
291     char* p=new char[s];
292     if (keep)
293         while (buflen--)
294             p[buflen]=bufptr[buflen];
295     buflen=s;
296     delete[] bufptr;
297     bufptr=p;
298 }
299
300 bool dynabuf::operator==(const char* s) const
301 {
302     if (buflen==NULL || s==NULL)
303         return (buflen==NULL && s==NULL);
304     else
305         return strcmp(bufptr,s)==0;
306 }
307
308 void GetServerVariable(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
309     throw (bad_alloc, DWORD)
310 {
311     s.reserve(size);
312     s.erase();
313     size=s.size();
314
315     while (!pfc->GetServerVariable(pfc,lpszVariable,s,&size))
316     {
317         // Grumble. Check the error.
318         DWORD e=GetLastError();
319         if (e==ERROR_INSUFFICIENT_BUFFER)
320             s.reserve(size);
321         else
322             break;
323     }
324     if (bRequired && s.empty())
325         throw ERROR_NO_DATA;
326 }
327
328 void GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
329     throw (bad_alloc, DWORD)
330 {
331     s.reserve(size);
332     s.erase();
333     size=s.size();
334
335     while (!lpECB->GetServerVariable(lpECB->ConnID,lpszVariable,s,&size))
336     {
337         // Grumble. Check the error.
338         DWORD e=GetLastError();
339         if (e==ERROR_INSUFFICIENT_BUFFER)
340             s.reserve(size);
341         else
342             break;
343     }
344     if (bRequired && s.empty())
345         throw ERROR_NO_DATA;
346 }
347
348 void GetHeader(PHTTP_FILTER_PREPROC_HEADERS pn, PHTTP_FILTER_CONTEXT pfc,
349                LPSTR lpszName, dynabuf& s, DWORD size=80, bool bRequired=true)
350     throw (bad_alloc, DWORD)
351 {
352     s.reserve(size);
353     s.erase();
354     size=s.size();
355
356     while (!pn->GetHeader(pfc,lpszName,s,&size))
357     {
358         // Grumble. Check the error.
359         DWORD e=GetLastError();
360         if (e==ERROR_INSUFFICIENT_BUFFER)
361             s.reserve(size);
362         else
363             break;
364     }
365     if (bRequired && s.empty())
366         throw ERROR_NO_DATA;
367 }
368
369 /****************************************************************************/
370 // ISAPI Filter
371
372 class ShibTargetIsapiF : public ShibTarget
373 {
374   PHTTP_FILTER_CONTEXT m_pfc;
375   PHTTP_FILTER_PREPROC_HEADERS m_pn;
376   string m_cookie;
377 public:
378   ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const site_t& site) {
379
380     // URL path always come from IIS.
381     dynabuf url(256);
382     GetHeader(pn,pfc,"url",url,256,false);
383
384     // Port may come from IIS or from site def.
385     dynabuf port(11);
386     if (!g_bNormalizeRequest || (pfc->fIsSecurePort && site.m_sslport.empty()) || (!pfc->fIsSecurePort && site.m_port.empty()))
387         GetServerVariable(pfc,"SERVER_PORT",port,10);
388     else if (pfc->fIsSecurePort) {
389         strncpy(port,site.m_sslport.c_str(),10);
390         static_cast<char*>(port)[10]=0;
391     }
392     else {
393         strncpy(port,site.m_port.c_str(),10);
394         static_cast<char*>(port)[10]=0;
395     }
396     
397     // Scheme may come from site def or be derived from IIS.
398     const char* scheme=site.m_scheme.c_str();
399     if (!scheme || !*scheme || !g_bNormalizeRequest)
400         scheme=pfc->fIsSecurePort ? "https" : "http";
401
402     // Get the rest of the server variables.
403     dynabuf remote_addr(16),method(5),content_type(32),hostname(32);
404     GetServerVariable(pfc,"SERVER_NAME",hostname,32);
405     GetServerVariable(pfc,"REMOTE_ADDR",remote_addr,16);
406     GetServerVariable(pfc,"REQUEST_METHOD",method,5,false);
407     GetServerVariable(pfc,"CONTENT_TYPE",content_type,32,false);
408
409     // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name.
410     const char* host=hostname;
411     if (site.m_name!=host && site.m_aliases.find(host)==site.m_aliases.end())
412         host=site.m_name.c_str();
413
414     init(scheme, host, atoi(port), url, content_type, remote_addr, method); 
415
416     m_pfc = pfc;
417     m_pn = pn;
418   }
419   ~ShibTargetIsapiF() { }
420
421   virtual void log(ShibLogLevel level, const string &msg) {
422     ShibTarget::log(level,msg);
423     if (level == LogLevelError)
424         LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str());
425   }
426   virtual string getCookies() const {
427     dynabuf buf(128);
428     GetHeader(m_pn, m_pfc, "Cookie:", buf, 128, false);
429     return buf.empty() ? "" : buf;
430   }
431   
432   virtual void clearHeader(const string &name) {
433     string hdr = name + ":";
434     m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()), "");
435   }
436   virtual void setHeader(const string &name, const string &value) {
437     string hdr = name + ":";
438     m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()),
439                     const_cast<char*>(value.c_str()));
440   }
441   virtual string getHeader(const string &name) {
442     string hdr = name + ":";
443     dynabuf buf(1024);
444     GetHeader(m_pn, m_pfc, const_cast<char*>(hdr.c_str()), buf, 1024, false);
445     return string(buf);
446   }
447   virtual void setRemoteUser(const string &user) {
448     setHeader(string("remote-user"), user);
449   }
450   virtual string getRemoteUser(void) {
451     return getHeader(string("remote-user"));
452   }
453   virtual void* sendPage(
454     const string& msg,
455     int code=200,
456     const string& content_type="text/html",
457     const Iterator<header_t>& headers=EMPTY(header_t)) {
458     string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n";
459     while (headers.hasNext()) {
460         const header_t& h=headers.next();
461         hdr += h.first + ": " + h.second + "\r\n";
462     }
463     hdr += "\r\n";
464     const char* codestr="200 OK";
465     switch (code) {
466         case 403:   codestr="403 Forbidden"; break;
467         case 404:   codestr="404 Not Found"; break;
468         case 500:   codestr="500 Server Error"; break;
469     }
470     m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, (void*)codestr, (DWORD)hdr.c_str(), 0);
471     DWORD resplen = msg.size();
472     m_pfc->WriteClient(m_pfc, (LPVOID)msg.c_str(), &resplen, 0);
473     return (void*)SF_STATUS_REQ_FINISHED;
474   }
475   virtual void* sendRedirect(const string& url) {
476     // XXX: Don't support the httpRedirect option, yet.
477     string hdrs=m_cookie + string("Location: ") + url + "\r\n"
478       "Content-Type: text/html\r\n"
479       "Content-Length: 40\r\n"
480       "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
481       "Cache-Control: private,no-store,no-cache\r\n\r\n";
482     m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER,
483                                  "302 Please Wait", (DWORD)hdrs.c_str(), 0);
484     static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
485     DWORD resplen=40;
486     m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0);
487     return reinterpret_cast<void*>(SF_STATUS_REQ_FINISHED);
488   }
489   // XXX: We might not ever hit the 'decline' status in this filter.
490   //virtual void* returnDecline(void) { }
491   virtual void* returnOK(void) { return (void*) SF_STATUS_REQ_NEXT_NOTIFICATION; }
492
493   // The filter never processes the POST, so stub these methods.
494   virtual void setCookie(const string &name, const string &value) {
495     // Set the cookie for later.  Use it during the redirect.
496     m_cookie += "Set-Cookie: " + name + "=" + value + "\r\n";
497   }
498   virtual string getArgs(void) { throw runtime_error("getArgs not implemented"); }
499   virtual string getPostData(void) { throw runtime_error("getPostData not implemented"); }
500 };
501
502 DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg)
503 {
504     LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
505     static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
506     pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0);
507     static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Filter Error</TITLE></HEAD><BODY>"
508                             "<H1>Shibboleth Filter Error</H1>";
509     DWORD resplen=strlen(xmsg);
510     pfc->WriteClient(pfc,(LPVOID)xmsg,&resplen,0);
511     resplen=strlen(msg);
512     pfc->WriteClient(pfc,(LPVOID)msg,&resplen,0);
513     static const char* xmsg2="</BODY></HTML>";
514     resplen=strlen(xmsg2);
515     pfc->WriteClient(pfc,(LPVOID)xmsg2,&resplen,0);
516     return SF_STATUS_REQ_FINISHED;
517 }
518
519 extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
520 {
521     // Is this a log notification?
522     if (notificationType==SF_NOTIFY_LOG)
523     {
524         if (pfc->pFilterContext)
525             ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=static_cast<LPCSTR>(pfc->pFilterContext);
526         return SF_STATUS_REQ_NEXT_NOTIFICATION;
527     }
528
529     PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification;
530     try
531     {
532         // Determine web site number. This can't really fail, I don't think.
533         dynabuf buf(128);
534         GetServerVariable(pfc,"INSTANCE_ID",buf,10);
535
536         // Match site instance to host name, skip if no match.
537         map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
538         if (map_i==g_Sites.end())
539             return SF_STATUS_REQ_NEXT_NOTIFICATION;
540             
541         ostringstream threadid;
542         threadid << "[" << getpid() << "] isapi_shib" << '\0';
543         saml::NDC ndc(threadid.str().c_str());
544
545         ShibTargetIsapiF stf(pfc, pn, map_i->second);
546
547         // "false" because we don't override the Shib settings
548         pair<bool,void*> res = stf.doCheckAuthN();
549         if (res.first) return (DWORD)res.second;
550
551         // "false" because we don't override the Shib settings
552         res = stf.doExportAssertions();
553         if (res.first) return (DWORD)res.second;
554
555         res = stf.doCheckAuthZ();
556         if (res.first) return (DWORD)res.second;
557
558         return SF_STATUS_REQ_NEXT_NOTIFICATION;
559     }
560     catch(bad_alloc) {
561         return WriteClientError(pfc,"Out of Memory");
562     }
563     catch(long e) {
564         if (e==ERROR_NO_DATA)
565             return WriteClientError(pfc,"A required variable or header was empty.");
566         else
567             return WriteClientError(pfc,"Server detected unexpected IIS error.");
568     }
569 #ifndef _DEBUG
570     catch(...) {
571         return WriteClientError(pfc,"Server caught an unknown exception.");
572     }
573 #endif
574
575     return WriteClientError(pfc,"Server reached unreachable code, save my walrus!");
576 }
577         
578
579 #if 0
580 IRequestMapper::Settings map_request(
581     PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, IRequestMapper* mapper, const site_t& site, string& target
582     )
583 {
584     // URL path always come from IIS.
585     dynabuf url(256);
586     GetHeader(pn,pfc,"url",url,256,false);
587
588     // Port may come from IIS or from site def.
589     dynabuf port(11);
590     if (site.m_port.empty() || !g_bNormalizeRequest)
591         GetServerVariable(pfc,"SERVER_PORT",port,10);
592     else {
593         strncpy(port,site.m_port.c_str(),10);
594         static_cast<char*>(port)[10]=0;
595     }
596     
597     // Scheme may come from site def or be derived from IIS.
598     const char* scheme=site.m_scheme.c_str();
599     if (!scheme || !*scheme || !g_bNormalizeRequest)
600         scheme=pfc->fIsSecurePort ? "https" : "http";
601
602     // Start with scheme and hostname.
603     if (g_bNormalizeRequest) {
604         target = string(scheme) + "://" + site.m_name;
605     }
606     else {
607         dynabuf name(64);
608         GetServerVariable(pfc,"SERVER_NAME",name,64);
609         target = string(scheme) + "://" + static_cast<char*>(name);
610     }
611     
612     // If port is non-default, append it.
613     if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
614         target = target + ':' + static_cast<char*>(port);
615
616     // Append path.
617     if (!url.empty())
618         target+=static_cast<char*>(url);
619     
620     return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
621 }
622
623 DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const IApplication* app, const char* page, ShibMLP& mlp)
624 {
625     const IPropertySet* props=app->getPropertySet("Errors");
626     if (props) {
627         pair<bool,const char*> p=props->getString(page);
628         if (p.first) {
629             ifstream infile(p.second);
630             if (!infile.fail()) {
631                 const char* res = mlp.run(infile,props);
632                 if (res) {
633                     static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
634                     pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0);
635                     DWORD resplen=strlen(res);
636                     pfc->WriteClient(pfc,(LPVOID)res,&resplen,0);
637                     return SF_STATUS_REQ_FINISHED;
638                 }
639             }
640         }
641     }
642
643     LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Filter unable to open error template.");
644     return WriteClientError(pfc,"Unable to open error template, check settings.");
645 }
646
647 DWORD WriteRedirectPage(PHTTP_FILTER_CONTEXT pfc, const IApplication* app, const char* file, ShibMLP& mlp, const char* headers=NULL)
648 {
649     ifstream infile(file);
650     if (!infile.fail()) {
651         const char* res = mlp.run(infile,app->getPropertySet("Errors"));
652         if (res) {
653             char buf[255];
654             sprintf(buf,"Content-Length: %u\r\nContent-Type: text/html\r\n\r\n",strlen(res));
655             if (headers) {
656                 string h(headers);
657                 h+=buf;
658                 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)h.c_str(),0);
659             }
660             else
661                 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)buf,0);
662             DWORD resplen=strlen(res);
663             pfc->WriteClient(pfc,(LPVOID)res,&resplen,0);
664             return SF_STATUS_REQ_FINISHED;
665         }
666     }
667     LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open redirect template.");
668     return WriteClientError(pfc,"Unable to open redirect template, check settings.");
669 }
670
671 extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
672 {
673     // Is this a log notification?
674     if (notificationType==SF_NOTIFY_LOG)
675     {
676         if (pfc->pFilterContext)
677             ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=static_cast<LPCSTR>(pfc->pFilterContext);
678         return SF_STATUS_REQ_NEXT_NOTIFICATION;
679     }
680
681     PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification;
682     try
683     {
684         // Determine web site number. This can't really fail, I don't think.
685         dynabuf buf(128);
686         GetServerVariable(pfc,"INSTANCE_ID",buf,10);
687
688         // Match site instance to host name, skip if no match.
689         map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
690         if (map_i==g_Sites.end())
691             return SF_STATUS_REQ_NEXT_NOTIFICATION;
692             
693         ostringstream threadid;
694         threadid << "[" << getpid() << "] isapi_shib" << '\0';
695         saml::NDC ndc(threadid.str().c_str());
696         
697         // We lock the configuration system for the duration.
698         IConfig* conf=g_Config->getINI();
699         Locker locker(conf);
700         
701         // Map request to application and content settings.
702         string targeturl;
703         IRequestMapper* mapper=conf->getRequestMapper();
704         Locker locker2(mapper);
705         IRequestMapper::Settings settings=map_request(pfc,pn,mapper,map_i->second,targeturl);
706         pair<bool,const char*> application_id=settings.first->getString("applicationId");
707         const IApplication* application=conf->getApplication(application_id.second);
708         if (!application)
709             return WriteClientError(pfc,"Unable to map request to application settings, check configuration.");
710         
711         // Declare SHIRE object for this request.
712         SHIRE shire(application);
713         
714         const char* shireURL=shire.getShireURL(targeturl.c_str());
715         if (!shireURL)
716             return WriteClientError(pfc,"Unable to map request to proper shireURL setting, check configuration.");
717
718         // If the user is accessing the SHIRE acceptance point, pass it on.
719         if (targeturl.find(shireURL)!=string::npos)
720             return SF_STATUS_REQ_NEXT_NOTIFICATION;
721
722         // Now check the policy for this request.
723         pair<bool,bool> requireSession=settings.first->getBool("requireSession");
724         pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
725         pair<bool,bool> httpRedirects=application->getPropertySet("Sessions")->getBool("httpRedirects");
726         pair<bool,const char*> redirectPage=application->getPropertySet("Sessions")->getString("redirectPage");
727         if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
728             return WriteClientError(pfc,"HTML-based redirection requires a redirectPage property.");
729
730         // Check for session cookie.
731         const char* session_id=NULL;
732         GetHeader(pn,pfc,"Cookie:",buf,128,false);
733         Category::getInstance("isapi_shib.HttpFilterProc").debug("cookie header is {%s}",(const char*)buf);
734         if (!buf.empty() && (session_id=strstr(buf,shib_cookie.first))) {
735             session_id+=strlen(shib_cookie.first) + 1;   /* Skip over the '=' */
736             char* cookieend=strchr(session_id,';');
737             if (cookieend)
738                 *cookieend = '\0';    /* Ignore anyting after a ; */
739         }
740         
741         if (!session_id || !*session_id) {
742             // If no session required, bail now.
743             if (!requireSession.second)
744                 return SF_STATUS_REQ_NEXT_NOTIFICATION;
745     
746             // No acceptable cookie, and we require a session.  Generate an AuthnRequest.
747             const char* areq = shire.getAuthnRequest(targeturl.c_str());
748             if (!httpRedirects.first || httpRedirects.second) {
749                 string hdrs=string("Location: ") + areq + "\r\n"
750                     "Content-Type: text/html\r\n"
751                     "Content-Length: 40\r\n"
752                     "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
753                     "Cache-Control: private,no-store,no-cache\r\n\r\n";
754                 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",(DWORD)hdrs.c_str(),0);
755                 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
756                 DWORD resplen=40;
757                 pfc->WriteClient(pfc,(LPVOID)redmsg,&resplen,0);
758                 return SF_STATUS_REQ_FINISHED;
759             }
760             else {
761                 ShibMLP markupProcessor;
762                 markupProcessor.insert("requestURL",areq);
763                 return WriteRedirectPage(pfc, application, redirectPage.second, markupProcessor);
764             }
765         }
766
767         // Make sure this session is still valid.
768         RPCError* status = NULL;
769         ShibMLP markupProcessor;
770         markupProcessor.insert("requestURL", targeturl);
771     
772         dynabuf abuf(16);
773         GetServerVariable(pfc,"REMOTE_ADDR",abuf,16);
774         try {
775             status = shire.sessionIsValid(session_id, abuf);
776         }
777         catch (ShibTargetException &e) {
778             markupProcessor.insert("errorType", "Session Processing Error");
779             markupProcessor.insert("errorText", e.what());
780             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
781             return WriteClientError(pfc, application, "shire", markupProcessor);
782         }
783 #ifndef _DEBUG
784         catch (...) {
785             markupProcessor.insert("errorType", "Session Processing Error");
786             markupProcessor.insert("errorText", "Unexpected Exception");
787             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
788             return WriteClientError(pfc, application, "shire", markupProcessor);
789         }
790 #endif
791
792         // Check the status
793         if (status->isError()) {
794             if (!requireSession.second)
795                 return SF_STATUS_REQ_NEXT_NOTIFICATION;
796             else if (status->isRetryable()) {
797                 // Oops, session is invalid. Generate AuthnRequest.
798                 delete status;
799                 const char* areq = shire.getAuthnRequest(targeturl.c_str());
800                 if (!httpRedirects.first || httpRedirects.second) {
801                     string hdrs=string("Location: ") + areq + "\r\n"
802                         "Content-Type: text/html\r\n"
803                         "Content-Length: 40\r\n"
804                         "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
805                         "Cache-Control: private,no-store,no-cache\r\n\r\n";
806                     pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",(DWORD)hdrs.c_str(),0);
807                     static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
808                     DWORD resplen=40;
809                     pfc->WriteClient(pfc,(LPVOID)redmsg,&resplen,0);
810                     return SF_STATUS_REQ_FINISHED;
811                 }
812                 else {
813                     markupProcessor.insert("requestURL",areq);
814                     return WriteRedirectPage(pfc, application, redirectPage.second, markupProcessor);
815                 }
816             }
817             else {
818                 // return the error page to the user
819                 markupProcessor.insert(*status);
820                 delete status;
821                 return WriteClientError(pfc, application, "shire", markupProcessor);
822             }
823         }
824         delete status;
825     
826         // Move to RM phase.
827         RM rm(application);
828         vector<SAMLAssertion*> assertions;
829         SAMLAuthenticationStatement* sso_statement=NULL;
830
831         try {
832             status = rm.getAssertions(session_id, abuf, assertions, &sso_statement);
833         }
834         catch (ShibTargetException &e) {
835             markupProcessor.insert("errorType", "Attribute Processing Error");
836             markupProcessor.insert("errorText", e.what());
837             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
838             return WriteClientError(pfc, application, "rm", markupProcessor);
839         }
840     #ifndef _DEBUG
841         catch (...) {
842             markupProcessor.insert("errorType", "Attribute Processing Error");
843             markupProcessor.insert("errorText", "Unexpected Exception");
844             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
845             return WriteClientError(pfc, application, "rm", markupProcessor);
846         }
847     #endif
848     
849         if (status->isError()) {
850             markupProcessor.insert(*status);
851             delete status;
852             return WriteClientError(pfc, application, "rm", markupProcessor);
853         }
854         delete status;
855
856         // Do we have an access control plugin?
857         if (settings.second) {
858             Locker acllock(settings.second);
859             if (!settings.second->authorized(*sso_statement,assertions)) {
860                 for (int k = 0; k < assertions.size(); k++)
861                     delete assertions[k];
862                 delete sso_statement;
863                 return WriteClientError(pfc, application, "access", markupProcessor);
864             }
865         }
866
867         // Get the AAP providers, which contain the attribute policy info.
868         Iterator<IAAP*> provs=application->getAAPProviders();
869     
870         // Clear out the list of mapped attributes
871         while (provs.hasNext()) {
872             IAAP* aap=provs.next();
873             aap->lock();
874             try {
875                 Iterator<const IAttributeRule*> rules=aap->getAttributeRules();
876                 while (rules.hasNext()) {
877                     const char* header=rules.next()->getHeader();
878                     if (header) {
879                         string hname=string(header) + ':';
880                         pn->SetHeader(pfc,const_cast<char*>(hname.c_str()),"");
881                     }
882                 }
883             }
884             catch(...) {
885                 aap->unlock();
886                 for (int k = 0; k < assertions.size(); k++)
887                   delete assertions[k];
888                 delete sso_statement;
889                 markupProcessor.insert("errorType", "Attribute Processing Error");
890                 markupProcessor.insert("errorText", "Unexpected Exception");
891                 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
892                 return WriteClientError(pfc, application, "rm", markupProcessor);
893             }
894             aap->unlock();
895         }
896         provs.reset();
897
898         // Maybe export the first assertion.
899         pn->SetHeader(pfc,"remote-user:","");
900         pn->SetHeader(pfc,"Shib-Attributes:","");
901         pair<bool,bool> exp=settings.first->getBool("exportAssertion");
902         if (exp.first && exp.second && assertions.size()) {
903             string assertion;
904             RM::serialize(*(assertions[0]), assertion);
905             string::size_type lfeed;
906             while ((lfeed=assertion.find('\n'))!=string::npos)
907                 assertion.erase(lfeed,1);
908             pn->SetHeader(pfc,"Shib-Attributes:",const_cast<char*>(assertion.c_str()));
909         }
910         
911         pn->SetHeader(pfc,"Shib-Origin-Site:","");
912         pn->SetHeader(pfc,"Shib-Authentication-Method:","");
913         pn->SetHeader(pfc,"Shib-NameIdentifier-Format:","");
914
915         // Export the SAML AuthnMethod and the origin site name.
916         auto_ptr_char os(sso_statement->getSubject()->getNameIdentifier()->getNameQualifier());
917         auto_ptr_char am(sso_statement->getAuthMethod());
918         pn->SetHeader(pfc,"Shib-Origin-Site:", const_cast<char*>(os.get()));
919         pn->SetHeader(pfc,"Shib-Authentication-Method:", const_cast<char*>(am.get()));
920
921         // Export NameID?
922         AAP wrapper(provs,sso_statement->getSubject()->getNameIdentifier()->getFormat(),Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
923         if (!wrapper.fail() && wrapper->getHeader()) {
924             auto_ptr_char form(sso_statement->getSubject()->getNameIdentifier()->getFormat());
925             auto_ptr_char nameid(sso_statement->getSubject()->getNameIdentifier()->getName());
926             pn->SetHeader(pfc,"Shib-NameIdentifier-Format:",const_cast<char*>(form.get()));
927             if (!strcmp(wrapper->getHeader(),"REMOTE_USER")) {
928                 char* principal=const_cast<char*>(nameid.get());
929                 pn->SetHeader(pfc,"remote-user:",principal);
930                 pfc->pFilterContext=pfc->AllocMem(pfc,strlen(principal)+1,0);
931                 if (pfc->pFilterContext)
932                     strcpy(static_cast<char*>(pfc->pFilterContext),principal);
933             }
934             else {
935                 string hname=string(wrapper->getHeader()) + ':';
936                 pn->SetHeader(pfc,const_cast<char*>(wrapper->getHeader()),const_cast<char*>(nameid.get()));
937             }
938         }
939
940         pn->SetHeader(pfc,"Shib-Application-ID:","");
941         pn->SetHeader(pfc,"Shib-Application-ID:",const_cast<char*>(application_id.second));
942
943         // Export the attributes.
944         Iterator<SAMLAssertion*> a_iter(assertions);
945         while (a_iter.hasNext()) {
946             SAMLAssertion* assert=a_iter.next();
947             Iterator<SAMLStatement*> statements=assert->getStatements();
948             while (statements.hasNext()) {
949                 SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());
950                 if (!astate)
951                     continue;
952                 Iterator<SAMLAttribute*> attrs=astate->getAttributes();
953                 while (attrs.hasNext()) {
954                     SAMLAttribute* attr=attrs.next();
955         
956                     // Are we supposed to export it?
957                     AAP wrapper(provs,attr->getName(),attr->getNamespace());
958                     if (wrapper.fail() || !wrapper->getHeader())
959                         continue;
960                 
961                     Iterator<string> vals=attr->getSingleByteValues();
962                     if (!strcmp(wrapper->getHeader(),"REMOTE_USER") && vals.hasNext()) {
963                         char* principal=const_cast<char*>(vals.next().c_str());
964                         pn->SetHeader(pfc,"remote-user:",principal);
965                         pfc->pFilterContext=pfc->AllocMem(pfc,strlen(principal)+1,0);
966                         if (pfc->pFilterContext)
967                             strcpy(static_cast<char*>(pfc->pFilterContext),principal);
968                     }
969                     else {
970                         int it=0;
971                         string header;
972                         string hname=string(wrapper->getHeader()) + ':';
973                         GetHeader(pn,pfc,const_cast<char*>(hname.c_str()),buf,256,false);
974                         if (!buf.empty()) {
975                             header=buf;
976                             it++;
977                         }
978                         for (; vals.hasNext(); it++) {
979                             string value = vals.next();
980                             for (string::size_type pos = value.find_first_of(";", string::size_type(0));
981                                     pos != string::npos;
982                                     pos = value.find_first_of(";", pos)) {
983                                 value.insert(pos, "\\");
984                                 pos += 2;
985                             }
986                             if (it == 0)
987                                 header=value;
988                             else
989                                 header=header + ';' + value;
990                         }
991                         pn->SetHeader(pfc,const_cast<char*>(hname.c_str()),const_cast<char*>(header.c_str()));
992                         }
993                 }
994             }
995         }
996     
997         // clean up memory
998         for (int k = 0; k < assertions.size(); k++)
999           delete assertions[k];
1000         delete sso_statement;
1001
1002         return SF_STATUS_REQ_NEXT_NOTIFICATION;
1003     }
1004     catch(bad_alloc) {
1005         return WriteClientError(pfc,"Out of Memory");
1006     }
1007     catch(DWORD e) {
1008         if (e==ERROR_NO_DATA)
1009             return WriteClientError(pfc,"A required variable or header was empty.");
1010         else
1011             return WriteClientError(pfc,"Server detected unexpected IIS error.");
1012     }
1013 #ifndef _DEBUG
1014     catch(...) {
1015         return WriteClientError(pfc,"Server caught an unknown exception.");
1016     }
1017 #endif
1018
1019     return WriteClientError(pfc,"Server reached unreachable code, save my walrus!");
1020 }
1021 #endif // 0
1022
1023 /****************************************************************************/
1024 // ISAPI Extension
1025
1026 DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg)
1027 {
1028     LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
1029     static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
1030     lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
1031     static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Error</TITLE></HEAD><BODY><H1>Shibboleth Error</H1>";
1032     DWORD resplen=strlen(xmsg);
1033     lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC);
1034     resplen=strlen(msg);
1035     lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC);
1036     static const char* xmsg2="</BODY></HTML>";
1037     resplen=strlen(xmsg2);
1038     lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC);
1039     return HSE_STATUS_SUCCESS;
1040 }
1041
1042
1043 class ShibTargetIsapiE : public ShibTarget
1044 {
1045   LPEXTENSION_CONTROL_BLOCK m_lpECB;
1046   string m_cookie;
1047   
1048 public:
1049   ShibTargetIsapiE(LPEXTENSION_CONTROL_BLOCK lpECB, const site_t& site) {
1050     dynabuf ssl(5);
1051     GetServerVariable(lpECB,"HTTPS",ssl,5);
1052     bool SSL=(ssl=="on" || ssl=="ON");
1053
1054     // URL path always come from IIS.
1055     dynabuf url(256);
1056     GetServerVariable(lpECB,"URL",url,255);
1057
1058     // Port may come from IIS or from site def.
1059     dynabuf port(11);
1060     if (!g_bNormalizeRequest || (SSL && site.m_sslport.empty()) || (!SSL && site.m_port.empty()))
1061         GetServerVariable(lpECB,"SERVER_PORT",port,10);
1062     else if (SSL) {
1063         strncpy(port,site.m_sslport.c_str(),10);
1064         static_cast<char*>(port)[10]=0;
1065     }
1066     else {
1067         strncpy(port,site.m_port.c_str(),10);
1068         static_cast<char*>(port)[10]=0;
1069     }
1070
1071     // Scheme may come from site def or be derived from IIS.
1072     const char* scheme=site.m_scheme.c_str();
1073     if (!scheme || !*scheme || !g_bNormalizeRequest) {
1074         scheme = SSL ? "https" : "http";
1075     }
1076
1077     // Get the other server variables.
1078     dynabuf remote_addr(16),hostname(32);
1079     GetServerVariable(lpECB, "REMOTE_ADDR", remote_addr, 16);
1080     GetServerVariable(lpECB, "SERVER_NAME", hostname, 32);
1081
1082     // Make sure SERVER_NAME is "authorized" for use on this site. If not, set to canonical name.
1083     const char* host=hostname;
1084     if (site.m_name!=host && site.m_aliases.find(host)==site.m_aliases.end())
1085         host=site.m_name.c_str();
1086
1087     /*
1088      * IIS screws us over on PATH_INFO (the hits keep on coming). We need to figure out if
1089      * the server is set up for proper PATH_INFO handling, or "IIS sucks rabid weasels mode",
1090      * which is the default. No perfect way to tell, but we can take a good guess by checking
1091      * whether the URL is a substring of the PATH_INFO:
1092      * 
1093      * e.g. for /Shibboleth.sso/SAML/POST
1094      * 
1095      *  Bad mode (default):
1096      *      URL:        /Shibboleth.sso
1097      *      PathInfo:   /Shibboleth.sso/SAML/POST
1098      * 
1099      *  Good mode:
1100      *      URL:        /Shibboleth.sso
1101      *      PathInfo:   /SAML/POST
1102      */
1103     
1104     string fullurl;
1105     
1106     // Clearly we're only in bad mode if path info exists at all.
1107     if (lpECB->lpszPathInfo && *(lpECB->lpszPathInfo)) {
1108         if (strstr(lpECB->lpszPathInfo,url))
1109             // Pretty good chance we're in bad mode, unless the PathInfo repeats the path itself.
1110             fullurl=lpECB->lpszPathInfo;
1111         else {
1112             fullurl+=url;
1113             fullurl+=lpECB->lpszPathInfo;
1114         }
1115     }
1116     
1117     // For consistency with Apache, let's add the query string.
1118     if (lpECB->lpszQueryString && *(lpECB->lpszQueryString)) {
1119         fullurl+='?';
1120         fullurl+=lpECB->lpszQueryString;
1121     }
1122     init(scheme, host, atoi(port), fullurl.c_str(), lpECB->lpszContentType, remote_addr, lpECB->lpszMethod);
1123
1124     m_lpECB = lpECB;
1125   }
1126   ~ShibTargetIsapiE() { }
1127
1128   virtual void log(ShibLogLevel level, const string &msg) {
1129       ShibTarget::log(level,msg);
1130       if (level == LogLevelError)
1131           LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg.c_str());
1132   }
1133   virtual string getCookies() const {
1134     dynabuf buf(128);
1135     GetServerVariable(m_lpECB, "HTTP_COOKIE", buf, 128, false);
1136     return buf.empty() ? "" : buf;
1137   }
1138   virtual void setCookie(const string &name, const string &value) {
1139     // Set the cookie for later.  Use it during the redirect.
1140     m_cookie += "Set-Cookie: " + name + "=" + value + "\r\n";
1141   }
1142   virtual string getArgs(void) {
1143     return string(m_lpECB->lpszQueryString ? m_lpECB->lpszQueryString : "");
1144   }
1145   virtual string getPostData(void) {
1146     if (m_lpECB->cbTotalBytes > 1024*1024) // 1MB?
1147       throw FatalProfileException("Blocked too-large a submission to profile endpoint.");
1148     else if (m_lpECB->cbTotalBytes != m_lpECB->cbAvailable) {
1149       string cgistr;
1150       char buf[8192];
1151       DWORD datalen=m_lpECB->cbTotalBytes;
1152       while (datalen) {
1153         DWORD buflen=8192;
1154         BOOL ret = m_lpECB->ReadClient(m_lpECB->ConnID, buf, &buflen);
1155         if (!ret || !buflen)
1156           throw FatalProfileException("Error reading profile submission from browser.");
1157         cgistr.append(buf, buflen);
1158         datalen-=buflen;
1159       }
1160       return cgistr;
1161     }
1162     else
1163       return string(reinterpret_cast<char*>(m_lpECB->lpbData),m_lpECB->cbAvailable);
1164   }
1165   virtual void* sendPage(
1166     const string &msg,
1167     int code=200,
1168     const string& content_type="text/html",
1169     const Iterator<header_t>& headers=EMPTY(header_t)) {
1170     string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n";
1171     for (int k = 0; k < headers.size(); k++) {
1172       hdr += headers[k].first + ": " + headers[k].second + "\r\n";
1173     }
1174     hdr += "\r\n";
1175     const char* codestr="200 OK";
1176     switch (code) {
1177         case 403:   codestr="403 Forbidden"; break;
1178         case 404:   codestr="404 Not Found"; break;
1179         case 500:   codestr="500 Server Error"; break;
1180     }
1181     m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, (void*)codestr, 0, (LPDWORD)hdr.c_str());
1182     DWORD resplen = msg.size();
1183     m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)msg.c_str(), &resplen, HSE_IO_SYNC);
1184     return (void*)HSE_STATUS_SUCCESS;
1185   }
1186   virtual void* sendRedirect(const string& url) {
1187     // XXX: Don't support the httpRedirect option, yet.
1188     string hdrs = m_cookie + "Location: " + url + "\r\n"
1189       "Content-Type: text/html\r\n"
1190       "Content-Length: 40\r\n"
1191       "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
1192       "Cache-Control: private,no-store,no-cache\r\n\r\n";
1193     m_lpECB->ServerSupportFunction(m_lpECB->ConnID, HSE_REQ_SEND_RESPONSE_HEADER,
1194                                  "302 Moved", 0, (LPDWORD)hdrs.c_str());
1195     static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
1196     DWORD resplen=40;
1197     m_lpECB->WriteClient(m_lpECB->ConnID, (LPVOID)redmsg, &resplen, HSE_IO_SYNC);
1198     return (void*)HSE_STATUS_SUCCESS;
1199   }
1200   // Decline happens in the POST processor if this isn't the shire url
1201   // Note that it can also happen with HTAccess, but we don't support that, yet.
1202   virtual void* returnDecline(void) {
1203     return (void*)
1204       WriteClientError(m_lpECB, "ISAPI extension can only be invoked to process Shibboleth protocol requests."
1205                        "Make sure the mapped file extension doesn't match actual content.");
1206   }
1207   virtual void* returnOK(void) { return (void*) HSE_STATUS_SUCCESS; }
1208
1209   // Not used in the extension.
1210   virtual void clearHeader(const string &name) { throw runtime_error("clearHeader not implemented"); }
1211   virtual void setHeader(const string &name, const string &value) { throw runtime_error("setHeader not implemented"); }
1212   virtual string getHeader(const string &name) { throw runtime_error("getHeader not implemented"); }
1213   virtual void setRemoteUser(const string &user) { throw runtime_error("setRemoteUser not implemented"); }
1214   virtual string getRemoteUser(void) { throw runtime_error("getRemoteUser not implemented"); }
1215 };
1216
1217 extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
1218 {
1219     string targeturl;
1220     const IApplication* application=NULL;
1221     try {
1222         ostringstream threadid;
1223         threadid << "[" << getpid() << "] isapi_shib_extension" << '\0';
1224         saml::NDC ndc(threadid.str().c_str());
1225
1226         // Determine web site number. This can't really fail, I don't think.
1227         dynabuf buf(128);
1228         GetServerVariable(lpECB,"INSTANCE_ID",buf,10);
1229
1230         // Match site instance to host name, skip if no match.
1231         map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
1232         if (map_i==g_Sites.end())
1233             return WriteClientError(lpECB, "Shibboleth Extension not configured for this web site.");
1234
1235         ShibTargetIsapiE ste(lpECB, map_i->second);
1236         pair<bool,void*> res = ste.doHandler();
1237         if (res.first) return (DWORD)res.second;
1238         
1239         return WriteClientError(lpECB, "Shibboleth Extension failed to process request");
1240
1241     }
1242     catch(bad_alloc) {
1243         return WriteClientError(lpECB,"Out of Memory");
1244     }
1245     catch(long e) {
1246         if (e==ERROR_NO_DATA)
1247             return WriteClientError(lpECB,"A required variable or header was empty.");
1248         else
1249             return WriteClientError(lpECB,"Server detected unexpected IIS error.");
1250     }
1251 #ifndef _DEBUG
1252     catch(...) {
1253         return WriteClientError(lpECB,"Server caught an unknown exception.");
1254     }
1255 #endif
1256
1257     // If we get here we've got an error.
1258     return HSE_STATUS_ERROR;
1259 }
1260
1261 #if 0
1262 IRequestMapper::Settings map_request(
1263     LPEXTENSION_CONTROL_BLOCK lpECB, IRequestMapper* mapper, const site_t& site, string& target
1264     )
1265 {
1266     dynabuf ssl(5);
1267     GetServerVariable(lpECB,"HTTPS",ssl,5);
1268     bool SSL=(ssl=="on" || ssl=="ON");
1269
1270     // URL path always come from IIS.
1271     dynabuf url(256);
1272     GetServerVariable(lpECB,"URL",url,255);
1273
1274     // Port may come from IIS or from site def.
1275     dynabuf port(11);
1276     if (site.m_port.empty() || !g_bNormalizeRequest)
1277         GetServerVariable(lpECB,"SERVER_PORT",port,10);
1278     else {
1279         strncpy(port,site.m_port.c_str(),10);
1280         static_cast<char*>(port)[10]=0;
1281     }
1282
1283     // Scheme may come from site def or be derived from IIS.
1284     const char* scheme=site.m_scheme.c_str();
1285     if (!scheme || !*scheme || !g_bNormalizeRequest) {
1286         scheme = SSL ? "https" : "http";
1287     }
1288
1289     // Start with scheme and hostname.
1290     if (g_bNormalizeRequest) {
1291         target = string(scheme) + "://" + site.m_name;
1292     }
1293     else {
1294         dynabuf name(64);
1295         GetServerVariable(lpECB,"SERVER_NAME",name,64);
1296         target = string(scheme) + "://" + static_cast<char*>(name);
1297     }
1298     
1299     // If port is non-default, append it.
1300     if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
1301         target = target + ':' + static_cast<char*>(port);
1302
1303     // Append path.
1304     if (!url.empty())
1305         target+=static_cast<char*>(url);
1306     
1307     return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
1308 }
1309
1310 DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const IApplication* app, const char* page, ShibMLP& mlp)
1311 {
1312     const IPropertySet* props=app->getPropertySet("Errors");
1313     if (props) {
1314         pair<bool,const char*> p=props->getString(page);
1315         if (p.first) {
1316             ifstream infile(p.second);
1317             if (!infile.fail()) {
1318                 const char* res = mlp.run(infile,props);
1319                 if (res) {
1320                     static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
1321                     lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
1322                     DWORD resplen=strlen(res);
1323                     lpECB->WriteClient(lpECB->ConnID,(LPVOID)res,&resplen,0);
1324                     return HSE_STATUS_SUCCESS;
1325                 }
1326             }
1327         }
1328     }
1329     LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open error template.");
1330     return WriteClientError(lpECB,"Unable to open error template, check settings.");
1331 }
1332
1333 DWORD WriteRedirectPage(LPEXTENSION_CONTROL_BLOCK lpECB, const IApplication* app, const char* file, ShibMLP& mlp, const char* headers=NULL)
1334 {
1335     ifstream infile(file);
1336     if (!infile.fail()) {
1337         const char* res = mlp.run(infile,app->getPropertySet("Errors"));
1338         if (res) {
1339             char buf[255];
1340             sprintf(buf,"Content-Length: %u\r\nContent-Type: text/html\r\n\r\n",strlen(res));
1341             if (headers) {
1342                 string h(headers);
1343                 h+=buf;
1344                 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)h.c_str());
1345             }
1346             else
1347                 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)buf);
1348             DWORD resplen=strlen(res);
1349             lpECB->WriteClient(lpECB->ConnID,(LPVOID)res,&resplen,0);
1350             return HSE_STATUS_SUCCESS;
1351         }
1352     }
1353     LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open redirect template.");
1354     return WriteClientError(lpECB,"Unable to open redirect template, check settings.");
1355 }
1356
1357 extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
1358 {
1359     string targeturl;
1360     const IApplication* application=NULL;
1361     try
1362     {
1363         ostringstream threadid;
1364         threadid << "[" << getpid() << "] shire_handler" << '\0';
1365         saml::NDC ndc(threadid.str().c_str());
1366
1367         // Determine web site number. This can't really fail, I don't think.
1368         dynabuf buf(128);
1369         GetServerVariable(lpECB,"INSTANCE_ID",buf,10);
1370
1371         // Match site instance to host name, skip if no match.
1372         map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
1373         if (map_i==g_Sites.end())
1374             return WriteClientError(lpECB,"Shibboleth filter not configured for this web site.");
1375             
1376         // We lock the configuration system for the duration.
1377         IConfig* conf=g_Config->getINI();
1378         Locker locker(conf);
1379         
1380         // Map request to application and content settings.
1381         IRequestMapper* mapper=conf->getRequestMapper();
1382         Locker locker2(mapper);
1383         IRequestMapper::Settings settings=map_request(lpECB,mapper,map_i->second,targeturl);
1384         pair<bool,const char*> application_id=settings.first->getString("applicationId");
1385         application=conf->getApplication(application_id.second);
1386         const IPropertySet* sessionProps=application ? application->getPropertySet("Sessions") : NULL;
1387         if (!application || !sessionProps)
1388             return WriteClientError(lpECB,"Unable to map request to application session settings, check configuration.");
1389
1390         SHIRE shire(application);
1391         
1392         const char* shireURL=shire.getShireURL(targeturl.c_str());
1393         if (!shireURL)
1394             return WriteClientError(lpECB,"Unable to map request to proper shireURL setting, check configuration.");
1395
1396         // Make sure we only process the SHIRE requests.
1397         if (!strstr(targeturl.c_str(),shireURL))
1398             return WriteClientError(lpECB,"ISAPI extension can only be invoked to process incoming sessions."
1399                 "Make sure the mapped file extension doesn't match actual content.");
1400
1401         pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
1402
1403         // Make sure this is SSL, if it should be
1404         pair<bool,bool> shireSSL=sessionProps->getBool("shireSSL");
1405         if (!shireSSL.first || shireSSL.second) {
1406             GetServerVariable(lpECB,"HTTPS",buf,10);
1407             if (buf!="on")
1408                 throw ShibTargetException(SHIBRPC_OK,"blocked non-SSL access to SHIRE POST processor");
1409         }
1410         
1411         pair<bool,bool> httpRedirects=sessionProps->getBool("httpRedirects");
1412         pair<bool,const char*> redirectPage=sessionProps->getString("redirectPage");
1413         if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
1414             return WriteClientError(lpECB,"HTML-based redirection requires a redirectPage property.");
1415         
1416         // Check for Mac web browser
1417         /*
1418         bool bSafari=false;
1419         dynabuf agent(64);
1420         GetServerVariable(lpECB,"HTTP_USER_AGENT",agent,64);
1421         if (strstr(agent,"AppleWebKit/"))
1422             bSafari=true;
1423         */
1424         
1425         // If this is a GET, we manufacture an AuthnRequest.
1426         if (!stricmp(lpECB->lpszMethod,"GET")) {
1427             const char* areq=lpECB->lpszQueryString ? shire.getLazyAuthnRequest(lpECB->lpszQueryString) : NULL;
1428             if (!areq)
1429                 throw ShibTargetException(SHIBRPC_OK, "malformed arguments to request a new session");
1430             if (!httpRedirects.first || httpRedirects.second) {
1431                 string hdrs=string("Location: ") + areq + "\r\n"
1432                     "Content-Type: text/html\r\n"
1433                     "Content-Length: 40\r\n"
1434                     "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
1435                     "Cache-Control: private,no-store,no-cache\r\n\r\n";
1436                 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)hdrs.c_str());
1437                 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
1438                 DWORD resplen=40;
1439                 lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
1440                 return HSE_STATUS_SUCCESS;
1441             }
1442             else {
1443                 ShibMLP markupProcessor;
1444                 markupProcessor.insert("requestURL",areq);
1445                 return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor);
1446             }
1447         }
1448         else if (stricmp(lpECB->lpszMethod,"POST"))
1449             throw ShibTargetException(SHIBRPC_OK,"blocked non-POST to SHIRE POST processor");
1450
1451         // Sure sure this POST is an appropriate content type
1452         if (!lpECB->lpszContentType || stricmp(lpECB->lpszContentType,"application/x-www-form-urlencoded"))
1453             throw ShibTargetException(SHIBRPC_OK,"blocked bad content-type to SHIRE POST processor");
1454     
1455         // Read the data.
1456         pair<const char*,const char*> elements=pair<const char*,const char*>(NULL,NULL);
1457         if (lpECB->cbTotalBytes > 1024*1024) // 1MB?
1458             throw ShibTargetException(SHIBRPC_OK,"blocked too-large a post to SHIRE POST processor");
1459         else if (lpECB->cbTotalBytes!=lpECB->cbAvailable) {
1460             string cgistr;
1461             char buf[8192];
1462             DWORD datalen=lpECB->cbTotalBytes;
1463             while (datalen) {
1464                 DWORD buflen=8192;
1465                 BOOL ret=lpECB->ReadClient(lpECB->ConnID,buf,&buflen);
1466                 if (!ret || !buflen)
1467                     throw ShibTargetException(SHIBRPC_OK,"error reading POST data from browser");
1468                 cgistr.append(buf,buflen);
1469                 datalen-=buflen;
1470             }
1471             elements=shire.getFormSubmission(cgistr.c_str(),cgistr.length());
1472         }
1473         else
1474             elements=shire.getFormSubmission(reinterpret_cast<char*>(lpECB->lpbData),lpECB->cbAvailable);
1475     
1476         // Make sure the SAML Response parameter exists
1477         if (!elements.first || !*elements.first)
1478             throw ShibTargetException(SHIBRPC_OK, "SHIRE POST failed to find SAMLResponse form element");
1479     
1480         // Make sure the target parameter exists
1481         if (!elements.second || !*elements.second)
1482             throw ShibTargetException(SHIBRPC_OK, "SHIRE POST failed to find TARGET form element");
1483             
1484         GetServerVariable(lpECB,"REMOTE_ADDR",buf,16);
1485
1486         // Process the post.
1487         string cookie;
1488         RPCError* status=NULL;
1489         ShibMLP markupProcessor;
1490         markupProcessor.insert("requestURL", targeturl.c_str());
1491         try {
1492             status = shire.sessionCreate(elements.first,buf,cookie);
1493         }
1494         catch (ShibTargetException &e) {
1495             markupProcessor.insert("errorType", "Session Creation Service Error");
1496             markupProcessor.insert("errorText", e.what());
1497             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1498             return WriteClientError(lpECB, application, "shire", markupProcessor);
1499         }
1500 #ifndef _DEBUG
1501         catch (...) {
1502             markupProcessor.insert("errorType", "Session Creation Service Error");
1503             markupProcessor.insert("errorText", "Unexpected Exception");
1504             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1505             return WriteClientError(lpECB, application, "shire", markupProcessor);
1506         }
1507 #endif
1508
1509         if (status->isError()) {
1510             if (status->isRetryable()) {
1511                 delete status;
1512                 const char* loc=shire.getAuthnRequest(elements.second);
1513                 if (!httpRedirects.first || httpRedirects.second) {
1514                     string hdrs=string("Location: ") + loc + "\r\n"
1515                         "Content-Type: text/html\r\n"
1516                         "Content-Length: 40\r\n"
1517                         "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
1518                         "Cache-Control: private,no-store,no-cache\r\n\r\n";
1519                     lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)hdrs.c_str());
1520                     static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
1521                     DWORD resplen=40;
1522                     lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
1523                     return HSE_STATUS_SUCCESS;
1524                 }
1525                 else {
1526                     markupProcessor.insert("requestURL",loc);
1527                     return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor);
1528                 }
1529             }
1530     
1531             // Return this error to the user.
1532             markupProcessor.insert(*status);
1533             delete status;
1534             return WriteClientError(lpECB,application,"shire",markupProcessor);
1535         }
1536         delete status;
1537     
1538         // We've got a good session, set the cookie and redirect to target.
1539         cookie = string("Set-Cookie: ") + shib_cookie.first + '=' + cookie + shib_cookie.second + "\r\n"
1540             "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
1541             "Cache-Control: private,no-store,no-cache\r\n";
1542         if (!httpRedirects.first || httpRedirects.second) {
1543             cookie=cookie + "Content-Type: text/html\r\nLocation: " + elements.second + "\r\nContent-Length: 40\r\n\r\n";
1544             lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)cookie.c_str());
1545             static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
1546             DWORD resplen=40;
1547             lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
1548             return HSE_STATUS_SUCCESS;
1549         }
1550         else {
1551             markupProcessor.insert("requestURL",elements.second);
1552             return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor, cookie.c_str());
1553         }
1554     }
1555     catch (ShibTargetException &e) {
1556         if (application) {
1557             ShibMLP markupProcessor;
1558             markupProcessor.insert("requestURL", targeturl.c_str());
1559             markupProcessor.insert("errorType", "Session Creation Service Error");
1560             markupProcessor.insert("errorText", e.what());
1561             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1562             return WriteClientError(lpECB,application,"shire",markupProcessor);
1563         }
1564     }
1565 #ifndef _DEBUG
1566     catch (...) {
1567         if (application) {
1568             ShibMLP markupProcessor;
1569             markupProcessor.insert("requestURL", targeturl.c_str());
1570             markupProcessor.insert("errorType", "Session Creation Service Error");
1571             markupProcessor.insert("errorText", "Unexpected Exception");
1572             markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1573             return WriteClientError(lpECB,application,"shire",markupProcessor);
1574         }
1575     }
1576 #endif
1577
1578     return HSE_STATUS_ERROR;
1579 }
1580 #endif // 0