2 * The Shibboleth License, Version 1.
4 * University Corporation for Advanced Internet Development, Inc.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
11 * Redistributions of source code must retain the above copyright notice, this
12 * list of conditions and the following disclaimer.
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.
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
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.
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.
50 /* isapi_shib.cpp - Shibboleth ISAPI filter
56 #include "config_win32.h"
59 #include <saml/saml.h>
60 #include <shib/shib.h>
61 #include <shib/shib-threads.h>
62 #include <shib-target/shib-target.h>
64 #include <log4cpp/Category.hh>
75 using namespace log4cpp;
77 using namespace shibboleth;
78 using namespace shibtarget;
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 scheme[] = { chLatin_s, chLatin_c, chLatin_h, chLatin_e, chLatin_m, chLatin_e, chNull };
85 static const XMLCh id[] = { chLatin_i, chLatin_d, chNull };
86 static const XMLCh Implementation[] =
87 { 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 };
88 static const XMLCh ISAPI[] = { chLatin_I, chLatin_S, chLatin_A, chLatin_P, chLatin_I, 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
93 static const XMLCh Site[] = { chLatin_S, chLatin_i, chLatin_t, chLatin_e, chNull };
96 site_t(const DOMElement* e)
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 if (n.get()) m_name=n.get();
102 if (s.get()) m_scheme=s.get();
103 if (p.get()) m_port=p.get();
105 string m_scheme,m_name,m_port;
108 HINSTANCE g_hinstDLL;
109 ShibTargetConfig* g_Config = NULL;
110 map<string,site_t> g_Sites;
111 bool g_bNormalizeRequest = true;
115 LPCSTR lpUNCServerName,
121 LPCSTR messages[] = {message, NULL};
123 HANDLE hElog = RegisterEventSource(lpUNCServerName, "Shibboleth ISAPI Filter");
124 BOOL res = ReportEvent(hElog, wType, 0, dwEventID, lpUserSid, 1, 0, messages, NULL);
125 return (DeregisterEventSource(hElog) && res);
128 extern "C" __declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID)
130 if (fdwReason==DLL_PROCESS_ATTACH)
135 extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO* pVer)
142 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
143 "Extension mode startup not possible, is the DLL loaded as a filter?");
147 pVer->dwExtensionVersion=HSE_VERSION;
148 strncpy(pVer->lpszExtensionDesc,"Shibboleth ISAPI Extension",HSE_MAX_EXT_DLL_NAME_LEN-1);
152 extern "C" BOOL WINAPI TerminateExtension(DWORD)
154 return TRUE; // cleanup should happen when filter unloads
157 extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer)
162 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
163 "Reentrant filter initialization, ignoring...");
169 LPCSTR schemadir=getenv("SHIBSCHEMAS");
171 schemadir=SHIB_SCHEMAS;
172 LPCSTR config=getenv("SHIBCONFIG");
175 g_Config=&ShibTargetConfig::getConfig();
176 g_Config->setFeatures(
177 ShibTargetConfig::Listener |
178 ShibTargetConfig::Metadata |
179 ShibTargetConfig::AAP |
180 ShibTargetConfig::RequestMapper |
181 ShibTargetConfig::SHIREExtensions |
182 ShibTargetConfig::Logging
184 if (!g_Config->init(schemadir,config)) {
186 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL,
187 "Filter startup failed during initialization, check shire log for help.");
191 // Access the implementation-specifics for site mappings.
192 IConfig* conf=g_Config->getINI();
194 const IPropertySet* props=conf->getPropertySet("SHIRE");
196 const DOMElement* impl=saml::XML::getFirstChildElement(
197 props->getElement(),ShibTargetConfig::SHIBTARGET_NS,Implementation
199 if (impl && (impl=saml::XML::getFirstChildElement(impl,ShibTargetConfig::SHIBTARGET_NS,ISAPI))) {
200 const XMLCh* flag=impl->getAttributeNS(NULL,normalizeRequest);
201 g_bNormalizeRequest=(!flag || !*flag || *flag==chDigit_1 || *flag==chLatin_t);
202 impl=saml::XML::getFirstChildElement(impl,ShibTargetConfig::SHIBTARGET_NS,Site);
204 auto_ptr_char id(impl->getAttributeNS(NULL,id));
206 g_Sites.insert(pair<string,site_t>(id.get(),site_t(impl)));
207 impl=saml::XML::getNextSiblingElement(impl,ShibTargetConfig::SHIBTARGET_NS,Site);
214 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Filter startup failed with an exception.");
221 pVer->dwFilterVersion=HTTP_FILTER_REVISION;
222 strncpy(pVer->lpszFilterDesc,"Shibboleth ISAPI Filter",SF_MAX_FILTER_DESC_LEN);
223 pVer->dwFlags=(SF_NOTIFY_ORDER_HIGH |
224 SF_NOTIFY_SECURE_PORT |
225 SF_NOTIFY_NONSECURE_PORT |
226 SF_NOTIFY_PREPROC_HEADERS |
228 LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter initialized...");
232 extern "C" BOOL WINAPI TerminateFilter(DWORD)
235 g_Config->shutdown();
237 LogEvent(NULL, EVENTLOG_INFORMATION_TYPE, 7701, NULL, "Filter shut down...");
241 /* Next up, some suck-free versions of various APIs.
243 You DON'T require people to guess the buffer size and THEN tell them the right size.
244 Returning an LPCSTR is apparently way beyond their ken. Not to mention the fact that
245 constant strings aren't typed as such, making it just that much harder. These versions
246 are now updated to use a special growable buffer object, modeled after the standard
247 string class. The standard string won't work because they left out the option to
248 pre-allocate a non-constant buffer.
254 dynabuf() { bufptr=NULL; buflen=0; }
255 dynabuf(size_t s) { bufptr=new char[buflen=s]; *bufptr=0; }
256 ~dynabuf() { delete[] bufptr; }
257 size_t length() const { return bufptr ? strlen(bufptr) : 0; }
258 size_t size() const { return buflen; }
259 bool empty() const { return length()==0; }
260 void reserve(size_t s, bool keep=false);
261 void erase() { if (bufptr) memset(bufptr,0,buflen); }
262 operator char*() { return bufptr; }
263 bool operator ==(const char* s) const;
264 bool operator !=(const char* s) const { return !(*this==s); }
270 void dynabuf::reserve(size_t s, bool keep)
277 p[buflen]=bufptr[buflen];
283 bool dynabuf::operator==(const char* s) const
285 if (buflen==NULL || s==NULL)
286 return (buflen==NULL && s==NULL);
288 return strcmp(bufptr,s)==0;
291 void GetServerVariable(PHTTP_FILTER_CONTEXT pfc, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
292 throw (bad_alloc, DWORD)
298 while (!pfc->GetServerVariable(pfc,lpszVariable,s,&size))
300 // Grumble. Check the error.
301 DWORD e=GetLastError();
302 if (e==ERROR_INSUFFICIENT_BUFFER)
307 if (bRequired && s.empty())
311 void GetServerVariable(LPEXTENSION_CONTROL_BLOCK lpECB, LPSTR lpszVariable, dynabuf& s, DWORD size=80, bool bRequired=true)
312 throw (bad_alloc, DWORD)
318 while (lpECB->GetServerVariable(lpECB->ConnID,lpszVariable,s,&size))
320 // Grumble. Check the error.
321 DWORD e=GetLastError();
322 if (e==ERROR_INSUFFICIENT_BUFFER)
327 if (bRequired && s.empty())
331 void GetHeader(PHTTP_FILTER_PREPROC_HEADERS pn, PHTTP_FILTER_CONTEXT pfc,
332 LPSTR lpszName, dynabuf& s, DWORD size=80, bool bRequired=true)
333 throw (bad_alloc, DWORD)
339 while (!pn->GetHeader(pfc,lpszName,s,&size))
341 // Grumble. Check the error.
342 DWORD e=GetLastError();
343 if (e==ERROR_INSUFFICIENT_BUFFER)
348 if (bRequired && s.empty())
352 /****************************************************************************/
355 class ShibTargetIsapiF : public ShibTarget
358 ShibTargetIsapiF(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPRC_HEADERS pn,
359 const site_t& site) {
361 // URL path always come from IIS.
363 GetHeader(pn,pfc,"url",url,256,false);
365 // Port may come from IIS or from site def.
367 if (site.m_port.empty() || !g_bNormalizeRequest)
368 GetServerVariable(pfc,"SERVER_PORT",port,10);
370 strncpy(port,site.m_port.c_str(),10);
371 static_cast<char*>(port)[10]=0;
374 // Scheme may come from site def or be derived from IIS.
375 const char* scheme=site.m_scheme.c_str();
376 if (!scheme || !*scheme || !g_bNormalizeRequest)
377 scheme=pfc->fIsSecurePort ? "https" : "http";
379 // Get the remote address
380 dynabuf remote_addr(16);
381 GetServerVariable(pfc,"REMOTE_ADDR",remote_addr,16);
383 // XXX: How do I get the content type and HTTP Method from this context?
385 init(g_Config, string(scheme), site.m_name, atoi(port),
386 string(url), string(""), // XXX: content type
387 string(remote_addr), string("") // XXX: http method
393 ~ShibTargetIsapiF() { }
395 virtual void log(ShibLogLevel level, const string &msg) {
396 LogEvent(NULL, (level == LogLevelDebug : EVENTLOG_DEBUG_TYPE ?
397 (level == LogLevelInfo : EVENTLOG_INFORMATION_TYPE ?
398 (level == LogLevelWarn : EVENTLOG_WARNING_TYPE ?
399 EVENTLOG_ERROR_TYPE))),
400 2100, NULL, msg.c_str());
402 virtual string getCookies(void) {
404 GetHeader(m_pn, m_pfc, "Cookie:", buf, 128, false);
406 // XXX: the filter never processes the POST.
407 //virtual void setCookie(const string &name, const string &value) { }
408 //virtual string getArgs(void) { }
409 //virtual string getPostData(void) { }
410 virtual void clearHeader(const string &name) {
411 string hdr = name + ":";
412 m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()), "");
414 virtual void setHeader(const string &name, const string &value) {
415 string hdr = name + ":";
416 m_pn->SetHeader(m_pfc, const_cast<char*>(hdr.c_str()),
417 const_cast<char*>(value.c_str()));
419 virtual string getHeader(const string &name) {
420 string hdr = name + ":";
422 GetHeader(m_pn, m_pfc, hdr.c_str(), buf, 1024, false);
425 virtual void setRemoteUser(const string &user) {
426 setHeader(string("remote-user"), user);
428 virtual string getRemoteUser(void) {
429 return getHeader(string("remote-user"));
431 virtual void* sendPage(const string &msg, const string content_type,
432 const pair<string, string> headers[], int code) {
433 string hdr = string ("Connection: close\r\nContent-type: ") + content_type + "\r\n";
434 for (int k = 0; k < headers.size(); k++) {
435 hdr += headers[k].first + ": " + headers[k].second + "\r\n";
438 // XXX Need to handle "code"
439 m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER, "200 OK",
440 (dword)hdr.c_str(), 0);
441 DWORD resplen = msg.size();
442 m_pfc->WriteClient(m_pfc, (LPVOID)msg.c_str(), &resplen, 0);
443 return (void*)SF_STATUS_REQ_FINISHED;
445 virtual void* sendRedirect(const string url) {
446 // XXX: Don't support the httpRedirect option, yet.
447 string hdrs=string("Location: ") + url + "\r\n"
448 "Content-Type: text/html\r\n"
449 "Content-Length: 40\r\n"
450 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
451 "Cache-Control: private,no-store,no-cache\r\n\r\n";
452 m_pfc->ServerSupportFunction(m_pfc, SF_REQ_SEND_RESPONSE_HEADER,
453 "302 Please Wait", (DWORD)hdrs.c_str(), 0);
454 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
456 m_pfc->WriteClient(m_pfc, (LPVOID)redmsg, &resplen, 0);
457 return SF_STATUS_REQ_FINISHED;
459 // XXX: We might not ever hit the 'decline' status in this filter.
460 //virtual void* returnDecline(void) { }
461 virtual void* returnOK(void) { return (void*) SF_STATUS_REQ_NEXT_NOTIFICATION; }
463 PHTTP_FILTER_CONTEXT m_pfc;
464 PHTTP_FILTER_PREPRC_HEADERS m_pn
467 IRequestMapper::Settings map_request(
468 PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, IRequestMapper* mapper, const site_t& site, string& target
471 // URL path always come from IIS.
473 GetHeader(pn,pfc,"url",url,256,false);
475 // Port may come from IIS or from site def.
477 if (site.m_port.empty() || !g_bNormalizeRequest)
478 GetServerVariable(pfc,"SERVER_PORT",port,10);
480 strncpy(port,site.m_port.c_str(),10);
481 static_cast<char*>(port)[10]=0;
484 // Scheme may come from site def or be derived from IIS.
485 const char* scheme=site.m_scheme.c_str();
486 if (!scheme || !*scheme || !g_bNormalizeRequest)
487 scheme=pfc->fIsSecurePort ? "https" : "http";
489 // Start with scheme and hostname.
490 if (g_bNormalizeRequest) {
491 target = string(scheme) + "://" + site.m_name;
495 GetServerVariable(pfc,"SERVER_NAME",name,64);
496 target = string(scheme) + "://" + static_cast<char*>(name);
499 // If port is non-default, append it.
500 if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
501 target = target + ':' + static_cast<char*>(port);
505 target+=static_cast<char*>(url);
507 return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
510 DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const char* msg)
512 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
513 static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
514 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0);
515 static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Filter Error</TITLE></HEAD><BODY>"
516 "<H1>Shibboleth Filter Error</H1>";
517 DWORD resplen=strlen(xmsg);
518 pfc->WriteClient(pfc,(LPVOID)xmsg,&resplen,0);
520 pfc->WriteClient(pfc,(LPVOID)msg,&resplen,0);
521 static const char* xmsg2="</BODY></HTML>";
522 resplen=strlen(xmsg2);
523 pfc->WriteClient(pfc,(LPVOID)xmsg2,&resplen,0);
524 return SF_STATUS_REQ_FINISHED;
527 DWORD WriteClientError(PHTTP_FILTER_CONTEXT pfc, const IApplication* app, const char* page, ShibMLP& mlp)
529 const IPropertySet* props=app->getPropertySet("Errors");
531 pair<bool,const char*> p=props->getString(page);
533 ifstream infile(p.second);
534 if (!infile.fail()) {
535 const char* res = mlp.run(infile,props);
537 static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
538 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)ctype,0);
539 DWORD resplen=strlen(res);
540 pfc->WriteClient(pfc,(LPVOID)res,&resplen,0);
541 return SF_STATUS_REQ_FINISHED;
547 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Filter unable to open error template.");
548 return WriteClientError(pfc,"Unable to open error template, check settings.");
551 DWORD WriteRedirectPage(PHTTP_FILTER_CONTEXT pfc, const IApplication* app, const char* file, ShibMLP& mlp, const char* headers=NULL)
553 ifstream infile(file);
554 if (!infile.fail()) {
555 const char* res = mlp.run(infile,app->getPropertySet("Errors"));
558 sprintf(buf,"Content-Length: %u\r\nContent-Type: text/html\r\n\r\n",strlen(res));
562 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)h.c_str(),0);
565 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"200 OK",(DWORD)buf,0);
566 DWORD resplen=strlen(res);
567 pfc->WriteClient(pfc,(LPVOID)res,&resplen,0);
568 return SF_STATUS_REQ_FINISHED;
571 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open redirect template.");
572 return WriteClientError(pfc,"Unable to open redirect template, check settings.");
575 extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificationType, LPVOID pvNotification)
577 // Is this a log notification?
578 if (notificationType==SF_NOTIFY_LOG)
580 if (pfc->pFilterContext)
581 ((PHTTP_FILTER_LOG)pvNotification)->pszClientUserName=static_cast<LPCSTR>(pfc->pFilterContext);
582 return SF_STATUS_REQ_NEXT_NOTIFICATION;
585 PHTTP_FILTER_PREPROC_HEADERS pn=(PHTTP_FILTER_PREPROC_HEADERS)pvNotification;
588 // Determine web site number. This can't really fail, I don't think.
590 GetServerVariable(pfc,"INSTANCE_ID",buf,10);
592 // Match site instance to host name, skip if no match.
593 map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
594 if (map_i==g_Sites.end())
595 return SF_STATUS_REQ_NEXT_NOTIFICATION;
597 ostringstream threadid;
598 threadid << "[" << getpid() << "] isapi_shib" << '\0';
599 saml::NDC ndc(threadid.str().c_str());
601 // We lock the configuration system for the duration.
602 IConfig* conf=g_Config->getINI();
605 // Map request to application and content settings.
607 IRequestMapper* mapper=conf->getRequestMapper();
608 Locker locker2(mapper);
609 IRequestMapper::Settings settings=map_request(pfc,pn,mapper,map_i->second,targeturl);
610 pair<bool,const char*> application_id=settings.first->getString("applicationId");
611 const IApplication* application=conf->getApplication(application_id.second);
613 return WriteClientError(pfc,"Unable to map request to application settings, check configuration.");
615 // Declare SHIRE object for this request.
616 SHIRE shire(application);
618 const char* shireURL=shire.getShireURL(targeturl.c_str());
620 return WriteClientError(pfc,"Unable to map request to proper shireURL setting, check configuration.");
622 // If the user is accessing the SHIRE acceptance point, pass it on.
623 if (targeturl.find(shireURL)!=string::npos)
624 return SF_STATUS_REQ_NEXT_NOTIFICATION;
626 // Now check the policy for this request.
627 pair<bool,bool> requireSession=settings.first->getBool("requireSession");
628 pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
629 pair<bool,bool> httpRedirects=application->getPropertySet("Sessions")->getBool("httpRedirects");
630 pair<bool,const char*> redirectPage=application->getPropertySet("Sessions")->getString("redirectPage");
631 if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
632 return WriteClientError(pfc,"HTML-based redirection requires a redirectPage property.");
634 // Check for session cookie.
635 const char* session_id=NULL;
636 GetHeader(pn,pfc,"Cookie:",buf,128,false);
637 Category::getInstance("isapi_shib.HttpFilterProc").debug("cookie header is {%s}",(const char*)buf);
638 if (!buf.empty() && (session_id=strstr(buf,shib_cookie.first))) {
639 session_id+=strlen(shib_cookie.first) + 1; /* Skip over the '=' */
640 char* cookieend=strchr(session_id,';');
642 *cookieend = '\0'; /* Ignore anyting after a ; */
645 if (!session_id || !*session_id) {
646 // If no session required, bail now.
647 if (!requireSession.second)
648 return SF_STATUS_REQ_NEXT_NOTIFICATION;
650 // No acceptable cookie, and we require a session. Generate an AuthnRequest.
651 const char* areq = shire.getAuthnRequest(targeturl.c_str());
652 if (!httpRedirects.first || httpRedirects.second) {
653 string hdrs=string("Location: ") + areq + "\r\n"
654 "Content-Type: text/html\r\n"
655 "Content-Length: 40\r\n"
656 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
657 "Cache-Control: private,no-store,no-cache\r\n\r\n";
658 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",(DWORD)hdrs.c_str(),0);
659 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
661 pfc->WriteClient(pfc,(LPVOID)redmsg,&resplen,0);
662 return SF_STATUS_REQ_FINISHED;
665 ShibMLP markupProcessor;
666 markupProcessor.insert("requestURL",areq);
667 return WriteRedirectPage(pfc, application, redirectPage.second, markupProcessor);
671 // Make sure this session is still valid.
672 RPCError* status = NULL;
673 ShibMLP markupProcessor;
674 markupProcessor.insert("requestURL", targeturl);
677 GetServerVariable(pfc,"REMOTE_ADDR",abuf,16);
679 status = shire.sessionIsValid(session_id, abuf);
681 catch (ShibTargetException &e) {
682 markupProcessor.insert("errorType", "Session Processing Error");
683 markupProcessor.insert("errorText", e.what());
684 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
685 return WriteClientError(pfc, application, "shire", markupProcessor);
689 markupProcessor.insert("errorType", "Session Processing Error");
690 markupProcessor.insert("errorText", "Unexpected Exception");
691 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
692 return WriteClientError(pfc, application, "shire", markupProcessor);
697 if (status->isError()) {
698 if (!requireSession.second)
699 return SF_STATUS_REQ_NEXT_NOTIFICATION;
700 else if (status->isRetryable()) {
701 // Oops, session is invalid. Generate AuthnRequest.
703 const char* areq = shire.getAuthnRequest(targeturl.c_str());
704 if (!httpRedirects.first || httpRedirects.second) {
705 string hdrs=string("Location: ") + areq + "\r\n"
706 "Content-Type: text/html\r\n"
707 "Content-Length: 40\r\n"
708 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
709 "Cache-Control: private,no-store,no-cache\r\n\r\n";
710 pfc->ServerSupportFunction(pfc,SF_REQ_SEND_RESPONSE_HEADER,"302 Please Wait",(DWORD)hdrs.c_str(),0);
711 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
713 pfc->WriteClient(pfc,(LPVOID)redmsg,&resplen,0);
714 return SF_STATUS_REQ_FINISHED;
717 markupProcessor.insert("requestURL",areq);
718 return WriteRedirectPage(pfc, application, redirectPage.second, markupProcessor);
722 // return the error page to the user
723 markupProcessor.insert(*status);
725 return WriteClientError(pfc, application, "shire", markupProcessor);
732 vector<SAMLAssertion*> assertions;
733 SAMLAuthenticationStatement* sso_statement=NULL;
736 status = rm.getAssertions(session_id, abuf, assertions, &sso_statement);
738 catch (ShibTargetException &e) {
739 markupProcessor.insert("errorType", "Attribute Processing Error");
740 markupProcessor.insert("errorText", e.what());
741 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
742 return WriteClientError(pfc, application, "rm", markupProcessor);
746 markupProcessor.insert("errorType", "Attribute Processing Error");
747 markupProcessor.insert("errorText", "Unexpected Exception");
748 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
749 return WriteClientError(pfc, application, "rm", markupProcessor);
753 if (status->isError()) {
754 markupProcessor.insert(*status);
756 return WriteClientError(pfc, application, "rm", markupProcessor);
760 // Do we have an access control plugin?
761 if (settings.second) {
762 Locker acllock(settings.second);
763 if (!settings.second->authorized(*sso_statement,assertions)) {
764 for (int k = 0; k < assertions.size(); k++)
765 delete assertions[k];
766 delete sso_statement;
767 return WriteClientError(pfc, application, "access", markupProcessor);
771 // Get the AAP providers, which contain the attribute policy info.
772 Iterator<IAAP*> provs=application->getAAPProviders();
774 // Clear out the list of mapped attributes
775 while (provs.hasNext()) {
776 IAAP* aap=provs.next();
779 Iterator<const IAttributeRule*> rules=aap->getAttributeRules();
780 while (rules.hasNext()) {
781 const char* header=rules.next()->getHeader();
783 string hname=string(header) + ':';
784 pn->SetHeader(pfc,const_cast<char*>(hname.c_str()),"");
790 for (int k = 0; k < assertions.size(); k++)
791 delete assertions[k];
792 delete sso_statement;
793 markupProcessor.insert("errorType", "Attribute Processing Error");
794 markupProcessor.insert("errorText", "Unexpected Exception");
795 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
796 return WriteClientError(pfc, application, "rm", markupProcessor);
802 // Maybe export the first assertion.
803 pn->SetHeader(pfc,"remote-user:","");
804 pn->SetHeader(pfc,"Shib-Attributes:","");
805 pair<bool,bool> exp=settings.first->getBool("exportAssertion");
806 if (exp.first && exp.second && assertions.size()) {
808 RM::serialize(*(assertions[0]), assertion);
809 string::size_type lfeed;
810 while ((lfeed=assertion.find('\n'))!=string::npos)
811 assertion.erase(lfeed,1);
812 pn->SetHeader(pfc,"Shib-Attributes:",const_cast<char*>(assertion.c_str()));
815 pn->SetHeader(pfc,"Shib-Origin-Site:","");
816 pn->SetHeader(pfc,"Shib-Authentication-Method:","");
817 pn->SetHeader(pfc,"Shib-NameIdentifier-Format:","");
819 // Export the SAML AuthnMethod and the origin site name.
820 auto_ptr_char os(sso_statement->getSubject()->getNameIdentifier()->getNameQualifier());
821 auto_ptr_char am(sso_statement->getAuthMethod());
822 pn->SetHeader(pfc,"Shib-Origin-Site:", const_cast<char*>(os.get()));
823 pn->SetHeader(pfc,"Shib-Authentication-Method:", const_cast<char*>(am.get()));
826 AAP wrapper(provs,sso_statement->getSubject()->getNameIdentifier()->getFormat(),Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
827 if (!wrapper.fail() && wrapper->getHeader()) {
828 auto_ptr_char form(sso_statement->getSubject()->getNameIdentifier()->getFormat());
829 auto_ptr_char nameid(sso_statement->getSubject()->getNameIdentifier()->getName());
830 pn->SetHeader(pfc,"Shib-NameIdentifier-Format:",const_cast<char*>(form.get()));
831 if (!strcmp(wrapper->getHeader(),"REMOTE_USER")) {
832 char* principal=const_cast<char*>(nameid.get());
833 pn->SetHeader(pfc,"remote-user:",principal);
834 pfc->pFilterContext=pfc->AllocMem(pfc,strlen(principal)+1,0);
835 if (pfc->pFilterContext)
836 strcpy(static_cast<char*>(pfc->pFilterContext),principal);
839 string hname=string(wrapper->getHeader()) + ':';
840 pn->SetHeader(pfc,const_cast<char*>(wrapper->getHeader()),const_cast<char*>(nameid.get()));
844 pn->SetHeader(pfc,"Shib-Application-ID:","");
845 pn->SetHeader(pfc,"Shib-Application-ID:",const_cast<char*>(application_id.second));
847 // Export the attributes.
848 Iterator<SAMLAssertion*> a_iter(assertions);
849 while (a_iter.hasNext()) {
850 SAMLAssertion* assert=a_iter.next();
851 Iterator<SAMLStatement*> statements=assert->getStatements();
852 while (statements.hasNext()) {
853 SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());
856 Iterator<SAMLAttribute*> attrs=astate->getAttributes();
857 while (attrs.hasNext()) {
858 SAMLAttribute* attr=attrs.next();
860 // Are we supposed to export it?
861 AAP wrapper(provs,attr->getName(),attr->getNamespace());
862 if (wrapper.fail() || !wrapper->getHeader())
865 Iterator<string> vals=attr->getSingleByteValues();
866 if (!strcmp(wrapper->getHeader(),"REMOTE_USER") && vals.hasNext()) {
867 char* principal=const_cast<char*>(vals.next().c_str());
868 pn->SetHeader(pfc,"remote-user:",principal);
869 pfc->pFilterContext=pfc->AllocMem(pfc,strlen(principal)+1,0);
870 if (pfc->pFilterContext)
871 strcpy(static_cast<char*>(pfc->pFilterContext),principal);
876 string hname=string(wrapper->getHeader()) + ':';
877 GetHeader(pn,pfc,const_cast<char*>(hname.c_str()),buf,256,false);
882 for (; vals.hasNext(); it++) {
883 string value = vals.next();
884 for (string::size_type pos = value.find_first_of(";", string::size_type(0));
886 pos = value.find_first_of(";", pos)) {
887 value.insert(pos, "\\");
893 header=header + ';' + value;
895 pn->SetHeader(pfc,const_cast<char*>(hname.c_str()),const_cast<char*>(header.c_str()));
902 for (int k = 0; k < assertions.size(); k++)
903 delete assertions[k];
904 delete sso_statement;
906 return SF_STATUS_REQ_NEXT_NOTIFICATION;
909 return WriteClientError(pfc,"Out of Memory");
912 if (e==ERROR_NO_DATA)
913 return WriteClientError(pfc,"A required variable or header was empty.");
915 return WriteClientError(pfc,"Server detected unexpected IIS error.");
919 return WriteClientError(pfc,"Server caught an unknown exception.");
923 return WriteClientError(pfc,"Server reached unreachable code, save my walrus!");
926 IRequestMapper::Settings map_request(
927 LPEXTENSION_CONTROL_BLOCK lpECB, IRequestMapper* mapper, const site_t& site, string& target
931 GetServerVariable(lpECB,"HTTPS",ssl,5);
932 bool SSL=(ssl=="on" || ssl=="ON");
934 // URL path always come from IIS.
936 GetServerVariable(lpECB,"URL",url,255);
938 // Port may come from IIS or from site def.
940 if (site.m_port.empty() || !g_bNormalizeRequest)
941 GetServerVariable(lpECB,"SERVER_PORT",port,10);
943 strncpy(port,site.m_port.c_str(),10);
944 static_cast<char*>(port)[10]=0;
947 // Scheme may come from site def or be derived from IIS.
948 const char* scheme=site.m_scheme.c_str();
949 if (!scheme || !*scheme || !g_bNormalizeRequest) {
950 scheme = SSL ? "https" : "http";
953 // Start with scheme and hostname.
954 if (g_bNormalizeRequest) {
955 target = string(scheme) + "://" + site.m_name;
959 GetServerVariable(lpECB,"SERVER_NAME",name,64);
960 target = string(scheme) + "://" + static_cast<char*>(name);
963 // If port is non-default, append it.
964 if ((!strcmp(scheme,"http") && port!="80") || (!strcmp(scheme,"https") && port!="443"))
965 target = target + ':' + static_cast<char*>(port);
969 target+=static_cast<char*>(url);
971 return mapper->getSettingsFromParsedURL(scheme,site.m_name.c_str(),strtoul(port,NULL,10),url);
974 DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg)
976 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, msg);
977 static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
978 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
979 static const char* xmsg="<HTML><HEAD><TITLE>Shibboleth Error</TITLE></HEAD><BODY><H1>Shibboleth Error</H1>";
980 DWORD resplen=strlen(xmsg);
981 lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg,&resplen,HSE_IO_SYNC);
983 lpECB->WriteClient(lpECB->ConnID,(LPVOID)msg,&resplen,HSE_IO_SYNC);
984 static const char* xmsg2="</BODY></HTML>";
985 resplen=strlen(xmsg2);
986 lpECB->WriteClient(lpECB->ConnID,(LPVOID)xmsg2,&resplen,HSE_IO_SYNC);
987 return HSE_STATUS_SUCCESS;
990 DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const IApplication* app, const char* page, ShibMLP& mlp)
992 const IPropertySet* props=app->getPropertySet("Errors");
994 pair<bool,const char*> p=props->getString(page);
996 ifstream infile(p.second);
997 if (!infile.fail()) {
998 const char* res = mlp.run(infile,props);
1000 static const char* ctype="Connection: close\r\nContent-Type: text/html\r\n\r\n";
1001 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)ctype);
1002 DWORD resplen=strlen(res);
1003 lpECB->WriteClient(lpECB->ConnID,(LPVOID)res,&resplen,0);
1004 return HSE_STATUS_SUCCESS;
1009 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open error template.");
1010 return WriteClientError(lpECB,"Unable to open error template, check settings.");
1013 DWORD WriteRedirectPage(LPEXTENSION_CONTROL_BLOCK lpECB, const IApplication* app, const char* file, ShibMLP& mlp, const char* headers=NULL)
1015 ifstream infile(file);
1016 if (!infile.fail()) {
1017 const char* res = mlp.run(infile,app->getPropertySet("Errors"));
1020 sprintf(buf,"Content-Length: %u\r\nContent-Type: text/html\r\n\r\n",strlen(res));
1024 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)h.c_str());
1027 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"200 OK",0,(LPDWORD)buf);
1028 DWORD resplen=strlen(res);
1029 lpECB->WriteClient(lpECB->ConnID,(LPVOID)res,&resplen,0);
1030 return HSE_STATUS_SUCCESS;
1033 LogEvent(NULL, EVENTLOG_ERROR_TYPE, 2100, NULL, "Extension unable to open redirect template.");
1034 return WriteClientError(lpECB,"Unable to open redirect template, check settings.");
1037 extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB)
1040 const IApplication* application=NULL;
1043 ostringstream threadid;
1044 threadid << "[" << getpid() << "] shire_handler" << '\0';
1045 saml::NDC ndc(threadid.str().c_str());
1047 // Determine web site number. This can't really fail, I don't think.
1049 GetServerVariable(lpECB,"INSTANCE_ID",buf,10);
1051 // Match site instance to host name, skip if no match.
1052 map<string,site_t>::const_iterator map_i=g_Sites.find(static_cast<char*>(buf));
1053 if (map_i==g_Sites.end())
1054 return WriteClientError(lpECB,"Shibboleth filter not configured for this web site.");
1056 // We lock the configuration system for the duration.
1057 IConfig* conf=g_Config->getINI();
1058 Locker locker(conf);
1060 // Map request to application and content settings.
1061 IRequestMapper* mapper=conf->getRequestMapper();
1062 Locker locker2(mapper);
1063 IRequestMapper::Settings settings=map_request(lpECB,mapper,map_i->second,targeturl);
1064 pair<bool,const char*> application_id=settings.first->getString("applicationId");
1065 application=conf->getApplication(application_id.second);
1066 const IPropertySet* sessionProps=application ? application->getPropertySet("Sessions") : NULL;
1067 if (!application || !sessionProps)
1068 return WriteClientError(lpECB,"Unable to map request to application session settings, check configuration.");
1070 SHIRE shire(application);
1072 const char* shireURL=shire.getShireURL(targeturl.c_str());
1074 return WriteClientError(lpECB,"Unable to map request to proper shireURL setting, check configuration.");
1076 // Make sure we only process the SHIRE requests.
1077 if (!strstr(targeturl.c_str(),shireURL))
1078 return WriteClientError(lpECB,"ISAPI extension can only be invoked to process incoming sessions."
1079 "Make sure the mapped file extension doesn't match actual content.");
1081 pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
1083 // Make sure this is SSL, if it should be
1084 pair<bool,bool> shireSSL=sessionProps->getBool("shireSSL");
1085 if (!shireSSL.first || shireSSL.second) {
1086 GetServerVariable(lpECB,"HTTPS",buf,10);
1088 throw ShibTargetException(SHIBRPC_OK,"blocked non-SSL access to SHIRE POST processor");
1091 pair<bool,bool> httpRedirects=sessionProps->getBool("httpRedirects");
1092 pair<bool,const char*> redirectPage=sessionProps->getString("redirectPage");
1093 if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
1094 return WriteClientError(lpECB,"HTML-based redirection requires a redirectPage property.");
1096 // Check for Mac web browser
1100 GetServerVariable(lpECB,"HTTP_USER_AGENT",agent,64);
1101 if (strstr(agent,"AppleWebKit/"))
1105 // If this is a GET, we manufacture an AuthnRequest.
1106 if (!stricmp(lpECB->lpszMethod,"GET")) {
1107 const char* areq=lpECB->lpszQueryString ? shire.getLazyAuthnRequest(lpECB->lpszQueryString) : NULL;
1109 throw ShibTargetException(SHIBRPC_OK, "malformed arguments to request a new session");
1110 if (!httpRedirects.first || httpRedirects.second) {
1111 string hdrs=string("Location: ") + areq + "\r\n"
1112 "Content-Type: text/html\r\n"
1113 "Content-Length: 40\r\n"
1114 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
1115 "Cache-Control: private,no-store,no-cache\r\n\r\n";
1116 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)hdrs.c_str());
1117 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
1119 lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
1120 return HSE_STATUS_SUCCESS;
1123 ShibMLP markupProcessor;
1124 markupProcessor.insert("requestURL",areq);
1125 return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor);
1128 else if (stricmp(lpECB->lpszMethod,"POST"))
1129 throw ShibTargetException(SHIBRPC_OK,"blocked non-POST to SHIRE POST processor");
1131 // Sure sure this POST is an appropriate content type
1132 if (!lpECB->lpszContentType || stricmp(lpECB->lpszContentType,"application/x-www-form-urlencoded"))
1133 throw ShibTargetException(SHIBRPC_OK,"blocked bad content-type to SHIRE POST processor");
1136 pair<const char*,const char*> elements=pair<const char*,const char*>(NULL,NULL);
1137 if (lpECB->cbTotalBytes > 1024*1024) // 1MB?
1138 throw ShibTargetException(SHIBRPC_OK,"blocked too-large a post to SHIRE POST processor");
1139 else if (lpECB->cbTotalBytes!=lpECB->cbAvailable) {
1142 DWORD datalen=lpECB->cbTotalBytes;
1145 BOOL ret=lpECB->ReadClient(lpECB->ConnID,buf,&buflen);
1146 if (!ret || !buflen)
1147 throw ShibTargetException(SHIBRPC_OK,"error reading POST data from browser");
1148 cgistr.append(buf,buflen);
1151 elements=shire.getFormSubmission(cgistr.c_str(),cgistr.length());
1154 elements=shire.getFormSubmission(reinterpret_cast<char*>(lpECB->lpbData),lpECB->cbAvailable);
1156 // Make sure the SAML Response parameter exists
1157 if (!elements.first || !*elements.first)
1158 throw ShibTargetException(SHIBRPC_OK, "SHIRE POST failed to find SAMLResponse form element");
1160 // Make sure the target parameter exists
1161 if (!elements.second || !*elements.second)
1162 throw ShibTargetException(SHIBRPC_OK, "SHIRE POST failed to find TARGET form element");
1164 GetServerVariable(lpECB,"REMOTE_ADDR",buf,16);
1166 // Process the post.
1168 RPCError* status=NULL;
1169 ShibMLP markupProcessor;
1170 markupProcessor.insert("requestURL", targeturl.c_str());
1172 status = shire.sessionCreate(elements.first,buf,cookie);
1174 catch (ShibTargetException &e) {
1175 markupProcessor.insert("errorType", "Session Creation Service Error");
1176 markupProcessor.insert("errorText", e.what());
1177 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1178 return WriteClientError(lpECB, application, "shire", markupProcessor);
1182 markupProcessor.insert("errorType", "Session Creation Service Error");
1183 markupProcessor.insert("errorText", "Unexpected Exception");
1184 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1185 return WriteClientError(lpECB, application, "shire", markupProcessor);
1189 if (status->isError()) {
1190 if (status->isRetryable()) {
1192 const char* loc=shire.getAuthnRequest(elements.second);
1193 if (!httpRedirects.first || httpRedirects.second) {
1194 string hdrs=string("Location: ") + loc + "\r\n"
1195 "Content-Type: text/html\r\n"
1196 "Content-Length: 40\r\n"
1197 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
1198 "Cache-Control: private,no-store,no-cache\r\n\r\n";
1199 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)hdrs.c_str());
1200 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
1202 lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
1203 return HSE_STATUS_SUCCESS;
1206 markupProcessor.insert("requestURL",loc);
1207 return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor);
1211 // Return this error to the user.
1212 markupProcessor.insert(*status);
1214 return WriteClientError(lpECB,application,"shire",markupProcessor);
1218 // We've got a good session, set the cookie and redirect to target.
1219 cookie = string("Set-Cookie: ") + shib_cookie.first + '=' + cookie + shib_cookie.second + "\r\n"
1220 "Expires: 01-Jan-1997 12:00:00 GMT\r\n"
1221 "Cache-Control: private,no-store,no-cache\r\n";
1222 if (!httpRedirects.first || httpRedirects.second) {
1223 cookie=cookie + "Content-Type: text/html\r\nLocation: " + elements.second + "\r\nContent-Length: 40\r\n\r\n";
1224 lpECB->ServerSupportFunction(lpECB->ConnID,HSE_REQ_SEND_RESPONSE_HEADER,"302 Moved",0,(LPDWORD)cookie.c_str());
1225 static const char* redmsg="<HTML><BODY>Redirecting...</BODY></HTML>";
1227 lpECB->WriteClient(lpECB->ConnID,(LPVOID)redmsg,&resplen,HSE_IO_SYNC);
1228 return HSE_STATUS_SUCCESS;
1231 markupProcessor.insert("requestURL",elements.second);
1232 return WriteRedirectPage(lpECB, application, redirectPage.second, markupProcessor, cookie.c_str());
1235 catch (ShibTargetException &e) {
1237 ShibMLP markupProcessor;
1238 markupProcessor.insert("requestURL", targeturl.c_str());
1239 markupProcessor.insert("errorType", "Session Creation Service Error");
1240 markupProcessor.insert("errorText", e.what());
1241 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1242 return WriteClientError(lpECB,application,"shire",markupProcessor);
1248 ShibMLP markupProcessor;
1249 markupProcessor.insert("requestURL", targeturl.c_str());
1250 markupProcessor.insert("errorType", "Session Creation Service Error");
1251 markupProcessor.insert("errorText", "Unexpected Exception");
1252 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
1253 return WriteClientError(lpECB,application,"shire",markupProcessor);
1258 return HSE_STATUS_ERROR;