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