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 /* nsapi_shib.cpp - Shibboleth NSAPI 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>
86 using namespace log4cpp;
88 using namespace shibboleth;
89 using namespace shibtarget;
91 // macros to output text to client
92 #define NET_WRITE(str) \
93 if (IO_ERROR==net_write(sn->csd,str,strlen(str))) return REQ_EXIT
95 #define NET_WRITE1(buf,fmstr,param) \
96 do { sprintf(buf,fmstr,param); NET_WRITE(buf); } while(0)
98 #define NET_WRITE2(buf,fmstr,param1,param2) \
99 do { sprintf(buf,fmstr,param1,param2); NET_WRITE(buf); } while(0)
101 #define NET_WRITE3(buf,fmstr,param1,param2,param3) \
102 do { sprintf(buf,fmstr,param1,param2,param3); NET_WRITE(buf); } while(0)
104 #define NET_WRITE4(buf,fmstr,param1,param2,param3,param4) \
105 do { sprintf(buf,fmstr,param1,param2,param3,param4); NET_WRITE(buf); } while(0)
108 ShibTargetConfig* g_Config=NULL;
110 string g_ServerScheme;
113 extern "C" NSAPI_PUBLIC void nsapi_shib_exit(void*)
116 g_Config->shutdown();
120 extern "C" NSAPI_PUBLIC int nsapi_shib_init(pblock* pb, Session* sn, Request* rq)
122 // Save off a default hostname for this virtual server.
123 char* name=pblock_findval("server-name",pb);
127 name=server_hostname;
131 name=util_hostname();
137 pblock_nvinsert("error","unable to determine web server hostname",pb);
142 name=pblock_findval("server-scheme",pb);
146 log_error(LOG_INFORM,"nsapi_shib_init",sn,rq,"nsapi_shib loaded for host (%s)",g_ServerName.c_str());
150 const char* schemadir=pblock_findval("shib-schemas",pb);
152 schemadir=getenv("SHIBSCHEMAS");
154 schemadir=SHIB_SCHEMAS;
155 const char* config=pblock_findval("shib-config",pb);
157 config=getenv("SHIBCONFIG");
160 g_Config=&ShibTargetConfig::getConfig();
161 g_Config->setFeatures(
162 ShibTargetConfig::Listener |
163 ShibTargetConfig::Metadata |
164 ShibTargetConfig::AAP |
165 ShibTargetConfig::RequestMapper |
166 ShibTargetConfig::SHIREExtensions |
167 ShibTargetConfig::Logging
169 if (!g_Config->init(schemadir,config)) {
171 pblock_nvinsert("error","unable to initialize Shibboleth libraries",pb);
175 daemon_atrestart(nsapi_shib_exit,NULL);
183 pblock_nvinsert("error","caught exception, unable to initialize Shibboleth libraries",pb);
189 IRequestMapper::Settings map_request(pblock* pb, Session* sn, Request* rq, IRequestMapper* mapper, string& target)
191 // Get everything but hostname...
192 const char* uri=pblock_findval("uri",rq->reqpb);
193 const char* qstr=pblock_findval("query",rq->reqpb);
194 int port=server_portnum;
195 const char* scheme=security_active ? "https" : "http";
196 const char* host=NULL;
202 url=url + '?' + qstr;
204 #ifdef vs_is_default_vs
205 // This is 6.0 or later, so we can distinguish requests to name-based vhosts.
206 if (!vs_is_default_vs)
207 // The beauty here is, a non-default vhost can *only* be accessed if the client
208 // specified the exact name in the Host header. So we can trust the Host header.
209 host=pblock_findval("host", rq->headers);
212 // In other cases, we're going to rely on the initialization process...
213 host=g_ServerName.c_str();
215 target=(g_ServerScheme.empty() ? string(scheme) : g_ServerScheme) + "://" + host;
217 // If port is non-default, append it.
218 if ((!security_active && port!=80) || (security_active && port!=443)) {
220 util_snprintf(portbuf,9,"%d",port);
221 target = target + ':' + portbuf;
226 return mapper->getSettingsFromParsedURL(scheme,host,port,url.c_str());
229 int WriteClientError(Session* sn, Request* rq, char* func, char* msg)
231 log_error(LOG_FAILURE,func,sn,rq,msg);
232 protocol_status(sn,rq,PROTOCOL_SERVER_ERROR,msg);
236 int WriteClientError(Session* sn, Request* rq, const IApplication* app, const char* page, ShibMLP& mlp)
238 const IPropertySet* props=app->getPropertySet("Errors");
240 pair<bool,const char*> p=props->getString(page);
242 ifstream infile(p.second);
243 if (!infile.fail()) {
244 const char* res = mlp.run(infile,props);
246 pblock_nvinsert("Content-Type","text/html",rq->srvhdrs);
247 pblock_nninsert("Content-Length",strlen(res),rq->srvhdrs);
248 pblock_nvinsert("Connection","close",rq->srvhdrs);
249 protocol_status(sn,rq,PROTOCOL_OK,NULL);
250 NET_WRITE(const_cast<char*>(res));
257 log_error(LOG_FAILURE,"WriteClientError",sn,rq,"Unable to open error template, check settings.");
258 protocol_status(sn,rq,PROTOCOL_SERVER_ERROR,"Unable to open error template, check settings.");
262 int WriteRedirectPage(Session* sn, Request* rq, const IApplication* app, const char* file, ShibMLP& mlp)
264 ifstream infile(file);
265 if (!infile.fail()) {
266 const char* res = mlp.run(infile,app->getPropertySet("Errors"));
268 pblock_nvinsert("Content-Type","text/html",rq->srvhdrs);
269 pblock_nninsert("Content-Length",strlen(res),rq->srvhdrs);
270 protocol_status(sn,rq,PROTOCOL_OK,NULL);
271 NET_WRITE(const_cast<char*>(res));
275 log_error(LOG_FAILURE,"WriteRedirectPage",sn,rq,"Unable to open redirect template, check settings.");
276 protocol_status(sn,rq,PROTOCOL_SERVER_ERROR,"Unable to open redirect template, check settings.");
281 #define FUNC "shibboleth"
282 extern "C" NSAPI_PUBLIC int nsapi_shib(pblock* pb, Session* sn, Request* rq)
286 ostringstream threadid;
287 threadid << "[" << getpid() << "] nsapi_shib" << '\0';
288 saml::NDC ndc(threadid.str().c_str());
290 // We lock the configuration system for the duration.
291 IConfig* conf=g_Config->getINI();
294 // Map request to application and content settings.
296 IRequestMapper* mapper=conf->getRequestMapper();
297 Locker locker2(mapper);
298 IRequestMapper::Settings settings=map_request(pb,sn,rq,mapper,targeturl);
299 pair<bool,const char*> application_id=settings.first->getString("applicationId");
300 const IApplication* application=conf->getApplication(application_id.second);
302 return WriteClientError(sn,rq,FUNC,"Unable to map request to application settings, check configuration.");
304 // Declare SHIRE object for this request.
305 SHIRE shire(application);
307 const char* shireURL=shire.getShireURL(targeturl.c_str());
309 return WriteClientError(sn,rq,FUNC,"Unable to map request to proper shireURL setting, check configuration.");
311 // If the user is accessing the SHIRE acceptance point, pass it on.
312 if (targeturl.find(shireURL)!=string::npos)
315 // Now check the policy for this request.
316 pair<bool,bool> requireSession=settings.first->getBool("requireSession");
317 if (!requireSession.first || !requireSession.second) {
318 const char* param=pblock_findval("require-session",pb);
319 if (param && (!strcmp(param,"1") || !strcasecmp(param,"true")))
320 requireSession.second=true;
322 pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
323 pair<bool,bool> httpRedirects=application->getPropertySet("Sessions")->getBool("httpRedirects");
324 pair<bool,const char*> redirectPage=application->getPropertySet("Sessions")->getString("redirectPage");
325 if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
326 return WriteClientError(sn,rq,FUNC,"HTML-based redirection requires a redirectPage property.");
328 // Check for session cookie.
329 const char* session_id=NULL;
331 if (request_header("cookie",(char**)&session_id,sn,rq)==REQ_ABORTED)
332 return WriteClientError(sn,rq,FUNC,"error accessing cookie header");
334 Category::getInstance("nsapi_shib."FUNC).debug("cookie header is {%s}",session_id ? session_id : "NULL");
335 if (session_id && (session_id=strstr(session_id,shib_cookie.first))) {
336 session_id+=strlen(shib_cookie.first) + 1; /* Skip over the '=' */
337 char* cookieend=strchr(session_id,';');
339 // Chop out just the value portion.
340 cookie.assign(session_id,cookieend-session_id-1);
341 session_id=cookie.c_str();
345 if (!session_id || !*session_id) {
346 // If no session required, bail now.
347 if (!requireSession.second)
350 // No acceptable cookie, and we require a session. Generate an AuthnRequest.
351 const char* areq = shire.getAuthnRequest(targeturl.c_str());
352 if (!httpRedirects.first || httpRedirects.second) {
353 pblock_nvinsert("Content-Type","text/html",rq->srvhdrs);
354 pblock_nvinsert("Content-Length","40",rq->srvhdrs);
355 pblock_nvinsert("Expires","01-Jan-1997 12:00:00 GMT",rq->srvhdrs);
356 pblock_nvinsert("Cache-Control","private,no-store,no-cache",rq->srvhdrs);
357 pblock_nvinsert("Location",areq,rq->srvhdrs);
358 protocol_status(sn,rq,PROTOCOL_REDIRECT,"302 Please wait");
359 protocol_start_response(sn,rq);
360 NET_WRITE("<HTML><BODY>Redirecting...</BODY></HTML>");
364 ShibMLP markupProcessor;
365 markupProcessor.insert("requestURL",areq);
366 return WriteRedirectPage(sn, rq, application, redirectPage.second, markupProcessor);
370 // Make sure this session is still valid.
371 RPCError* status = NULL;
372 ShibMLP markupProcessor;
373 markupProcessor.insert("requestURL", targeturl);
376 status = shire.sessionIsValid(session_id, pblock_findval("ip",sn->client));
378 catch (ShibTargetException &e) {
379 markupProcessor.insert("errorType", "Session Processing Error");
380 markupProcessor.insert("errorText", e.what());
381 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
382 return WriteClientError(sn, rq, application, "shire", markupProcessor);
386 markupProcessor.insert("errorType", "Session Processing Error");
387 markupProcessor.insert("errorText", "Unexpected Exception");
388 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
389 return WriteClientError(sn, rq, application, "shire", markupProcessor);
394 if (status->isError()) {
395 if (!requireSession.second)
397 else if (status->isRetryable()) {
398 // Oops, session is invalid. Generate AuthnRequest.
400 const char* areq = shire.getAuthnRequest(targeturl.c_str());
401 if (!httpRedirects.first || httpRedirects.second) {
402 pblock_nvinsert("Content-Type","text/html",rq->srvhdrs);
403 pblock_nvinsert("Content-Length","40",rq->srvhdrs);
404 pblock_nvinsert("Expires","01-Jan-1997 12:00:00 GMT",rq->srvhdrs);
405 pblock_nvinsert("Cache-Control","private,no-store,no-cache",rq->srvhdrs);
406 pblock_nvinsert("Location",areq,rq->srvhdrs);
407 protocol_status(sn,rq,PROTOCOL_REDIRECT,"302 Please wait");
408 protocol_start_response(sn,rq);
409 NET_WRITE("<HTML><BODY>Redirecting...</BODY></HTML>");
413 markupProcessor.insert("requestURL",areq);
414 return WriteRedirectPage(sn, rq, application, redirectPage.second, markupProcessor);
418 // return the error page to the user
419 markupProcessor.insert(*status);
421 return WriteClientError(sn, rq, application, "shire", markupProcessor);
428 vector<SAMLAssertion*> assertions;
429 SAMLAuthenticationStatement* sso_statement=NULL;
432 status = rm.getAssertions(session_id, pblock_findval("ip",sn->client), assertions, &sso_statement);
434 catch (ShibTargetException &e) {
435 markupProcessor.insert("errorType", "Attribute Processing Error");
436 markupProcessor.insert("errorText", e.what());
437 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
438 return WriteClientError(sn, rq, application, "rm", markupProcessor);
442 markupProcessor.insert("errorType", "Attribute Processing Error");
443 markupProcessor.insert("errorText", "Unexpected Exception");
444 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
445 return WriteClientError(sn, rq, application, "rm", markupProcessor);
449 if (status->isError()) {
450 markupProcessor.insert(*status);
452 return WriteClientError(sn, rq, application, "rm", markupProcessor);
456 // Do we have an access control plugin?
457 if (settings.second) {
458 Locker acllock(settings.second);
459 if (!settings.second->authorized(*sso_statement,assertions)) {
460 for (int k = 0; k < assertions.size(); k++)
461 delete assertions[k];
462 delete sso_statement;
463 return WriteClientError(sn, rq, application, "access", markupProcessor);
467 // Get the AAP providers, which contain the attribute policy info.
468 Iterator<IAAP*> provs=application->getAAPProviders();
470 // Clear out the list of mapped attributes
471 while (provs.hasNext()) {
472 IAAP* aap=provs.next();
475 Iterator<const IAttributeRule*> rules=aap->getAttributeRules();
476 while (rules.hasNext()) {
477 const char* header=rules.next()->getHeader();
479 param_free(pblock_remove(header,rq->headers));
484 for (int k = 0; k < assertions.size(); k++)
485 delete assertions[k];
486 delete sso_statement;
487 markupProcessor.insert("errorType", "Attribute Processing Error");
488 markupProcessor.insert("errorText", "Unexpected Exception");
489 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
490 return WriteClientError(sn, rq, application, "rm", markupProcessor);
496 // Maybe export the first assertion.
497 param_free(pblock_remove("remote-user",rq->headers));
498 param_free(pblock_remove("auth-user",rq->vars));
499 param_free(pblock_remove("Shib-Attributes",rq->headers));
500 pair<bool,bool> exp=settings.first->getBool("exportAssertion");
501 if (!exp.first || !exp.second) {
502 const char* param=pblock_findval("export-assertion",pb);
503 if (param && (!strcmp(param,"1") || !strcasecmp(param,"true")))
506 if (exp.second && assertions.size()) {
508 RM::serialize(*(assertions[0]), assertion);
509 string::size_type lfeed;
510 while ((lfeed=assertion.find('\n'))!=string::npos)
511 assertion.erase(lfeed,1);
512 pblock_nvinsert("Shib-Attributes",assertion.c_str(),rq->headers);
515 pblock_nvinsert("auth-type","shibboleth",rq->vars);
516 param_free(pblock_remove("Shib-Origin-Site",rq->headers));
517 param_free(pblock_remove("Shib-Authentication-Method",rq->headers));
518 param_free(pblock_remove("Shib-NameIdentifier-Format",rq->headers));
520 // Export the SAML AuthnMethod and the origin site name.
521 auto_ptr_char os(sso_statement->getSubject()->getNameIdentifier()->getNameQualifier());
522 auto_ptr_char am(sso_statement->getAuthMethod());
523 pblock_nvinsert("Shib-Origin-Site",os.get(),rq->headers);
524 pblock_nvinsert("Shib-Authentication-Method",am.get(),rq->headers);
527 AAP wrapper(provs,sso_statement->getSubject()->getNameIdentifier()->getFormat(),Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
528 if (!wrapper.fail() && wrapper->getHeader()) {
529 auto_ptr_char form(sso_statement->getSubject()->getNameIdentifier()->getFormat());
530 auto_ptr_char nameid(sso_statement->getSubject()->getNameIdentifier()->getName());
531 pblock_nvinsert("Shib-NameIdentifier-Format",form.get(),pb);
532 if (!strcmp(wrapper->getHeader(),"REMOTE_USER")) {
533 pblock_nvinsert("remote-user",nameid.get(),rq->headers);
534 pblock_nvinsert("auth-user",nameid.get(),rq->vars);
537 pblock_nvinsert(wrapper->getHeader(),nameid.get(),rq->headers);
541 param_free(pblock_remove("Shib-Application-ID",rq->headers));
542 pblock_nvinsert("Shib-Application-ID",application_id.second,rq->headers);
544 // Export the attributes.
545 Iterator<SAMLAssertion*> a_iter(assertions);
546 while (a_iter.hasNext()) {
547 SAMLAssertion* assert=a_iter.next();
548 Iterator<SAMLStatement*> statements=assert->getStatements();
549 while (statements.hasNext()) {
550 SAMLAttributeStatement* astate=dynamic_cast<SAMLAttributeStatement*>(statements.next());
553 Iterator<SAMLAttribute*> attrs=astate->getAttributes();
554 while (attrs.hasNext()) {
555 SAMLAttribute* attr=attrs.next();
557 // Are we supposed to export it?
558 AAP wrapper(provs,attr->getName(),attr->getNamespace());
559 if (wrapper.fail() || !wrapper->getHeader())
562 Iterator<string> vals=attr->getSingleByteValues();
563 if (!strcmp(wrapper->getHeader(),"REMOTE_USER") && vals.hasNext()) {
564 char* principal=const_cast<char*>(vals.next().c_str());
565 pblock_nvinsert("remote-user",principal,rq->headers);
566 pblock_nvinsert("auth-user",principal,rq->vars);
571 const char* h=pblock_findval(wrapper->getHeader(),rq->headers);
574 param_free(pblock_remove(wrapper->getHeader(),rq->headers));
577 for (; vals.hasNext(); it++) {
578 string value = vals.next();
579 for (string::size_type pos = value.find_first_of(";", string::size_type(0));
581 pos = value.find_first_of(";", pos)) {
582 value.insert(pos, "\\");
588 header=header + ';' + value;
590 pblock_nvinsert(wrapper->getHeader(),header.c_str(),rq->headers);
597 for (int k = 0; k < assertions.size(); k++)
598 delete assertions[k];
599 delete sso_statement;
604 return WriteClientError(sn, rq, FUNC,"Out of Memory");
608 return WriteClientError(sn, rq, FUNC,"Server caught an unknown exception.");
612 return WriteClientError(sn, rq, FUNC,"Server reached unreachable code, save my walrus!");
616 #define FUNC "shib_handler"
617 extern "C" NSAPI_PUBLIC int shib_handler(pblock* pb, Session* sn, Request* rq)
620 const IApplication* application=NULL;
623 ostringstream threadid;
624 threadid << "[" << getpid() << "] shib_handler" << '\0';
625 saml::NDC ndc(threadid.str().c_str());
627 // We lock the configuration system for the duration.
628 IConfig* conf=g_Config->getINI();
631 // Map request to application and content settings.
632 IRequestMapper* mapper=conf->getRequestMapper();
633 Locker locker2(mapper);
634 IRequestMapper::Settings settings=map_request(pb,sn,rq,mapper,targeturl);
635 pair<bool,const char*> application_id=settings.first->getString("applicationId");
636 application=conf->getApplication(application_id.second);
637 const IPropertySet* sessionProps=application ? application->getPropertySet("Sessions") : NULL;
638 if (!application || !sessionProps)
639 return WriteClientError(sn,rq,FUNC,"Unable to map request to application settings, check configuration.");
641 SHIRE shire(application);
643 const char* shireURL=shire.getShireURL(targeturl.c_str());
645 return WriteClientError(sn,rq,FUNC,"Unable to map request to proper shireURL setting, check configuration.");
647 // Make sure we only process the SHIRE requests.
648 if (!strstr(targeturl.c_str(),shireURL))
649 return WriteClientError(sn,rq,FUNC,"NSAPI service function can only be invoked to process incoming sessions."
650 "Make sure the mapped file extension or URL doesn't match actual content.");
652 pair<const char*,const char*> shib_cookie=shire.getCookieNameProps();
654 // Make sure this is SSL, if it should be
655 pair<bool,bool> shireSSL=sessionProps->getBool("shireSSL");
656 if (!shireSSL.first || shireSSL.second) {
657 if (!security_active)
658 throw ShibTargetException(SHIBRPC_OK,"blocked non-SSL access to Shibboleth session processor");
661 pair<bool,bool> httpRedirects=sessionProps->getBool("httpRedirects");
662 pair<bool,const char*> redirectPage=sessionProps->getString("redirectPage");
663 if (httpRedirects.first && !httpRedirects.second && !redirectPage.first)
664 return WriteClientError(sn,rq,FUNC,"HTML-based redirection requires a redirectPage property.");
666 // If this is a GET, we manufacture an AuthnRequest.
667 if (!strcasecmp(pblock_findval("method",rq->reqpb),"GET")) {
668 const char* areq=pblock_findval("query",rq->reqpb) ? shire.getLazyAuthnRequest(pblock_findval("query",rq->reqpb)) : NULL;
670 throw ShibTargetException(SHIBRPC_OK, "malformed arguments to request a new session");
671 if (!httpRedirects.first || httpRedirects.second) {
672 pblock_nvinsert("Content-Type","text/html",rq->srvhdrs);
673 pblock_nvinsert("Content-Length","40",rq->srvhdrs);
674 pblock_nvinsert("Expires","01-Jan-1997 12:00:00 GMT",rq->srvhdrs);
675 pblock_nvinsert("Cache-Control","private,no-store,no-cache",rq->srvhdrs);
676 pblock_nvinsert("Location",areq,rq->srvhdrs);
677 protocol_status(sn,rq,PROTOCOL_REDIRECT,"302 Please wait");
678 protocol_start_response(sn,rq);
679 NET_WRITE("<HTML><BODY>Redirecting...</BODY></HTML>");
683 ShibMLP markupProcessor;
684 markupProcessor.insert("requestURL",areq);
685 return WriteRedirectPage(sn, rq, application, redirectPage.second, markupProcessor);
688 else if (strcasecmp(pblock_findval("method",rq->reqpb),"POST"))
689 throw ShibTargetException(SHIBRPC_OK,"blocked non-POST to Shibboleth session processor");
691 // Make sure this POST is an appropriate content type
692 char* content_type=NULL;
693 if (request_header("content-type",&content_type,sn,rq)!=REQ_PROCEED ||
694 !content_type || strcasecmp(content_type,"application/x-www-form-urlencoded"))
695 throw ShibTargetException(SHIBRPC_OK,"blocked bad content-type to Shibboleth session processor");
698 pair<const char*,const char*> elements=pair<const char*,const char*>(NULL,NULL);
699 char* content_length=NULL;
700 if (request_header("content-length",&content_length,sn,rq)!=REQ_PROCEED ||
701 atoi(content_length) > 1024*1024) // 1MB?
702 throw ShibTargetException(SHIBRPC_OK,"blocked too-large a post to Shibboleth session processor");
705 int cl=atoi(content_length);
707 while (cl && ch!=IO_EOF) {
708 ch=netbuf_getc(sn->inbuf);
717 throw ShibTargetException(SHIBRPC_OK,"error reading POST data from browser");
718 elements=shire.getFormSubmission(cgistr.c_str(),cgistr.length());
721 // Make sure the SAML Response parameter exists
722 if (!elements.first || !*elements.first)
723 throw ShibTargetException(SHIBRPC_OK, "Shibboleth POST failed to find SAMLResponse form element");
725 // Make sure the target parameter exists
726 if (!elements.second || !*elements.second)
727 throw ShibTargetException(SHIBRPC_OK, "Shibboleth POST failed to find TARGET form element");
731 RPCError* status=NULL;
732 ShibMLP markupProcessor;
733 markupProcessor.insert("requestURL", targeturl.c_str());
735 status = shire.sessionCreate(elements.first,pblock_findval("ip",sn->client),cookie);
737 catch (ShibTargetException &e) {
738 markupProcessor.insert("errorType", "Session Creation Service Error");
739 markupProcessor.insert("errorText", e.what());
740 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
741 return WriteClientError(sn, rq, application, "shire", markupProcessor);
745 markupProcessor.insert("errorType", "Session Creation Service Error");
746 markupProcessor.insert("errorText", "Unexpected Exception");
747 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
748 return WriteClientError(sn, rq, application, "shire", markupProcessor);
752 if (status->isError()) {
753 if (status->isRetryable()) {
755 const char* loc=shire.getAuthnRequest(elements.second);
756 if (!httpRedirects.first || httpRedirects.second) {
757 pblock_nvinsert("Content-Type","text/html",rq->srvhdrs);
758 pblock_nvinsert("Content-Length","40",rq->srvhdrs);
759 pblock_nvinsert("Expires","01-Jan-1997 12:00:00 GMT",rq->srvhdrs);
760 pblock_nvinsert("Cache-Control","private,no-store,no-cache",rq->srvhdrs);
761 pblock_nvinsert("Location",loc,rq->srvhdrs);
762 protocol_status(sn,rq,PROTOCOL_REDIRECT,"302 Please wait");
763 protocol_start_response(sn,rq);
764 NET_WRITE("<HTML><BODY>Redirecting...</BODY></HTML>");
768 markupProcessor.insert("requestURL",loc);
769 return WriteRedirectPage(sn, rq, application, redirectPage.second, markupProcessor);
773 // Return this error to the user.
774 markupProcessor.insert(*status);
776 return WriteClientError(sn,rq,application,"shire",markupProcessor);
780 // We've got a good session, set the cookie and redirect to target.
781 cookie = string(shib_cookie.first) + '=' + cookie + shib_cookie.second;
782 pblock_nvinsert("Set-Cookie",cookie.c_str(),rq->srvhdrs);
783 if (!httpRedirects.first || httpRedirects.second) {
784 pblock_nvinsert("Content-Type","text/html",rq->srvhdrs);
785 pblock_nvinsert("Content-Length","40",rq->srvhdrs);
786 pblock_nvinsert("Expires","01-Jan-1997 12:00:00 GMT",rq->srvhdrs);
787 pblock_nvinsert("Cache-Control","private,no-store,no-cache",rq->srvhdrs);
788 pblock_nvinsert("Location",elements.second,rq->srvhdrs);
789 protocol_status(sn,rq,PROTOCOL_REDIRECT,"302 Please wait");
790 protocol_start_response(sn,rq);
791 NET_WRITE("<HTML><BODY>Redirecting...</BODY></HTML>");
795 markupProcessor.insert("requestURL",elements.second);
796 return WriteRedirectPage(sn, rq, application, redirectPage.second, markupProcessor);
799 catch (ShibTargetException &e) {
801 ShibMLP markupProcessor;
802 markupProcessor.insert("requestURL", targeturl.c_str());
803 markupProcessor.insert("errorType", "Session Creation Service Error");
804 markupProcessor.insert("errorText", e.what());
805 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
806 return WriteClientError(sn,rq,application,"shire",markupProcessor);
812 ShibMLP markupProcessor;
813 markupProcessor.insert("requestURL", targeturl.c_str());
814 markupProcessor.insert("errorType", "Session Creation Service Error");
815 markupProcessor.insert("errorText", "Unexpected Exception");
816 markupProcessor.insert("errorDesc", "An error occurred while processing your request.");
817 return WriteClientError(sn,rq,application,"shire",markupProcessor);