From 768fefc0b33272c67834fc49952284d3025e7519 Mon Sep 17 00:00:00 2001 From: Scott Cantor Date: Sat, 17 Jan 2004 22:57:14 +0000 Subject: [PATCH] Redesigned target around URL->application mapping --- configs/shibboleth.ini.in | 54 +++--- isapi_shib/isapi_shib.cpp | 319 ++++++++++++++---------------- mod_shibrm/mod_shibrm.cpp | 333 +++++++++++++------------------- mod_shire/mod_shire.cpp | 171 +++++++--------- schemas/shibboleth-appmap-1.0.xsd | 5 +- shib-mysql-ccache/shib-mysql-ccache.cpp | 4 +- shib-target/Makefile.am | 5 +- shib-target/XMLApplicationMapper.cpp | 200 ++++++++++--------- shib-target/ccache-utils.h | 7 +- shib-target/internal.h | 78 ++++++++ shib-target/shib-ccache.cpp | 74 +++---- shib-target/shib-config.cpp | 166 +++++++++------- shib-target/shib-resource.cpp | 179 ----------------- shib-target/shib-resourceentry.cpp | 82 ++++++-- shib-target/shib-rm.cpp | 33 +--- shib-target/shib-rpcerror.cpp | 6 +- shib-target/shib-shire.cpp | 24 ++- shib-target/shib-target.h | 73 ++++--- shib-target/shibrpc-server.cpp | 32 ++- shib-target/shibrpc-svc.c | 6 +- shib-target/shibrpc-xdr.c | 6 +- shib-target/shibrpc.h | 7 +- shib-target/shibrpc.x | 5 +- shib-target/shibtarget.dsp | 12 +- shib/AAP.cpp | 230 ++++++++-------------- shib/ClubShibPOSTProfile.cpp | 12 +- shib/Makefile.am | 1 + shib/Metadata.cpp | 110 ++++++++--- shib/ReloadableXMLFile.cpp | 176 +++++++++++++++++ shib/SAMLBindingFactory.cpp | 9 +- shib/ScopedAttribute.cpp | 69 ++++--- shib/ShibConfig.cpp | 124 ++++-------- shib/ShibPOSTProfile.cpp | 50 ++--- shib/ShibPOSTProfileFactory.cpp | 14 +- shib/ShibSOAPBinding.cpp | 4 +- shib/SimpleAttribute.cpp | 89 --------- shib/XMLCredentials.cpp | 135 ++++--------- shib/XMLMetadata.cpp | 143 ++++---------- shib/XMLTrust.cpp | 136 ++++--------- shib/internal.h | 48 ++--- shib/shib.dsp | 12 +- shib/shib.h | 174 ++++++++++------- 42 files changed, 1585 insertions(+), 1832 deletions(-) delete mode 100644 shib-target/shib-resource.cpp create mode 100644 shib/ReloadableXMLFile.cpp delete mode 100644 shib/SimpleAttribute.cpp diff --git a/configs/shibboleth.ini.in b/configs/shibboleth.ini.in index 059a832..bef4887 100644 --- a/configs/shibboleth.ini.in +++ b/configs/shibboleth.ini.in @@ -5,7 +5,19 @@ sharsocket=/tmp/shar-socket # SERVER CONFIGURATION -# Optional, may also be set per-server (or per-directory in Apache) +# Mandatory +providerID = https://wayf.internet2.edu/InQueue/ServiceProvider +wayfURL = https://wayf.internet2.edu/InQueue/WAYF +cookieName = shib-cookie +shireSSLOnly = false +shireError=@-PKGSYSCONFDIR-@/shireError.html +rmError=@-PKGSYSCONFDIR-@/rmError.html +accessError=@-PKGSYSCONFDIR-@/accessError.html +shireURL = /Shibboleth.shire + +# Optional, may also be set per-application (some are per-directory in Apache) +#requireSession = false +#requestAttributes = #normalizeRequest = true #checkIPAddress = false #contentSSLOnly = false @@ -18,26 +30,14 @@ sharsocket=/tmp/shar-socket supportContact=admin@your.site logoLocation=/logo.gif -# Mandatory -wayfURL = https://wayf.internet2.edu/InQueue/WAYF -cookieName = shib-cookie -shireSSLOnly = false -shireError=@-PKGSYSCONFDIR-@/shireError.html -rmError=@-PKGSYSCONFDIR-@/rmError.html -accessError=@-PKGSYSCONFDIR-@/accessError.html - -# Mandatory for IIS, set for Apache with SHIREURL command -shireURL = /Shibboleth.shire - [shire] logger=@-PKGSYSCONFDIR-@/shire.logger -metadata=metadata_shire +#applicationMap=@-PKGSYSCONFDIR-@/applications.xml [shar] logger=@-PKGSYSCONFDIR-@/shar.logger # If using a TCP-based SHAR, space delimit the allowed client IPs #sharacl = 127.0.0.1 -metadata=metadata_shar # Controls timeouts for AA queries (in seconds) AATimeout=60 @@ -65,18 +65,20 @@ mysql = @-LIBEXECDIR-@/shib-mysql-ccache.so arg1 = --language=@-PREFIX-@/share/english arg2 = --datadir=@-PREFIX-@/data -[metadata_shire] +[metadata] edu.internet2.middleware.shibboleth.metadata.provider.XML=@-PKGSYSCONFDIR-@/sites.xml + +[aap] edu.internet2.middleware.shibboleth.target.AAP.provider.XML=@-PKGSYSCONFDIR-@/AAP.xml -[metadata_shar] -edu.internet2.middleware.shibboleth.metadata.provider.XML=@-PKGSYSCONFDIR-@/sites.xml -edu.internet2.middleware.shibboleth.trust.provider.XML=@-PKGSYSCONFDIR-@/trust.xml +[creds] edu.internet2.middleware.shibboleth.creds.provider.XML=@-PKGSYSCONFDIR-@/creds.xml -edu.internet2.middleware.shibboleth.target.AAP.provider.XML=@-PKGSYSCONFDIR-@/AAP.xml + +[trust] +edu.internet2.middleware.shibboleth.trust.provider.XML=@-PKGSYSCONFDIR-@/trust.xml [isapi] -# When using the ISAPI filter version, map IIS Instance IDs to server names. +# When using the ISAPI filter version, map IIS Instance IDs to server hostnames. # #1=my.server.name @@ -85,18 +87,14 @@ edu.internet2.middleware.shibboleth.target.AAP.provider.XML=@-PKGSYSCONFDIR-@/AA # You can filter incoming users at a high level by listing the policies to allow. InQueue=urn:mace:inqueue -# To define per-server or per-vhost settings, create a section -# for the server's hostname and set or override configuration. -#[my.server.name] +# To define per-application settings, create a section +# for the application's ID and set or override configuration. +#[my_application] #normalizeRequest = true #checkIPAddress = false #contentSSLOnly = false #authLifetime = 7200 #authTimeout = 3600 #exportAssertion = false -# For IIS, determine what content to protect by specifying strings -# to match against the request path. Separate matches with semicolons. -#mustContain = /secure/;/protected/ -# list of attributes to request for server "my.server.name" -# requests everything if this doesn't exist or is empty +#requireSession = true #requestAttributes = diff --git a/isapi_shib/isapi_shib.cpp b/isapi_shib/isapi_shib.cpp index f59d8a6..ab3c9b2 100644 --- a/isapi_shib/isapi_shib.cpp +++ b/isapi_shib/isapi_shib.cpp @@ -77,21 +77,12 @@ using namespace saml; using namespace shibboleth; using namespace shibtarget; -struct settings_t -{ - settings_t() {} - settings_t(string& name) : m_name(name) {} - - string m_name; - vector m_mustContain; -}; - // globals namespace { HINSTANCE g_hinstDLL; ThreadKey* rpc_handle_key = NULL; ShibTargetConfig* g_Config = NULL; - vector g_Sites; + vector g_Sites; } void destroy_handle(void* data) @@ -163,36 +154,13 @@ extern "C" BOOL WINAPI GetFilterVersion(PHTTP_FILTER_VERSION pVer) log.info("configuring for site ID (%d), hostname (%s)",i-1,hostname.empty() ? "null" : hostname.c_str()); // If no section exists for the host, mark it as a "skip" site. - if (!ini.exists(hostname)) + if (hostname == "skip") { log.info("skipping site ID (%d)",i-1); - g_Sites.push_back(settings_t()); - sprintf(iid,"%u",i++); - continue; - } - - settings_t settings(hostname); - - // Content matching string. - string mustcontain; - if (ini.get_tag(hostname,"mustContain",true,&mustcontain) && !mustcontain.empty()) - { - char* buf=strdup(mustcontain.c_str()); - _strupr(buf); - char* start=buf; - while (char* sep=strchr(start,';')) - { - *sep='\0'; - if (*start) - settings.m_mustContain.push_back(start); - start=sep+1; - } - if (*start) - settings.m_mustContain.push_back(start); - free(buf); + hostname.erase(); } - - g_Sites.push_back(settings); + + g_Sites.push_back(hostname); sprintf(iid,"%u",i++); hostname.erase(); } @@ -370,46 +338,40 @@ string url_encode(const char* url) throw (bad_alloc) return s; } -string get_target(PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, settings_t& site) +void get_target_and_appid( + PHTTP_FILTER_CONTEXT pfc, PHTTP_FILTER_PREPROC_HEADERS pn, const char* hostname, string& target, string& appid + ) { - // Reconstructing the requested URL is not fun. Apparently, the PREPROC_HEADERS - // event means way pre. As in, none of the usual CGI headers are in place yet. - // It's actually almost easier, in a way, because all the path-info and query - // stuff is in one place, the requested URL, which we can get. But we have to - // reconstruct the protocol/host pair using tweezers. - string s; - if (pfc->fIsSecurePort) - s="https://"; - else - s="http://"; + dynabuf port(10); + dynabuf url(256); + GetServerVariable(pfc,"SERVER_PORT",port,10); + GetHeader(pn,pfc,"url",url,256,false); + + // First get the appid using the normalized hostname. + ApplicationMapper mapper; + appid = mapper->getApplicationFromParsedURL((pfc->fIsSecurePort ? "https" : "http"), hostname, atoi(port), url); - // We use the "normalizeRequest" tag to decide how to obtain the server's name. - dynabuf buf(256); + target=static_cast(url); + if (port!=(pfc->fIsSecurePort ? "443" : "80")) + target = ':' + static_cast(port) + target; + + // For the target, we use the "normalizeRequest" tag to decide how to set the server's name. string tag; - if (g_Config->getINI().get_tag(site.m_name,"normalizeRequest",true,&tag) && ShibINI::boolean(tag)) + if (g_Config->getINI().get_tag(appid,"normalizeRequest",true,&tag) && ShibINI::boolean(tag)) { - s+=site.m_name; + target=string(pfc->fIsSecurePort ? "https://" : "http://") + hostname + target; } else { - GetServerVariable(pfc,"SERVER_NAME",buf); - s+=buf; + GetServerVariable(pfc,"SERVER_NAME",url); + target=string(pfc->fIsSecurePort ? "https://" : "http://") + static_cast(url) + target; } - - GetServerVariable(pfc,"SERVER_PORT",buf,10); - if (buf!=(pfc->fIsSecurePort ? "443" : "80")) - s=s + ':' + static_cast(buf); - - GetHeader(pn,pfc,"url",buf,256,false); - s+=buf; - - return s; } -string get_shire_location(settings_t& site, const char* target) +string get_shire_location(const char* application_id, const char* target) { string shireURL; - if (g_Config->getINI().get_tag(site.m_name,"shireURL",true,&shireURL) && !shireURL.empty()) + if (g_Config->getINI().get_tag(application_id,"shireURL",true,&shireURL) && !shireURL.empty()) { if (shireURL[0]!='/') return shireURL; @@ -476,35 +438,27 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat return WriteClientError(pfc,"IIS site instance appears to be invalid."); // Match site instance to site settings. - if (site_id>g_Sites.size() || g_Sites[site_id-1].m_name.length()==0) + if (site_id>g_Sites.size() || g_Sites[site_id-1].length()==0) return SF_STATUS_REQ_NEXT_NOTIFICATION; - settings_t& site=g_Sites[site_id-1]; - - string target_url=get_target(pfc,pn,site); - string shire_url=get_shire_location(site,target_url.c_str()); + string& site=g_Sites[site_id-1]; + + string application_id; + string target_url; + get_target_and_appid(pfc,pn,site.c_str(),target_url,application_id); + string shire_url=get_shire_location(application_id.c_str(),target_url.c_str()); // If the user is accessing the SHIRE acceptance point, pass it on. if (target_url.find(shire_url)!=string::npos) return SF_STATUS_REQ_NEXT_NOTIFICATION; - // Get the url request and scan for the must-contain string. - if (!site.m_mustContain.empty()) - { - char* upcased=new char[target_url.length()+1]; - strcpy(upcased,target_url.c_str()); - _strupr(upcased); - for (vector::const_iterator index=site.m_mustContain.begin(); index!=site.m_mustContain.end(); index++) - if (strstr(upcased,index->c_str())) - break; - delete[] upcased; - if (index==site.m_mustContain.end()) - return SF_STATUS_REQ_NEXT_NOTIFICATION; - } + // Now check the policy for this application. + string tag; + ShibINI& ini=g_Config->getINI(); + if (!ini.get_tag(application_id,"requireSession",true,&tag) || !ShibINI::boolean(tag)) + return SF_STATUS_REQ_NEXT_NOTIFICATION; // SSL content check. - ShibINI& ini=g_Config->getINI(); - string tag; - if (ini.get_tag(site.m_name,"contentSSLOnly",true,&tag) && ShibINI::boolean(tag) && !pfc->fIsSecurePort) + if (ini.get_tag(application_id,"contentSSLOnly",true,&tag) && ShibINI::boolean(tag) && !pfc->fIsSecurePort) { return WriteClientError(pfc, "This server is configured to deny non-SSL requests for secure resources. " @@ -517,30 +471,30 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat // Set SHIRE policies. SHIREConfig config; - config.checkIPAddress = (ini.get_tag(site.m_name,"checkIPAddress",true,&tag) && ShibINI::boolean(tag)); + config.checkIPAddress = (ini.get_tag(application_id,"checkIPAddress",true,&tag) && ShibINI::boolean(tag)); config.lifetime=config.timeout=0; tag.erase(); - if (ini.get_tag(site.m_name, "authLifetime", true, &tag)) + if (ini.get_tag(application_id, "authLifetime", true, &tag)) config.lifetime=strtoul(tag.c_str(),NULL,10); tag.erase(); - if (ini.get_tag(site.m_name, "authTimeout", true, &tag)) + if (ini.get_tag(application_id, "authTimeout", true, &tag)) config.timeout=strtoul(tag.c_str(),NULL,10); // Pull the config data we need to handle the various possible conditions. string shib_cookie; - if (!ini.get_tag(site.m_name, "cookieName", true, &shib_cookie)) + if (!ini.get_tag(application_id, "cookieName", true, &shib_cookie)) return WriteClientError(pfc,"The cookieName configuration setting is missing, check configuration."); string wayfLocation; - if (!ini.get_tag(site.m_name, "wayfURL", true, &wayfLocation)) + if (!ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) return WriteClientError(pfc,"The wayfURL configuration setting is missing, check configuration."); string shireError; - if (!ini.get_tag(site.m_name, "shireError", true, &shireError)) + if (!ini.get_tag(application_id, "shireError", true, &shireError)) return WriteClientError(pfc,"The shireError configuration setting is missing, check configuration."); string accessError; - if (!ini.get_tag(site.m_name, "accessError", true, &shireError)) + if (!ini.get_tag(application_id, "accessError", true, &shireError)) return WriteClientError(pfc,"The accessError configuration setting is missing, check configuration."); // Get an RPC handle and build the SHIRE object. @@ -550,7 +504,7 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat rpc_handle = new RPCHandle(shib_target_sockname(), SHIBRPC_PROG, SHIBRPC_VERS_1); rpc_handle_key->setData(rpc_handle); } - SHIRE shire(rpc_handle, config, shire_url); + SHIRE shire(rpc_handle, config, shire_url.c_str()); // Check for authentication cookie. const char* session_id=NULL; @@ -575,16 +529,16 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat // Make sure this session is still valid. RPCError* status = NULL; ShibMLP markupProcessor; - bool has_tag = ini.get_tag(site.m_name, "supportContact", true, &tag); + bool has_tag = ini.get_tag(application_id, "supportContact", true, &tag); markupProcessor.insert("supportContact", has_tag ? tag : ""); - has_tag = ini.get_tag(site.m_name, "logoLocation", true, &tag); + has_tag = ini.get_tag(application_id, "logoLocation", true, &tag); markupProcessor.insert("logoLocation", has_tag ? tag : ""); markupProcessor.insert("requestURL", target_url); dynabuf abuf(16); GetServerVariable(pfc,"REMOTE_ADDR",abuf,16); try { - status = shire.sessionIsValid(session_id, abuf, target_url.c_str()); + status = shire.sessionIsValid(session_id, abuf, application_id.c_str()); } catch (ShibTargetException &e) { markupProcessor.insert("errorType", "SHIRE Processing Error"); @@ -628,11 +582,11 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat // Get the attributes. vector assertions; SAMLAuthenticationStatement* sso_statement=NULL; - status = rm.getAssertions(session_id, buf, target_url.c_str(), assertions, &sso_statement); + status = rm.getAssertions(session_id, buf, application_id.c_str(), assertions, &sso_statement); if (status->isError()) { string rmError; - if (!ini.get_tag(site.m_name, "rmError", true, &shireError)) + if (!ini.get_tag(application_id, "rmError", true, &shireError)) return WriteClientError(pfc,"The rmError configuration setting is missing, check configuration."); markupProcessor.insert(*status); @@ -650,7 +604,7 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat } // Get the AAP providers, which contain the attribute policy info. - Iterator provs=ShibConfig::getConfig().getAAPProviders(); + Iterator provs=g_Config->getAAPProviders(); // Clear out the list of mapped attributes while (provs.hasNext()) @@ -685,8 +639,11 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat pn->SetHeader(pfc,"Shib-Origin-Site:",""); pn->SetHeader(pfc,"Shib-Authentication-Method:",""); + pn->SetHeader(pfc,"Shib-Application-ID:",""); + pn->SetHeader(pfc,"Shib-Application-ID:",const_cast(application_id.c_str())); + // Maybe export the assertion. - if (ini.get_tag(site.m_name,"exportAssertion",true,&tag) && ShibINI::boolean(tag)) + if (ini.get_tag(application_id,"exportAssertion",true,&tag) && ShibINI::boolean(tag)) { string assertion; RM::serialize(*(assertions[0]), assertion); @@ -698,50 +655,54 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat if (sso_statement) { - auto_ptr os(XMLString::transcode(sso_statement->getSubject()->getNameQualifier())); - auto_ptr am(XMLString::transcode(sso_statement->getAuthMethod())); - pn->SetHeader(pfc,"Shib-Origin-Site:", os.get()); - pn->SetHeader(pfc,"Shib-Authentication-Method:", am.get()); + auto_ptr_char os(sso_statement->getSubject()->getNameQualifier()); + auto_ptr_char am(sso_statement->getAuthMethod()); + pn->SetHeader(pfc,"Shib-Origin-Site:", const_cast(os.get())); + pn->SetHeader(pfc,"Shib-Authentication-Method:", const_cast(am.get())); } - // Export the attributes. Only supports a single statement. - Iterator j = assertions.size()==1 ? RM::getAttributes(*(assertions[0])) : EMPTY(SAMLAttribute*); - while (j.hasNext()) - { - SAMLAttribute* attr=j.next(); - - // Are we supposed to export it? - const char* hname=NULL; - AAP wrapper(attr->getName(),attr->getNamespace()); - if (!wrapper.fail()) - hname=wrapper->getHeader(); - if (hname) - { - Iterator vals=attr->getSingleByteValues(); - if (!strcmp(hname,"REMOTE_USER") && vals.hasNext()) - { - char* principal=const_cast(vals.next().c_str()); - pn->SetHeader(pfc,"remote-user:",principal); - pfc->pFilterContext=pfc->AllocMem(pfc,strlen(principal)+1,0); - if (pfc->pFilterContext) - strcpy(static_cast(pfc->pFilterContext),principal); - } - else - { - string header; - for (int it = 0; vals.hasNext(); it++) { - string value = vals.next(); - for (string::size_type pos = value.find_first_of(";", string::size_type(0)); pos != string::npos; pos = value.find_first_of(";", pos)) { - value.insert(pos, "\\"); - pos += 2; - } - if (it == 0) - header=value; - else - header=header + ';' + value; + // Export the attributes. + Iterator a_iter(assertions); + while (a_iter.hasNext()) { + SAMLAssertion* assert=a_iter.next(); + Iterator statements=assert->getStatements(); + while (statements.hasNext()) { + SAMLAttributeStatement* astate=dynamic_cast(statements.next()); + if (!astate) + continue; + Iterator attrs=astate->getAttributes(); + while (attrs.hasNext()) { + SAMLAttribute* attr=attrs.next(); + + // Are we supposed to export it? + AAP wrapper(g_Config->getAAPProviders(),attr->getName(),attr->getNamespace()); + if (wrapper.fail()) + continue; + + Iterator vals=attr->getSingleByteValues(); + if (!strcmp(wrapper->getHeader(),"REMOTE_USER") && vals.hasNext()) { + char* principal=const_cast(vals.next().c_str()); + pn->SetHeader(pfc,"remote-user:",principal); + pfc->pFilterContext=pfc->AllocMem(pfc,strlen(principal)+1,0); + if (pfc->pFilterContext) + strcpy(static_cast(pfc->pFilterContext),principal); } - string hname2=string(hname) + ':'; - pn->SetHeader(pfc,const_cast(hname2.c_str()),const_cast(header.c_str())); + else { + string header; + for (int it = 0; vals.hasNext(); it++) { + string value = vals.next(); + for (string::size_type pos = value.find_first_of(";", string::size_type(0)); pos != string::npos; pos = value.find_first_of(";", pos)) { + value.insert(pos, "\\"); + pos += 2; + } + if (it == 0) + header=value; + else + header=header + ';' + value; + } + string hname2=string(wrapper->getHeader()) + ':'; + pn->SetHeader(pfc,const_cast(hname2.c_str()),const_cast(header.c_str())); + } } } } @@ -772,37 +733,35 @@ extern "C" DWORD WINAPI HttpFilterProc(PHTTP_FILTER_CONTEXT pfc, DWORD notificat return WriteClientError(pfc,"Server reached unreachable code!"); } -string get_target(LPEXTENSION_CONTROL_BLOCK lpECB, settings_t& site) +void get_target_and_appid(LPEXTENSION_CONTROL_BLOCK lpECB, const char* hostname, string& target, string& appid) { - string s; - dynabuf buf(256); - GetServerVariable(lpECB,"HTTPS",buf); - bool SSL=(buf=="on"); - if (SSL) - s="https://"; - else - s="http://"; + dynabuf ssl(5); + dynabuf port(10); + dynabuf url(256); + GetServerVariable(lpECB,"HTTPS",ssl,5); + GetServerVariable(lpECB,"SERVER_PORT",port,10); + GetServerVariable(lpECB,"URL",url,255); + bool SSL=(ssl=="on"); + + // First get the appid using the normalized hostname. + ApplicationMapper mapper; + appid = mapper->getApplicationFromParsedURL((SSL ? "https" : "http"), hostname, atoi(port), url); + + target=static_cast(url); + if (port!=(SSL ? "443" : "80")) + target = ':' + static_cast(port) + target; - // We use the "normalizeRequest" tag to decide how to obtain the server's name. + // For the target, we use the "normalizeRequest" tag to decide how to set the server's name. string tag; - if (g_Config->getINI().get_tag(site.m_name,"normalizeRequest",true,&tag) && ShibINI::boolean(tag)) + if (g_Config->getINI().get_tag(appid,"normalizeRequest",true,&tag) && ShibINI::boolean(tag)) { - s+=site.m_name; + target=string(SSL ? "https://" : "http://") + hostname + target; } else { - GetServerVariable(lpECB,"SERVER_NAME",buf); - s+=buf; + GetServerVariable(lpECB,"SERVER_NAME",url); + target=string(SSL ? "https://" : "http://") + static_cast(url) + target; } - - GetServerVariable(lpECB,"SERVER_PORT",buf,10); - if (buf!=(SSL ? "443" : "80")) - s=s + ':' + static_cast(buf); - - GetServerVariable(lpECB,"URL",buf,255); - s+=buf; - - return s; } DWORD WriteClientError(LPEXTENSION_CONTROL_BLOCK lpECB, const char* msg) @@ -855,40 +814,42 @@ extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) return WriteClientError(lpECB,"IIS site instance appears to be invalid."); // Match site instance to site settings. - if (site_id>g_Sites.size() || g_Sites[site_id-1].m_name.length()==0) + if (site_id>g_Sites.size() || g_Sites[site_id-1].length()==0) return WriteClientError(lpECB,"Shibboleth filter not configured for this web site."); - settings_t& site=g_Sites[site_id-1]; + string& site=g_Sites[site_id-1]; + + string target_url,application_id; + get_target_and_appid(lpECB,site.c_str(),target_url,application_id); - if (!ini.get_tag(site.m_name, "shireError", true, &shireError)) + if (!ini.get_tag(application_id, "shireError", true, &shireError)) return WriteClientError(lpECB,"The shireError configuration setting is missing, check configuration."); - string target_url=get_target(lpECB,site); - string shire_url = get_shire_location(site,target_url.c_str()); + string shire_url = target_url; // Set SHIRE policies. SHIREConfig config; string tag; - config.checkIPAddress = (ini.get_tag(site.m_name,"checkIPAddress",true,&tag) && ShibINI::boolean(tag)); + config.checkIPAddress = (ini.get_tag(application_id,"checkIPAddress",true,&tag) && ShibINI::boolean(tag)); config.lifetime=config.timeout=0; tag.erase(); - if (ini.get_tag(site.m_name, "authLifetime", true, &tag)) + if (ini.get_tag(application_id, "authLifetime", true, &tag)) config.lifetime=strtoul(tag.c_str(),NULL,10); tag.erase(); - if (ini.get_tag(site.m_name, "authTimeout", true, &tag)) + if (ini.get_tag(application_id, "authTimeout", true, &tag)) config.timeout=strtoul(tag.c_str(),NULL,10); // Pull the config data we need to handle the various possible conditions. string shib_cookie; - if (!ini.get_tag(site.m_name, "cookieName", true, &shib_cookie)) + if (!ini.get_tag(application_id, "cookieName", true, &shib_cookie)) return WriteClientError(lpECB,"The cookieName configuration setting is missing, check configuration."); string wayfLocation; - if (!ini.get_tag(site.m_name, "wayfURL", true, &wayfLocation)) + if (!ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) return WriteClientError(lpECB,"The wayfURL configuration setting is missing, check configuration."); - bool has_tag = ini.get_tag(site.m_name, "supportContact", true, &tag); + bool has_tag = ini.get_tag(application_id, "supportContact", true, &tag); markupProcessor.insert("supportContact", has_tag ? tag : ""); - has_tag = ini.get_tag(site.m_name, "logoLocation", true, &tag); + has_tag = ini.get_tag(application_id, "logoLocation", true, &tag); markupProcessor.insert("logoLocation", has_tag ? tag : ""); markupProcessor.insert("requestURL", target_url.c_str()); @@ -902,7 +863,7 @@ extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) SHIRE shire(rpc_handle, config, shire_url.c_str()); // Process SHIRE POST - if (ini.get_tag(site.m_name, "shireSSLOnly", true, &tag) && ShibINI::boolean(tag)) + if (ini.get_tag(application_id, "shireSSLOnly", true, &tag) && ShibINI::boolean(tag)) { // Make sure this is SSL, if it should be. GetServerVariable(lpECB,"HTTPS",buf,10); @@ -943,7 +904,7 @@ extern "C" DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK lpECB) // Process the post. string cookie; - RPCError* status = shire.sessionCreate(post,buf,cookie); + RPCError* status = shire.sessionCreate(post,buf,application_id.c_str(),cookie); if (status->isError()) { if (status->isRetryable()) { diff --git a/mod_shibrm/mod_shibrm.cpp b/mod_shibrm/mod_shibrm.cpp index 062ad60..f8340c7 100644 --- a/mod_shibrm/mod_shibrm.cpp +++ b/mod_shibrm/mod_shibrm.cpp @@ -36,21 +36,6 @@ using namespace shibtarget; namespace { ThreadKey* rpc_handle_key = NULL; ShibTargetConfig* g_Config = NULL; - - map g_mapAttribNameToHeader; - map g_mapAttribRuleToHeader; -} - -extern "C" const char* -ap_set_attribute_mapping(cmd_parms* parms, void*, const char* attrName, - const char* headerName, const char* ruleName) -{ - ap_log_error(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,parms->server, - "ShibMapAttribute command has been deprecated, please transfer this information to your AAP file(s)."); - g_mapAttribNameToHeader[attrName]=headerName; - if (ruleName) - g_mapAttribRuleToHeader[ruleName]=headerName; - return NULL; } extern "C" module MODULE_VAR_EXPORT shibrm_module; @@ -95,9 +80,6 @@ typedef const char* (*config_fn_t)(void); // SHIBRM Module commands static command_rec shibrm_cmds[] = { - {"ShibMapAttribute", (config_fn_t)ap_set_attribute_mapping, NULL, - RSRC_CONF, TAKE23, "Define request header name and 'require' alias for an attribute."}, - {"AuthGroupFile", (config_fn_t)ap_set_file_slot, (void *) XtOffsetOf (shibrm_dir_config, szAuthGrpFile), OR_AUTHCFG, TAKE1, "text file containing group names and member user IDs"}, @@ -217,25 +199,14 @@ static int shibrm_error_page(request_rec* r, const char* filename, ShibMLP& mlp) return DONE; } -// return the "normalized" target URL -static const char* get_target(request_rec* r, const char* target) +static const char* get_application_id(request_rec* r) { - string tag; - if ((g_Config->getINI()).get_tag (ap_get_server_name(r), "normalizeRequest", true, &tag)) - { - if (ShibINI::boolean (tag)) - { - const char* colon=strchr(target,':'); - const char* slash=strchr(colon+3,'/'); - const char* second_colon=strchr(colon+3,':'); - return ap_pstrcat(r->pool,ap_pstrndup(r->pool,target,colon+3-target), - ap_get_server_name(r), - (second_colon && second_colon < slash) ? - second_colon : slash, - NULL); - } - } - return target; + ApplicationMapper mapper; + return ap_pstrdup(r->pool, + mapper->getApplicationFromParsedURL( + ap_http_method(r), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri + ) + ); } extern "C" int shibrm_check_auth(request_rec* r) @@ -256,28 +227,27 @@ extern "C" int shibrm_check_auth(request_rec* r) saml::NDC ndc(threadid.str().c_str()); ShibINI& ini = g_Config->getINI(); - const char* serverName = ap_get_server_name(r); - // Ok, this is a SHIB target; grab the cookie + // This will always be normalized, because Apache uses ap_get_server_name in this API call. + const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r); + + // Map request to application ID, which is the key for config lookup. + const char* application_id=get_application_id(r); + // Ok, this is a SHIB target; grab the cookie string shib_cookie; - if (!ini.get_tag(serverName, "cookieName", true, &shib_cookie)) { + if (!ini.get_tag(application_id, "cookieName", true, &shib_cookie)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shibrm_check_user: no cookieName configuration for %s", - serverName); + "shibrm_check_user: no cookieName configuration for %s", application_id); return SERVER_ERROR; } - const char* targeturl=get_target(r,ap_construct_url(r->pool,r->unparsed_uri,r)); - const char* session_id=NULL; const char* cookies=ap_table_get(r->headers_in,"Cookie"); if (!cookies || !(session_id=strstr(cookies,shib_cookie.c_str()))) { // No cookie??? Must be a server error! - ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, - "shibrm_check_auth() no cookie found"); - + ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,"shibrm_check_auth() no cookie found"); return SERVER_ERROR; } @@ -291,14 +261,14 @@ extern "C" int shibrm_check_auth(request_rec* r) ShibMLP markupProcessor; string tag; - bool has_tag = ini.get_tag(serverName, "supportContact", true, &tag); + bool has_tag = ini.get_tag(application_id, "supportContact", true, &tag); markupProcessor.insert("supportContact", has_tag ? tag : ""); - has_tag = ini.get_tag(serverName, "logoLocation", true, &tag); + has_tag = ini.get_tag(application_id, "logoLocation", true, &tag); markupProcessor.insert("logoLocation", has_tag ? tag : ""); markupProcessor.insert("requestURL", targeturl); // Now grab the attributes... - has_tag = ini.get_tag (serverName, "checkIPAddress", true, &tag); + has_tag = ini.get_tag (application_id, "checkIPAddress", true, &tag); dc->config.checkIPAddress = (has_tag ? ShibINI::boolean (tag) : false); // Get an RPC handle and build the RM object. @@ -312,7 +282,7 @@ extern "C" int shibrm_check_auth(request_rec* r) vector assertions; SAMLAuthenticationStatement* sso_statement=NULL; - RPCError* status = rm.getAssertions(session_id, r->connection->remote_ip, targeturl, assertions, &sso_statement); + RPCError* status = rm.getAssertions(session_id, r->connection->remote_ip, application_id, assertions, &sso_statement); if (status->isError()) { ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, @@ -320,10 +290,9 @@ extern "C" int shibrm_check_auth(request_rec* r) status->getText()); string rmError; - if (!ini.get_tag(serverName, "rmError", true, &rmError)) { + if (!ini.get_tag(application_id, "rmError", true, &rmError)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shibrm_check_auth: no rmError configuration for %s", - serverName); + "shibrm_check_auth: no rmError configuration for %s", application_id); delete status; return SERVER_ERROR; } @@ -334,10 +303,9 @@ extern "C" int shibrm_check_auth(request_rec* r) delete status; string rmError; - if (!ini.get_tag(serverName, "accessError", true, &rmError)) { + if (!ini.get_tag(application_id, "accessError", true, &rmError)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shibrm_check_auth: no accessError configuration for %s", - serverName); + "shibrm_check_auth: no accessError configuration for %s", application_id); delete status; for (int k = 0; k < assertions.size(); k++) @@ -346,19 +314,8 @@ extern "C" int shibrm_check_auth(request_rec* r) return SERVER_ERROR; } - // Only allow a single assertion... - if (assertions.size() > 1) { - ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, - "shibrm_check_auth() found %d assertions (only handle 1 currently)", - assertions.size()); - for (int k = 0; k < assertions.size(); k++) - delete assertions[k]; - delete sso_statement; - return shibrm_error_page (r, rmError.c_str(), markupProcessor); - } - // Get the AAP providers, which contain the attribute policy info. - Iterator provs=ShibConfig::getConfig().getAAPProviders(); + Iterator provs=g_Config->getAAPProviders(); // Clear out the list of mapped attributes while (provs.hasNext()) @@ -387,9 +344,9 @@ extern "C" int shibrm_check_auth(request_rec* r) } provs.reset(); - // Maybe export the assertion. + // Maybe export the first assertion. ap_table_unset(r->headers_in,"Shib-Attributes"); - if (dc->bExportAssertion==1 && assertions.size()==1) { + if (dc->bExportAssertion==1 && assertions.size()) { string assertion; RM::serialize(*(assertions[0]), assertion); ap_table_set(r->headers_in,"Shib-Attributes", assertion.c_str()); @@ -400,53 +357,49 @@ extern "C" int shibrm_check_auth(request_rec* r) ap_table_unset(r->headers_in,"Shib-Authentication-Method"); if (sso_statement) { - auto_ptr os(XMLString::transcode(sso_statement->getSubject()->getNameQualifier())); - auto_ptr am(XMLString::transcode(sso_statement->getAuthMethod())); + auto_ptr_char os(sso_statement->getSubject()->getNameQualifier()); + auto_ptr_char am(sso_statement->getAuthMethod()); ap_table_set(r->headers_in,"Shib-Origin-Site", os.get()); ap_table_set(r->headers_in,"Shib-Authentication-Method", am.get()); } - - // Export the attributes. Only supports a single statement. - Iterator j = assertions.size()==1 ? RM::getAttributes(*(assertions[0])) : EMPTY(SAMLAttribute*); - while (j.hasNext()) - { - SAMLAttribute* attr=j.next(); - - // Are we supposed to export it? - const char* hname=NULL; - AAP wrapper(attr->getName(),attr->getNamespace()); - if (!wrapper.fail()) - hname=wrapper->getHeader(); - if (!hname) - { - auto_ptr tname(XMLString::transcode(attr->getName())); - map::const_iterator iname=g_mapAttribNameToHeader.find(tname.get()); - if (iname!=g_mapAttribNameToHeader.end()) - hname=iname->second.c_str(); - } - if (hname) - { - Iterator vals=attr->getSingleByteValues(); - if (!strcmp(hname,"REMOTE_USER") && vals.hasNext()) - r->connection->user=ap_pstrdup(r->connection->pool,vals.next().c_str()); - else - { - char* header = ap_pstrdup(r->pool, ""); - for (int it = 0; vals.hasNext(); it++) { - string value = vals.next(); - for (string::size_type pos = value.find_first_of(";", string::size_type(0)); pos != string::npos; pos = value.find_first_of(";", pos)) { - value.insert(pos, "\\"); - pos += 2; - } - if (it == 0) { - header=ap_pstrcat(r->pool, value.c_str(), NULL); - } - else { - header=ap_pstrcat(r->pool, header, ";", value.c_str(), NULL); + + ap_table_unset(r->headers_in,"Shib-Application-ID"); + ap_table_set(r->headers_in,"Shib-Application-ID",application_id); + + // Export the attributes. + Iterator a_iter(assertions); + while (a_iter.hasNext()) { + SAMLAssertion* assert=a_iter.next(); + Iterator statements=assert->getStatements(); + while (statements.hasNext()) { + SAMLAttributeStatement* astate=dynamic_cast(statements.next()); + if (!astate) + continue; + Iterator attrs=astate->getAttributes(); + while (attrs.hasNext()) { + SAMLAttribute* attr=attrs.next(); + + // Are we supposed to export it? + AAP wrapper(provs,attr->getName(),attr->getNamespace()); + if (wrapper.fail()) + continue; + + Iterator vals=attr->getSingleByteValues(); + if (!strcmp(wrapper->getHeader(),"REMOTE_USER") && vals.hasNext()) + r->connection->user=ap_pstrdup(r->connection->pool,vals.next().c_str()); + else { + char* header = ap_pstrdup(r->pool, ""); + for (int it = 0; vals.hasNext(); it++) { + string value = vals.next(); + for (string::size_type pos = value.find_first_of(";", string::size_type(0)); pos != string::npos; pos = value.find_first_of(";", pos)) { + value.insert(pos, "\\"); + pos += 2; + } + header=ap_pstrcat(r->pool, header, (it ? ";" : ""), value.c_str(), NULL); } - } - ap_table_setn(r->headers_in, hname, header); - } + ap_table_setn(r->headers_in, wrapper->getHeader(), header); + } + } } } @@ -508,7 +461,7 @@ extern "C" int shibrm_check_auth(request_rec* r) } catch (XMLException& ex) { - auto_ptr tmp(XMLString::transcode(ex.getMessage())); + auto_ptr_char tmp(ex.getMessage()); ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, "shibrm_check_auth caught exception while parsing regular expression (%s): %s",w,tmp.get()); } @@ -544,112 +497,94 @@ extern "C" int shibrm_check_auth(request_rec* r) } else { - const char* hname=NULL; - AAP wrapper(w); - if (!wrapper.fail()) - hname=wrapper->getHeader(); - if (!hname) - { - map::const_iterator fallback=g_mapAttribRuleToHeader.find(w); - if (fallback!=g_mapAttribRuleToHeader.end()) - hname=fallback->second.c_str(); - } - - if (!hname) { + AAP wrapper(provs,w); + if (wrapper.fail()) { ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,r, "shibrm_check_auth() didn't recognize require rule: %s\n",w); + continue; } - else - { - bool regexp=false; - const char* vals=ap_table_get(r->headers_in,hname); - while (*t && vals) - { - w=ap_getword_conf(r->pool,&t); - if (*w=='~') - { - regexp=true; - continue; - } - try - { - auto_ptr re; - if (regexp) - { - delete re.release(); - auto_ptr trans(fromUTF8(w)); - auto_ptr temp(new RegularExpression(trans.get())); - re=temp; - } - - string vals_str(vals); - int j = 0; - for (int i = 0; i < vals_str.length(); i++) - { - if (vals_str.at(i) == ';') - { - if (i == 0) { - ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,r, - "shibrm_check_auth() invalid header encoding %s: starts with semicolon", vals); - return SERVER_ERROR; - } - - if (vals_str.at(i-1) == '\\') { - vals_str.erase(i-1, 1); - i--; - continue; - } - - string val = vals_str.substr(j, i-j); - j = i+1; - if (regexp) { - auto_ptr trans(fromUTF8(val.c_str())); - if (re->matches(trans.get())) { - ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, - "shibrm_check_auth() expecting %s, got %s: authorization granted", w, val.c_str()); - return OK; - } - } - else if (val==w) { + bool regexp=false; + const char* vals=ap_table_get(r->headers_in,wrapper->getHeader()); + while (*t && vals) { + w=ap_getword_conf(r->pool,&t); + if (*w=='~') { + regexp=true; + continue; + } + + try { + auto_ptr re; + if (regexp) { + delete re.release(); + auto_ptr trans(fromUTF8(w)); + auto_ptr temp(new RegularExpression(trans.get())); + re=temp; + } + + string vals_str(vals); + int j = 0; + for (int i = 0; i < vals_str.length(); i++) { + if (vals_str.at(i) == ';') { + if (i == 0) { + ap_log_rerror(APLOG_MARK,APLOG_WARNING|APLOG_NOERRNO,r, + "shibrm_check_auth() invalid header encoding %s: starts with semicolon", vals); + return SERVER_ERROR; + } + + if (vals_str.at(i-1) == '\\') { + vals_str.erase(i-1, 1); + i--; + continue; + } + + string val = vals_str.substr(j, i-j); + j = i+1; + if (regexp) { + auto_ptr trans(fromUTF8(val.c_str())); + if (re->matches(trans.get())) { ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, "shibrm_check_auth() expecting %s, got %s: authorization granted", w, val.c_str()); return OK; } - else { - ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, - "shibrm_check_auth() expecting %s, got %s: authorization not granted", w, val.c_str()); - } } - } - - string val = vals_str.substr(j, vals_str.length()-j); - if (regexp) { - auto_ptr trans(fromUTF8(val.c_str())); - if (re->matches(trans.get())) { + else if (val==w) { ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, "shibrm_check_auth() expecting %s, got %s: authorization granted", w, val.c_str()); return OK; } + else { + ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, + "shibrm_check_auth() expecting %s, got %s: authorization not granted", w, val.c_str()); + } } - else if (val==w) { + } + + string val = vals_str.substr(j, vals_str.length()-j); + if (regexp) { + auto_ptr trans(fromUTF8(val.c_str())); + if (re->matches(trans.get())) { ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, "shibrm_check_auth() expecting %s, got %s: authorization granted", w, val.c_str()); return OK; } - else { - ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, - "shibrm_check_auth() expecting %s, got %s: authorization not granted", w, val.c_str()); - } } - catch (XMLException& ex) - { - auto_ptr tmp(XMLString::transcode(ex.getMessage())); - ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, - "shibrm_check_auth caught exception while parsing regular expression (%s): %s",w,tmp.get()); + else if (val==w) { + ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, + "shibrm_check_auth() expecting %s, got %s: authorization granted", w, val.c_str()); + return OK; + } + else { + ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, + "shibrm_check_auth() expecting %s, got %s: authorization not granted", w, val.c_str()); } } - } + catch (XMLException& ex) { + auto_ptr tmp(XMLString::transcode(ex.getMessage())); + ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, + "shibrm_check_auth caught exception while parsing regular expression (%s): %s",w,tmp.get()); + } + } } } diff --git a/mod_shire/mod_shire.cpp b/mod_shire/mod_shire.cpp index 1c8e08f..615f1f6 100644 --- a/mod_shire/mod_shire.cpp +++ b/mod_shire/mod_shire.cpp @@ -198,49 +198,35 @@ static char* url_encode(request_rec* r, const char* s) return ret; } -static const char* get_shire_location(request_rec* r, const char* target, bool encode) +static const char* get_application_id(request_rec* r) +{ + ApplicationMapper mapper; + return ap_pstrdup(r->pool, + mapper->getApplicationFromParsedURL( + ap_http_method(r), ap_get_server_name(r), ap_get_server_port(r), r->unparsed_uri + ) + ); +} + +static const char* get_shire_location(request_rec* r, const char* target, const char* application_id) { - ShibINI& ini = g_Config->getINI(); string shire_location; + ShibINI& ini = g_Config->getINI(); - if (g_szSHIREURL) - shire_location = g_szSHIREURL; - else if (! ini.get_tag (ap_get_server_name(r), "shireURL", true, &shire_location)) { + if (!ini.get_tag (application_id, "shireURL", true, &shire_location)) { ap_log_rerror(APLOG_MARK,APLOG_ERR,r, - "shire_get_location() no shireURL configuration for %s", - ap_get_server_name(r)); + "shire_get_location() no shireURL configuration for %s", application_id); return NULL; } const char* shire = shire_location.c_str(); - if (*shire != '/') { - if (encode) - return url_encode(r,shire); - else + if (*shire != '/') return ap_pstrdup(r->pool,shire); - } - const char* colon=strchr(target,':'); - const char* slash=strchr(colon+3,'/'); - if (encode) - return url_encode(r,ap_pstrcat(r->pool, - ap_pstrndup(r->pool,target,slash-target), - shire,NULL)); - else - return ap_pstrcat(r->pool, ap_pstrndup(r->pool,target,slash-target), - shire, NULL); -} - -static bool is_shire_location(request_rec* r, const char* target) -{ - const char* shire = get_shire_location(r, target, false); - if (!shire) return false; - - if (!strstr(target, shire)) - return false; - - return (!strcmp(target,shire)); + const char* colon=strchr(target,':'); + const char* slash=strchr(colon+3,'/'); + return ap_pstrcat(r->pool, ap_pstrndup(r->pool,target,slash-target), shire, NULL); } static int shire_error_page(request_rec* r, const char* filename, ShibMLP& mlp) @@ -265,9 +251,15 @@ extern "C" int shire_check_user(request_rec* r) shire_dir_config* dc=(shire_dir_config*)ap_get_module_config(r->per_dir_config,&shire_module); // This will always be normalized, because Apache uses ap_get_server_name in this API call. - char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r); - - if (is_shire_location (r, targeturl)) { + const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r); + + // Map request to application ID, which is the key for config lookup. + const char* application_id=get_application_id(r); + + // Get unescaped location of this application's assertion consumer service. + const char* unescaped_shire = get_shire_location(r, targeturl, application_id); + + if (strstr(targeturl,unescaped_shire)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, "shire_check_user: REQUEST FOR SHIRE! Maybe you did not configure the SHIRE Handler?"); return SERVER_ERROR; @@ -298,8 +290,7 @@ extern "C" int shire_check_user(request_rec* r) // SSL check. if (dc->bSSLOnly==1 && strcmp(ap_http_method(r),"https")) { - ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, - "shire_check_user() blocked non-SSL access"); + ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r,"shire_check_user() blocked non-SSL access"); return SERVER_ERROR; } } @@ -313,38 +304,29 @@ extern "C" int shire_check_user(request_rec* r) ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, "shire_check_user() Shib check for %s", targeturl); - - const char * shire_location = get_shire_location(r,targeturl,true); - if (!shire_location) - return SERVER_ERROR; - string shire_url = get_shire_location(r,targeturl,false); - - const char* serverName = ap_get_server_name(r); + string tag; - bool has_tag = ini.get_tag (serverName, "checkIPAddress", true, &tag); + bool has_tag = ini.get_tag (application_id, "checkIPAddress", true, &tag); dc->config.checkIPAddress = (has_tag ? ShibINI::boolean (tag) : false); string shib_cookie; - if (! ini.get_tag(serverName, "cookieName", true, &shib_cookie)) { + if (!ini.get_tag(application_id, "cookieName", true, &shib_cookie)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shire_check_user: no cookieName configuration for %s", - serverName); + "shire_check_user: no cookieName configuration for %s", application_id); return SERVER_ERROR; } string wayfLocation; - if (! ini.get_tag(serverName, "wayfURL", true, &wayfLocation)) { + if (!ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shire_check_user: no wayfURL configuration for %s", - serverName); + "shire_check_user: no wayfURL configuration for %s", application_id); return SERVER_ERROR; } string shireError; - if (! ini.get_tag(serverName, "shireError", true, &shireError)) { + if (!ini.get_tag(application_id, "shireError", true, &shireError)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shire_check_user: no shireError configuration for %s", - serverName); + "shire_check_user: no shireError configuration for %s", application_id); return SERVER_ERROR; } @@ -355,7 +337,7 @@ extern "C" int shire_check_user(request_rec* r) rpc_handle = new RPCHandle(shib_target_sockname(), SHIBRPC_PROG, SHIBRPC_VERS_1); rpc_handle_key->setData(rpc_handle); } - SHIRE shire(rpc_handle, dc->config, shire_url); + SHIRE shire(rpc_handle, dc->config, unescaped_shire); // We're in charge, so check for cookie. const char* session_id=NULL; @@ -383,7 +365,7 @@ extern "C" int shire_check_user(request_rec* r) ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, "shire_check_user() no cookie found -- redirecting to WAYF"); char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(), - "?shire=",shire_location, + "?shire=",url_encode(r,unescaped_shire), "&target=",url_encode(r,targeturl),NULL); ap_table_setn(r->headers_out,"Location",wayf); return REDIRECT; @@ -392,14 +374,14 @@ extern "C" int shire_check_user(request_rec* r) // Make sure this session is still valid RPCError* status = NULL; ShibMLP markupProcessor; - has_tag = ini.get_tag(serverName, "supportContact", true, &tag); + has_tag = ini.get_tag(application_id, "supportContact", true, &tag); markupProcessor.insert("supportContact", has_tag ? tag : ""); - has_tag = ini.get_tag(serverName, "logoLocation", true, &tag); + has_tag = ini.get_tag(application_id, "logoLocation", true, &tag); markupProcessor.insert("logoLocation", has_tag ? tag : ""); markupProcessor.insert("requestURL", targeturl); try { - status = shire.sessionIsValid(session_id, r->connection->remote_ip,targeturl); + status = shire.sessionIsValid(session_id, r->connection->remote_ip,application_id); } catch (ShibTargetException &e) { ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_check_user(): %s", e.what()); @@ -425,7 +407,7 @@ extern "C" int shire_check_user(request_rec* r) if (status->isRetryable()) { // Oops, session is invalid. Redirect to WAYF. char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(), - "?shire=",shire_location, + "?shire=",url_encode(r,unescaped_shire), "&target=",url_encode(r,targeturl),NULL); ap_table_setn(r->headers_out,"Location",wayf); @@ -441,8 +423,7 @@ extern "C" int shire_check_user(request_rec* r) } else { delete status; - ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r, - "shire_check_user() success"); + ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_check_user() success"); return OK; } @@ -461,46 +442,44 @@ extern "C" int shire_post_handler (request_rec* r) ap_log_rerror(APLOG_MARK,APLOG_DEBUG|APLOG_NOERRNO,r,"shire_post_handler() ENTER"); - const char* targeturl=ap_construct_url(r->pool,r->unparsed_uri,r); - - const char * shire_location = get_shire_location(r,targeturl,true); - if (!shire_location) - return SERVER_ERROR; - string shire_url = get_shire_location(r,targeturl,false); + // This will always be normalized, because Apache uses ap_get_server_name in this API call. + const char* targeturl = ap_construct_url(r->pool,r->unparsed_uri,r); + + // Map request to application ID, which is the key for config lookup. + const char* application_id = get_application_id(r); + + // The SHIRE URL is the current request URL, by definition... + const char* unescaped_shire = targeturl; - const char* serverName = ap_get_server_name(r); string tag; - bool has_tag = ini.get_tag(serverName, "checkIPAddress", true, &tag); + bool has_tag = ini.get_tag(application_id, "checkIPAddress", true, &tag); SHIREConfig config; config.checkIPAddress = (has_tag ? ShibINI::boolean(tag) : false); string shib_cookie; - if (! ini.get_tag(serverName, "cookieName", true, &shib_cookie)) { + if (! ini.get_tag(application_id, "cookieName", true, &shib_cookie)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shire_check_user: no cookieName configuration for %s", - serverName); + "shire_check_user: no cookieName configuration for %s", application_id); return SERVER_ERROR; } string wayfLocation; - if (! ini.get_tag(serverName, "wayfURL", true, &wayfLocation)) { + if (! ini.get_tag(application_id, "wayfURL", true, &wayfLocation)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shire_check_user: no wayfURL configuration for %s", - serverName); + "shire_check_user: no wayfURL configuration for %s", application_id); return SERVER_ERROR; } string shireError; - if (! ini.get_tag(serverName, "shireError", true, &shireError)) { + if (! ini.get_tag(application_id, "shireError", true, &shireError)) { ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, - "shire_check_user: no shireError configuration for %s", - serverName); + "shire_check_user: no shireError configuration for %s", application_id); return SERVER_ERROR; } - has_tag = ini.get_tag(serverName, "supportContact", true, &tag); + has_tag = ini.get_tag(application_id, "supportContact", true, &tag); markupProcessor.insert("supportContact", has_tag ? tag : ""); - has_tag = ini.get_tag(serverName, "logoLocation", true, &tag); + has_tag = ini.get_tag(application_id, "logoLocation", true, &tag); markupProcessor.insert("logoLocation", has_tag ? tag : ""); markupProcessor.insert("requestURL", targeturl); @@ -511,7 +490,7 @@ extern "C" int shire_post_handler (request_rec* r) rpc_handle = new RPCHandle(shib_target_sockname(), SHIBRPC_PROG, SHIBRPC_VERS_1); rpc_handle_key->setData(rpc_handle); } - SHIRE shire(rpc_handle, config, shire_url); + SHIRE shire(rpc_handle, config, unescaped_shire); // Process SHIRE POST @@ -520,19 +499,17 @@ extern "C" int shire_post_handler (request_rec* r) try { string sslonly; - if (!ini.get_tag(serverName, "shireSSLOnly", true, &sslonly)) + if (!ini.get_tag(application_id, "shireSSLOnly", true, &sslonly)) ap_log_rerror(APLOG_MARK,APLOG_CRIT|APLOG_NOERRNO,r, "shire_post_handler: no shireSSLOnly configuration"); // Make sure this is SSL, if it should be if (ShibINI::boolean(sslonly) && strcmp(ap_http_method(r),"https")) - throw ShibTargetException (SHIBRPC_OK, - "blocked non-SSL access to SHIRE POST processor"); + throw ShibTargetException(SHIBRPC_OK, "blocked non-SSL access to SHIRE POST processor"); // Make sure this is a POST if (strcasecmp (r->method, "POST")) - throw ShibTargetException (SHIBRPC_OK, - "blocked non-POST to SHIRE POST processor"); + throw ShibTargetException(SHIBRPC_OK, "blocked non-POST to SHIRE POST processor"); // Sure sure this POST is an appropriate content type const char *ct = ap_table_get (r->headers_in, "Content-type"); @@ -575,24 +552,24 @@ extern "C" int shire_post_handler (request_rec* r) // process the post string cookie; - RPCError* status = shire.sessionCreate(post, r->connection->remote_ip, cookie); + RPCError* status = shire.sessionCreate(post, r->connection->remote_ip, application_id, cookie); if (status->isError()) { ap_log_rerror(APLOG_MARK,APLOG_ERR|APLOG_NOERRNO,r, "shire_post_handler() POST process failed (%d): %s", status->getCode(), status->getText()); - if (status->isRetryable()) { - ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r, - "shire_post_handler() Retrying POST by redirecting to WAYF"); + if (status->isRetryable()) { + ap_log_rerror(APLOG_MARK,APLOG_INFO|APLOG_NOERRNO,r, + "shire_post_handler() Retrying POST by redirecting to WAYF"); - char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(), - "?shire=",shire_location, + char* wayf=ap_pstrcat(r->pool,wayfLocation.c_str(), + "?shire=",url_encode(r,unescaped_shire), "&target=",url_encode(r,target),NULL); - ap_table_setn(r->headers_out,"Location",wayf); - delete status; - return REDIRECT; - } + ap_table_setn(r->headers_out,"Location",wayf); + delete status; + return REDIRECT; + } // return this error to the user. markupProcessor.insert (*status); diff --git a/schemas/shibboleth-appmap-1.0.xsd b/schemas/shibboleth-appmap-1.0.xsd index 2fbf58d..77ad33a 100644 --- a/schemas/shibboleth-appmap-1.0.xsd +++ b/schemas/shibboleth-appmap-1.0.xsd @@ -13,7 +13,7 @@ - + @@ -36,7 +36,7 @@ - + @@ -46,7 +46,6 @@ - diff --git a/shib-mysql-ccache/shib-mysql-ccache.cpp b/shib-mysql-ccache/shib-mysql-ccache.cpp index f05dd56..13fd3af 100644 --- a/shib-mysql-ccache/shib-mysql-ccache.cpp +++ b/shib-mysql-ccache/shib-mysql-ccache.cpp @@ -61,9 +61,9 @@ public: ShibMySQLCCacheEntry(const char *, CCacheEntry*, ShibMySQLCCache*); ~ShibMySQLCCacheEntry() {} - virtual Iterator getAssertions(Resource& resource) + virtual Iterator getAssertions(const char* resource) { return m_cacheEntry->getAssertions(resource); } - virtual void preFetch(Resource& resource, int prefetch_window) + virtual void preFetch(const char* resource, int prefetch_window) { m_cacheEntry->preFetch(resource, prefetch_window); } virtual bool isSessionValid(time_t lifetime, time_t timeout); virtual const char* getClientAddress() diff --git a/shib-target/Makefile.am b/shib-target/Makefile.am index 3ed6968..8284b98 100644 --- a/shib-target/Makefile.am +++ b/shib-target/Makefile.am @@ -22,7 +22,6 @@ libshib_target_la_SOURCES = \ shib-config.cpp \ shib-ini.cpp \ shib-mlp.cpp \ - shib-resource.cpp \ shib-resourceentry.cpp \ shib-rm.cpp \ shib-rpcerror.cpp \ @@ -34,7 +33,9 @@ libshib_target_la_SOURCES = \ shibrpc-clnt.c \ shibrpc-server.cpp \ shibrpc-svc.c \ - shibrpc-xdr.c + shibrpc-xdr.c \ + XML.cpp \ + XMLApplicationMapper.cpp # this is different from the project version # http://sources.redhat.com/autobook/autobook/autobook_91.html diff --git a/shib-target/XMLApplicationMapper.cpp b/shib-target/XMLApplicationMapper.cpp index 30f64ff..9426c82 100644 --- a/shib-target/XMLApplicationMapper.cpp +++ b/shib-target/XMLApplicationMapper.cpp @@ -73,18 +73,21 @@ namespace shibtarget { struct Override { - Override(const XMLCh* AppID) : m_XMLChAppID(AppID), m_AppID(AppID) {} + Override(const XMLCh* AppID) : m_XMLChAppID((AppID && *AppID) ? AppID : NULL), + m_AppID((AppID && *AppID) ? AppID : NULL) {} ~Override(); const Override* locate(const char* path) const; auto_ptr_char m_AppID; const XMLCh* m_XMLChAppID; map m_map; }; + + const Override* findOverride(const char* vhost, const char* path) const; - const XMLCh* m_XMLChAppID; - string m_AppID; map m_map; map m_extras; + + Category* log; private: Override* buildOverride(const XMLCh* appID, DOMElement* root, Category& log); @@ -102,30 +105,10 @@ XMLApplicationMapperImpl::Override::~Override() delete i->second; } -XMLApplicationMapperImpl::Override* XMLApplicationMapperImpl::buildOverride(const XMLCh* appID, DOMElement* root, Category& log) -{ - Override* o=new Override(appID); - DOMNodeList* nlist = root->getElementsByTagNameNS(shibtarget::XML::APPMAP_NS,shibtarget::XML::Literals::Path); - for (int i=0; nlist && igetLength(); i++) - { - DOMElement* path=static_cast(nlist->item(i)); - const XMLCh* name=path->getAttributeNS(NULL,shibboleth::XML::Literals::Name); - if (!name || !*name) - { - log.warn("Skipping Path element (%d) with empty Name attribute",i); - continue; - } - - auto_ptr_char n(name); - o->m_map[n.get()]=buildOverride(path->getAttributeNS(NULL,shibtarget::XML::Literals::ApplicationID),path,log); - } - return o; -} - XMLApplicationMapperImpl::XMLApplicationMapperImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { NDC ndc("XMLApplicationMapperImpl"); - Category& log=Category::getInstance("shibtarget.XMLApplicationMapperImpl"); + log=&Category::getInstance("shibtarget.XMLApplicationMapper"); try { @@ -133,47 +116,40 @@ XMLApplicationMapperImpl::XMLApplicationMapperImpl(const char* pathname) : Reloa if (XMLString::compareString(shibtarget::XML::APPMAP_NS,e->getNamespaceURI()) || XMLString::compareString(shibtarget::XML::Literals::ApplicationMap,e->getLocalName())) { - log.error("Construction requires a valid app mapping file: (appmap:ApplicationMap as root element)"); + log->error("Construction requires a valid app mapping file: (appmap:ApplicationMap as root element)"); throw MetadataException("Construction requires a valid app mapping file: (appmap:ApplicationMap as root element)"); } - m_XMLChAppID=e->getAttributeNS(NULL,shibtarget::XML::Literals::ApplicationID); - if (!m_XMLChAppID || !*m_XMLChAppID) - { - log.error("Default ApplicationID must be defined"); - throw MetadataException("Default ApplicationID must be defined"); - } - auto_ptr_char defappid(m_XMLChAppID); - m_AppID=defappid.get(); - // Loop over the Host elements. DOMNodeList* nlist = e->getElementsByTagNameNS(shibtarget::XML::APPMAP_NS,shibtarget::XML::Literals::Host); for (int i=0; nlist && igetLength(); i++) { DOMElement* host=static_cast(nlist->item(i)); const XMLCh* scheme=host->getAttributeNS(NULL,shibtarget::XML::Literals::Scheme); - const XMLCh* name=host->getAttributeNS(NULL,shibboleth::XML::Literals::Name); const XMLCh* port=host->getAttributeNS(NULL,shibtarget::XML::Literals::Port); + auto_ptr_XMLCh name(host->getAttributeNS(NULL,shibboleth::XML::Literals::Name)); - if (!name || !*name) + if (!name.get() || !*(name.get())) { - log.warn("Skipping Host element (%d) with empty Name attribute",i); + log->warn("Skipping Host element (%d) with empty Name attribute",i); continue; } + XMLString::lowerCase(const_cast(name.get())); - Override* o=buildOverride(host->getAttributeNS(NULL,shibtarget::XML::Literals::ApplicationID),host,log); + Override* o=buildOverride(host->getAttributeNS(NULL,shibtarget::XML::Literals::ApplicationID),host,*log); auto_ptr_char s(scheme); - auto_ptr_char n(name); + auto_ptr_char n(name.get()); auto_ptr_char p(port); - string url(s.get() ? s.get() : "http"); + string url((s.get() && *(s.get())) ? s.get() : "http"); url=url + "://" + n.get(); - if (p.get()==NULL) + if (p.get()==NULL || *(p.get())=='\0') { // First store a port-less version. if (m_map.count(url)) { - log.warn("Skipping duplicate Host element (%s)",url.c_str()); + log->warn("Skipping duplicate Host element (%s)",url.c_str()); + delete o; continue; } m_map[url]=o; @@ -198,31 +174,35 @@ XMLApplicationMapperImpl::XMLApplicationMapperImpl(const char* pathname) : Reloa url=url + ':' + p.get(); if (m_map.count(url)) { - log.warn("Skipping duplicate Host element (%s)",url.c_str()); + log->warn("Skipping duplicate Host element (%s)",url.c_str()); + delete o; continue; } m_map[url]=o; } + log->debug("Added mapping for %s",url.c_str()); } } catch (SAMLException& e) { - log.errorStream() << "Error while parsing app mapping configuration: " << e.what() << CategoryStream::ENDLINE; + log->errorStream() << "Error while parsing app mapping configuration: " << e.what() << CategoryStream::ENDLINE; for (map::iterator i=m_map.begin(); i!=m_map.end(); i++) delete i->second; if (m_doc) m_doc->release(); throw; } +#ifndef _DEBUG catch (...) { - log.error("Unexpected error while parsing app mapping configuration"); + log->error("Unexpected error while parsing app mapping configuration"); for (map::iterator i=m_map.begin(); i!=m_map.end(); i++) delete i->second; if (m_doc) m_doc->release(); throw; } +#endif } XMLApplicationMapperImpl::~XMLApplicationMapperImpl() @@ -231,17 +211,67 @@ XMLApplicationMapperImpl::~XMLApplicationMapperImpl() delete i->second; } +XMLApplicationMapperImpl::Override* XMLApplicationMapperImpl::buildOverride(const XMLCh* appID, DOMElement* root, Category& log) +{ + Override* o=new Override(appID); + DOMNodeList* nlist = root->getElementsByTagNameNS(shibtarget::XML::APPMAP_NS,shibtarget::XML::Literals::Path); + for (int i=0; nlist && igetLength(); i++) + { + DOMElement* path=static_cast(nlist->item(i)); + auto_ptr_XMLCh name(path->getAttributeNS(NULL,shibboleth::XML::Literals::Name)); + if (!name.get() || !*(name.get())) + { + log.warn("Skipping Path element (%d) with empty Name attribute",i); + continue; + } + XMLString::lowerCase(const_cast(name.get())); + + auto_ptr_char n(name.get()); + o->m_map[n.get()]=buildOverride(path->getAttributeNS(NULL,shibtarget::XML::Literals::ApplicationID),path,log); + } + return o; +} + +const XMLApplicationMapperImpl::Override* XMLApplicationMapperImpl::findOverride(const char* vhost, const char* path) const +{ + const Override* o=NULL; + map::const_iterator i=m_map.find(vhost); + if (i!=m_map.end()) + o=i->second; + else + { + i=m_extras.find(vhost); + if (i!=m_extras.end()) + o=i->second; + } + + if (o) + { + const Override* o2=o->locate(path); + if (o2) + return o2; + } + return o; +} + const XMLApplicationMapperImpl::Override* XMLApplicationMapperImpl::Override::locate(const char* path) const { char* dup=strdup(path); + char* sep=strchr(path,'?'); + if (sep) + *sep=0; + for (char* pch=dup; *pch; pch++) + *pch=tolower(*pch); + + const Override* o=this; const Override* specifier=((m_XMLChAppID && *m_XMLChAppID) ? this : NULL); -#ifdef WIN32 - const char* token=strtok(dup,"/"); -#else +#ifdef HAVE_STRTOK_R char* pos=NULL; const char* token=strtok_r(dup,"/",&pos); +#else + const char* token=strtok(dup,"/"); #endif while (token) { @@ -251,10 +281,10 @@ const XMLApplicationMapperImpl::Override* XMLApplicationMapperImpl::Override::lo o=i->second; if (o->m_XMLChAppID && *(o->m_XMLChAppID)) specifier=o; -#ifdef WIN32 - token=strtok(NULL,"/"); -#else +#ifdef HAVE_STRTOK_R token=strtok_r(NULL,"/",&pos); +#else + token=strtok(NULL,"/"); #endif } @@ -285,38 +315,34 @@ const char* XMLApplicationMapper::getApplicationFromURL(const char* url) const { string vhost; const char* path=split_url(url,vhost); + XMLApplicationMapperImpl* impl=dynamic_cast(getImplementation()); - - map::const_iterator i=impl->m_map.find(vhost); - if (i==impl->m_map.end()) - i=impl->m_extras.find(vhost); - if (i!=impl->m_map.end()) + const XMLApplicationMapperImpl::Override* o=impl->findOverride(vhost.c_str(), path); + + if (impl->log->isDebugEnabled()) { - const XMLApplicationMapperImpl::Override* o=i->second->locate(path); - if (o) - return o->m_AppID.get(); + saml::NDC ndc("getApplicationFromURL"); + impl->log->debug("mapped %s to %s", url, o ? o->m_AppID.get() : "default application ID"); } - - return impl->m_AppID.c_str(); + + return o ? o->m_AppID.get() : ""; } const XMLCh* XMLApplicationMapper::getXMLChApplicationFromURL(const char* url) const { string vhost; const char* path=split_url(url,vhost); + XMLApplicationMapperImpl* impl=dynamic_cast(getImplementation()); - - map::const_iterator i=impl->m_map.find(vhost); - if (i==impl->m_map.end()) - i=impl->m_extras.find(vhost); - if (i!=impl->m_map.end()) + const XMLApplicationMapperImpl::Override* o=impl->findOverride(vhost.c_str(), path); + + if (impl->log->isDebugEnabled()) { - const XMLApplicationMapperImpl::Override* o=i->second->locate(path); - if (o) - return o->m_XMLChAppID; + saml::NDC ndc("getXMLChApplicationFromURL"); + impl->log->debug("mapped %s to %s", url, o ? o->m_AppID.get() : "default application ID"); } - - return impl->m_XMLChAppID; + + return o ? o->m_XMLChAppID : &chNull; } const char* XMLApplicationMapper::getApplicationFromParsedURL( @@ -334,17 +360,15 @@ const char* XMLApplicationMapper::getApplicationFromParsedURL( vhost+=buf; XMLApplicationMapperImpl* impl=dynamic_cast(getImplementation()); - map::const_iterator i=impl->m_map.find(vhost); - if (i==impl->m_map.end()) - i=impl->m_extras.find(vhost); - if (i!=impl->m_map.end()) + const XMLApplicationMapperImpl::Override* o=impl->findOverride(vhost.c_str(), path); + + if (impl->log->isDebugEnabled()) { - const XMLApplicationMapperImpl::Override* o=i->second->locate(path); - if (o) - return o->m_AppID.get(); + saml::NDC ndc("getApplicationFromParsedURL"); + impl->log->debug("mapped %s%s to %s", vhost.c_str(), path ? path : "", o ? o->m_AppID.get() : "default application ID"); } - - return impl->m_AppID.c_str(); + + return o ? o->m_AppID.get() : ""; } const XMLCh* XMLApplicationMapper::getXMLChApplicationFromParsedURL( @@ -362,15 +386,13 @@ const XMLCh* XMLApplicationMapper::getXMLChApplicationFromParsedURL( vhost+=buf; XMLApplicationMapperImpl* impl=dynamic_cast(getImplementation()); - map::const_iterator i=impl->m_map.find(vhost); - if (i==impl->m_map.end()) - i=impl->m_extras.find(vhost); - if (i!=impl->m_map.end()) + const XMLApplicationMapperImpl::Override* o=impl->findOverride(vhost.c_str(), path); + + if (impl->log->isDebugEnabled()) { - const XMLApplicationMapperImpl::Override* o=i->second->locate(path); - if (o) - return o->m_XMLChAppID; + saml::NDC ndc("getXMLChApplicationFromParsedURL"); + impl->log->debug("mapped %s%s to %s", vhost.c_str(), path ? path : "", o ? o->m_AppID.get() : "default application ID"); } - - return impl->m_XMLChAppID; + + return o ? o->m_XMLChAppID : &chNull; } diff --git a/shib-target/ccache-utils.h b/shib-target/ccache-utils.h index a486c9f..a026044 100644 --- a/shib-target/ccache-utils.h +++ b/shib-target/ccache-utils.h @@ -71,8 +71,8 @@ namespace shibtarget { class SHIBTARGET_EXPORTS CCacheEntry { public: - virtual saml::Iterator getAssertions(Resource& resource) = 0; - virtual void preFetch(Resource& resource, int prefetch_window) = 0; + virtual saml::Iterator getAssertions(const char* appId) = 0; + virtual void preFetch(const char* appId, int prefetch_window) = 0; virtual bool isSessionValid(time_t lifetime, time_t timeout) = 0; virtual const char* getClientAddress() = 0; @@ -132,8 +132,7 @@ namespace shibtarget { class ResourceEntry { public: - ResourceEntry(const Resource&, const saml::SAMLSubject&, CCache *, - const saml::Iterator); + ResourceEntry(const char*, const saml::SAMLSubject&, CCache*, const saml::Iterator); ~ResourceEntry(); // Is this ResourceEntry going to be valid for the next seconds? diff --git a/shib-target/internal.h b/shib-target/internal.h index a801df1..80b8772 100644 --- a/shib-target/internal.h +++ b/shib-target/internal.h @@ -84,6 +84,84 @@ using namespace saml; using namespace shibboleth; using namespace shibtarget; +namespace shibtarget { + + // An implementation of the URL->application mapping API using an XML file + class XMLApplicationMapper : public IApplicationMapper, public ReloadableXMLFile + { + public: + XMLApplicationMapper(const char* pathname) : ReloadableXMLFile(pathname) {} + ~XMLApplicationMapper() {} + + const char* getApplicationFromURL(const char* url) const; + const XMLCh* getXMLChApplicationFromURL(const char* url) const; + const char* getApplicationFromParsedURL( + const char* scheme, const char* hostname, unsigned int port, const char* path=NULL + ) const; + const XMLCh* getXMLChApplicationFromParsedURL( + const char* scheme, const char* hostname, unsigned int port, const char* path=NULL + ) const; + + protected: + virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const; + }; + + class STConfig : public ShibTargetConfig + { + public: + STConfig(const char* app_name, const char* inifile); + ~STConfig(); + void ref(); + void init(); + void shutdown(); + ShibINI& getINI() const { return *ini; } + IApplicationMapper* getApplicationMapper() const { return m_applicationMapper; } + saml::Iterator getMetadataProviders() const { return metadatas; } + saml::Iterator getTrustProviders() const { return trusts; } + saml::Iterator getCredentialProviders() const { return creds; } + saml::Iterator getAAPProviders() const { return aaps; } + saml::Iterator getPolicies() const { return saml::Iterator(policies); } + + private: + saml::SAMLConfig& samlConf; + shibboleth::ShibConfig& shibConf; + ShibINI* ini; + std::string m_app_name; + int refcount; + std::vector policies; + std::string m_SocketName; +#ifdef WANT_TCP_SHAR + std::vector m_SocketACL; +#endif + IApplicationMapper* m_applicationMapper; + std::vector metadatas; + std::vector trusts; + std::vector creds; + std::vector aaps; + + friend ShibSockName ::shib_target_sockname(); + friend ShibSockName ::shib_target_sockacl(unsigned int); + }; + + class XML + { + public: + // URI constants + static const XMLCh APPMAP_NS[]; + static const XMLCh APPMAP_SCHEMA_ID[]; + + struct Literals + { + static const XMLCh ApplicationID[]; + static const XMLCh ApplicationMap[]; + static const XMLCh Host[]; + static const XMLCh Path[]; + static const XMLCh Port[]; + static const XMLCh Scheme[]; + }; + }; +} + #endif #endif diff --git a/shib-target/shib-ccache.cpp b/shib-target/shib-ccache.cpp index af3a0b3..1903782 100644 --- a/shib-target/shib-ccache.cpp +++ b/shib-target/shib-ccache.cpp @@ -81,8 +81,8 @@ public: InternalCCacheEntry(SAMLAuthenticationStatement *s, const char *client_addr); ~InternalCCacheEntry(); - virtual Iterator getAssertions(Resource& resource); - virtual void preFetch(Resource& resource, int prefetch_window); + virtual Iterator getAssertions(const char* resource); + virtual void preFetch(const char* resource, int prefetch_window); virtual bool isSessionValid(time_t lifetime, time_t timeout); virtual const char* getClientAddress() { return m_clientAddress.c_str(); } virtual const char* getSerializedStatement() { return m_statement.c_str(); } @@ -94,10 +94,8 @@ public: void rdlock() { cacheitem_lock->rdlock(); } void wrlock() { cacheitem_lock->wrlock(); } - static vector g_emptyVector; - private: - ResourceEntry* populate(Resource& resource, int slop); + ResourceEntry* populate(const char* resource, int slop); ResourceEntry* find(const char* resource); void insert(const char* resource, ResourceEntry* entry); void remove(const char* resource); @@ -131,13 +129,13 @@ private: class ResourceLock { public: - ResourceLock(InternalCCacheEntry* entry, string resource); + ResourceLock(InternalCCacheEntry* entry, const char* resource); ~ResourceLock(); private: - Mutex* find(string& resource); + Mutex* find(const char* resource); InternalCCacheEntry* entry; - string resource; + string m_resource; }; friend class ResourceLock; @@ -150,8 +148,7 @@ public: virtual ~InternalCCache(); virtual CCacheEntry* find(const char* key); - virtual void insert(const char* key, SAMLAuthenticationStatement *s, - const char *client_addr); + virtual void insert(const char* key, SAMLAuthenticationStatement *s, const char *client_addr); virtual void remove(const char* key); InternalCCacheEntry* findi(const char* key); @@ -203,9 +200,6 @@ CCache* CCache::getInstance(const char* type) return (CCache*) new InternalCCache(); } -// static members -vector InternalCCacheEntry::g_emptyVector; - /******************************************************************************/ /* InternalCCache: A Credential Cache */ @@ -213,8 +207,7 @@ vector InternalCCacheEntry::g_emptyVector; InternalCCache::InternalCCache() { - string ctx="shibtarget.InternalCCache"; - log = &(log4cpp::Category::getInstance(ctx)); + log = &(log4cpp::Category::getInstance("shibtarget.InternalCCache")); lock = RWLock::create(); shutdown_wait = CondWait::create(); @@ -263,8 +256,7 @@ CCacheEntry* InternalCCache::find(const char* key) return dynamic_cast(entry); } -void InternalCCache::insert(const char* key, SAMLAuthenticationStatement *s, - const char *client_addr) +void InternalCCache::insert(const char* key, SAMLAuthenticationStatement *s, const char *client_addr) { log->debug("caching new entry for \"%s\"", key); @@ -409,8 +401,7 @@ void* InternalCCache::cleanup_fcn(void* cache_p) InternalCCacheEntry::InternalCCacheEntry(SAMLAuthenticationStatement *s, const char *client_addr) : m_hasbinding(false) { - string ctx = "shibtarget::InternalCCacheEntry"; - log = &(log4cpp::Category::getInstance(ctx)); + log = &(log4cpp::Category::getInstance("shibtarget::InternalCCacheEntry")); pop_locks_lock = Mutex::create(); access_lock = Mutex::create(); resource_lock = RWLock::create(); @@ -426,8 +417,8 @@ InternalCCacheEntry::InternalCCacheEntry(SAMLAuthenticationStatement *s, const c const XMLCh* name = m_subject->getName(); const XMLCh* qual = m_subject->getNameQualifier(); - auto_ptr h(XMLString::transcode(name)); - auto_ptr d(XMLString::transcode(qual)); + auto_ptr_char h(name); + auto_ptr_char d(qual); m_handle = h.get(); m_originSite = d.get(); @@ -448,8 +439,7 @@ InternalCCacheEntry::InternalCCacheEntry(SAMLAuthenticationStatement *s, const c m_statement = os.str(); log->info("New Session Created..."); - log->debug("Handle: \"%s\", Site: \"%s\", Address: %s", h.get(), d.get(), - client_addr); + log->debug("Handle: \"%s\", Site: \"%s\", Address: %s", h.get(), d.get(), client_addr); } InternalCCacheEntry::~InternalCCacheEntry() @@ -491,32 +481,31 @@ bool InternalCCacheEntry::isSessionValid(time_t lifetime, time_t timeout) return true; } -Iterator InternalCCacheEntry::getAssertions(Resource& resource) +Iterator InternalCCacheEntry::getAssertions(const char* resource) { saml::NDC ndc("getAssertions"); ResourceEntry* entry = populate(resource, 0); if (entry) return entry->getAssertions(); - return Iterator(InternalCCacheEntry::g_emptyVector); + return EMPTY(SAMLAssertion*); } -void InternalCCacheEntry::preFetch(Resource& resource, int prefetch_window) +void InternalCCacheEntry::preFetch(const char* resource, int prefetch_window) { saml::NDC ndc("preFetch"); - ResourceEntry* entry = populate(resource, prefetch_window); + populate(resource, prefetch_window); } -ResourceEntry* InternalCCacheEntry::populate(Resource& resource, int slop) +ResourceEntry* InternalCCacheEntry::populate(const char* resource, int slop) { saml::NDC ndc("populate"); - log->debug("populating entry for %s (%s)", - resource.getResource(), resource.getURL()); + log->debug("populating entry for %s", resource); // Lock the resource within this entry... - InternalCCacheEntry::ResourceLock lock(this, resource.getResource()); + InternalCCacheEntry::ResourceLock lock(this, resource); // Can we use what we have? - ResourceEntry *entry = find(resource.getResource()); + ResourceEntry *entry = find(resource); if (entry) { log->debug("found resource"); if (entry->isValid(slop)) @@ -524,7 +513,7 @@ ResourceEntry* InternalCCacheEntry::populate(Resource& resource, int slop) // entry is invalid (expired) -- go fetch a new one. log->debug("removing resource cache; assertion is invalid"); - remove (resource.getResource()); + remove(resource); delete entry; } @@ -536,25 +525,25 @@ ResourceEntry* InternalCCacheEntry::populate(Resource& resource, int slop) } log->info("trying to request attributes for %s@%s -> %s", - m_handle.c_str(), m_originSite.c_str(), resource.getURL()); + m_handle.c_str(), m_originSite.c_str(), resource); try { entry = new ResourceEntry(resource, *m_subject, m_cache, p_auth->getBindings()); } catch (ShibTargetException&) { return NULL; } - insert (resource.getResource(), entry); + insert(resource, entry); log->info("fetched and stored SAML response"); return entry; } -ResourceEntry* InternalCCacheEntry::find(const char* resource_url) +ResourceEntry* InternalCCacheEntry::find(const char* resource) { ReadLock rwlock(resource_lock); - log->debug("find: %s", resource_url); - map::const_iterator i=m_resources.find(resource_url); + log->debug("find: %s", resource); + map::const_iterator i=m_resources.find(resource); if (i==m_resources.end()) { log->debug("no match found"); return NULL; @@ -589,9 +578,8 @@ void InternalCCacheEntry::remove(const char* resource) // will obtain and hold the proper lock until it goes out of scope and // deconstructs. -InternalCCacheEntry::ResourceLock::ResourceLock(InternalCCacheEntry* entry, - string resource) : - entry(entry), resource(resource) +InternalCCacheEntry::ResourceLock::ResourceLock(InternalCCacheEntry* entry, const char* resource) : + entry(entry), m_resource(resource) { Mutex *mutex = find(resource); mutex->lock(); @@ -599,11 +587,11 @@ InternalCCacheEntry::ResourceLock::ResourceLock(InternalCCacheEntry* entry, InternalCCacheEntry::ResourceLock::~ResourceLock() { - Mutex *mutex = find(resource); + Mutex *mutex = find(m_resource.c_str()); mutex->unlock(); } -Mutex* InternalCCacheEntry::ResourceLock::find(string& resource) +Mutex* InternalCCacheEntry::ResourceLock::find(const char* resource) { Lock(entry->pop_locks_lock); diff --git a/shib-target/shib-config.cpp b/shib-target/shib-config.cpp index 9bb9686..8172dcf 100644 --- a/shib-target/shib-config.cpp +++ b/shib-target/shib-config.cpp @@ -66,33 +66,6 @@ #define SHIBTARGET_INIFILE "/opt/shibboleth/etc/shibboleth/shibboleth.ini" #endif -class STConfig : public ShibTargetConfig -{ -public: - STConfig(const char* app_name, const char* inifile); - ~STConfig(); - void shutdown(); - void init(); - ShibINI& getINI() { return *ini; } - - Iterator getPolicies() { return Iterator(policies); } - - void ref(); -private: - SAMLConfig& samlConf; - ShibConfig& shibConf; - ShibINI* ini; - string m_app_name; - int refcount; - vector policies; - string m_SocketName; -#ifdef WANT_TCP_SHAR - vector m_SocketACL; -#endif - friend ShibSockName shib_target_sockname(); - friend ShibSockName shib_target_sockacl(unsigned int); -}; - namespace { STConfig * g_Config = NULL; Mutex * g_lock = NULL; @@ -100,6 +73,7 @@ namespace { CCache* shibtarget::g_shibTargetCCache = NULL; + /****************************************************************************/ // External Interface @@ -141,7 +115,7 @@ ShibTargetConfig& ShibTargetConfig::getConfig() STConfig::STConfig(const char* app_name, const char* inifile) : samlConf(SAMLConfig::getConfig()), shibConf(ShibConfig::getConfig()), - m_app_name(app_name), refcount(0) + m_app_name(app_name), m_applicationMapper(NULL), refcount(0) { try { ini = new ShibINI((inifile ? inifile : SHIBTARGET_INIFILE)); @@ -240,54 +214,67 @@ void STConfig::init() delete iter; } - // Load the specified metadata. - if (ini->get_tag(app, SHIBTARGET_TAG_METADATA, true, &tag) && ini->exists(tag)) - { - ShibINI::Iterator* iter=ini->tag_iterator(tag); - for (const string* prov=iter->begin(); prov; prov=iter->next()) - { - string sources=ini->get(tag,*prov); - int j = 0; - for (int i = 0; i < sources.length(); i++) - { - if (sources.at(i) == ';') - { - string val = sources.substr(j, i-j); - j = i+1; - log.info("registering metadata provider: type=%s, source=%s",prov->c_str(),val.c_str()); - if (!shibConf.addMetadata(prov->c_str(),val.c_str())) - { - log.crit("error adding metadata provider: type=%s, source=%s",prov->c_str(),val.c_str()); - if (app == SHIBTARGET_SHAR) - throw runtime_error("error adding metadata provider"); - } - } + // Load the specified metadata, trust, creds, and aap sources. + const string* prov; + ShibINI::Iterator* iter=ini->tag_iterator(SHIBTARGET_TAG_METADATA); + for (prov=iter->begin(); prov; prov=iter->next()) { + string source=ini->get(SHIBTARGET_TAG_METADATA,*prov); + log.info("building metadata provider: type=%s, source=%s",prov->c_str(),source.c_str()); + try { + metadatas.push_back(shibConf.newMetadata(prov->c_str(),source.c_str())); } - string val = sources.substr(j, sources.length()-j); - log.info("registering metadata provider: type=%s, source=%s",prov->c_str(),val.c_str()); - if (!shibConf.addMetadata(prov->c_str(),val.c_str())) - { - log.crit("error adding metadata provider: type=%s, source=%s",prov->c_str(),val.c_str()); + catch (exception& e) { + log.crit("error building metadata provider: type=%s, source=%s (%s)",prov->c_str(),source.c_str(),e.what()); if (app == SHIBTARGET_SHAR) - throw runtime_error("error adding metadata provider"); + throw; } } delete iter; - } - - // Backward-compatibility-hack to pull in aap-uri from [shire] and load - // as attribute metadata. We load this for anything, not just the SHIRE. - if (ini->get_tag(SHIBTARGET_SHIRE, "aap-uri", false, &tag)) - { - log.warn("using DEPRECATED aap-uri setting for backward compatibility, please read the latest target deploy guide"); - log.info("registering metadata provider: type=edu.internet2.middleware.shibboleth.target.AAP.XML, source=%s",tag.c_str()); - if (!shibConf.addMetadata("edu.internet2.middleware.shibboleth.target.AAP.XML",tag.c_str())) - { - log.crit("error adding metadata provider: type=edu.internet2.middleware.shibboleth.target.AAP.XML, source=%s",tag.c_str()); - if (!strcmp(app.c_str(), SHIBTARGET_SHAR)) - throw runtime_error("error adding metadata provider"); + + iter=ini->tag_iterator(SHIBTARGET_TAG_AAP); + for (prov=iter->begin(); prov; prov=iter->next()) { + string source=ini->get(SHIBTARGET_TAG_AAP,*prov); + log.info("building AAP provider: type=%s, source=%s",prov->c_str(),source.c_str()); + try { + aaps.push_back(shibConf.newAAP(prov->c_str(),source.c_str())); + } + catch (exception& e) { + log.crit("error building AAP provider: type=%s, source=%s (%s)",prov->c_str(),source.c_str(),e.what()); + if (app == SHIBTARGET_SHAR) + throw; + } + } + delete iter; + + if (app == SHIBTARGET_SHAR) { + iter=ini->tag_iterator(SHIBTARGET_TAG_TRUST); + for (prov=iter->begin(); prov; prov=iter->next()) { + string source=ini->get(SHIBTARGET_TAG_TRUST,*prov); + log.info("building trust provider: type=%s, source=%s",prov->c_str(),source.c_str()); + try { + trusts.push_back(shibConf.newTrust(prov->c_str(),source.c_str())); + } + catch (exception& e) { + log.crit("error building trust provider: type=%s, source=%s (%s)",prov->c_str(),source.c_str(),e.what()); + throw; + } + } + delete iter; + + iter=ini->tag_iterator(SHIBTARGET_TAG_CREDS); + for (prov=iter->begin(); prov; prov=iter->next()) { + string source=ini->get(SHIBTARGET_TAG_CREDS,*prov); + log.info("building creds provider: type=%s, source=%s",prov->c_str(),source.c_str()); + try { + creds.push_back(shibConf.newCredentials(prov->c_str(),source.c_str())); + } + catch (exception& e) { + log.crit("error building creds provider: type=%s, source=%s (%s)",prov->c_str(),source.c_str(),e.what()); + throw; + } + } + delete iter; } - } // Load SAML policies. if (ini->exists(SHIBTARGET_POLICIES)) { @@ -300,8 +287,22 @@ void STConfig::init() delete iter; } + if (app == SHIBTARGET_SHIRE && ini->get_tag(app, SHIBTARGET_TAG_APPMAPPER, false, &tag)) { + saml::XML::registerSchema(shibtarget::XML::APPMAP_NS,shibtarget::XML::APPMAP_SCHEMA_ID); + try { + m_applicationMapper=new XMLApplicationMapper(tag.c_str()); + dynamic_cast(m_applicationMapper)->getImplementation(); + } + catch (exception& e) { + log.crit("caught exception while loading URL->Application mapping file (%s)", e.what()); + } + catch (...) { + log.crit("caught unknown exception while loading URL->Application mapping file"); + } + } + // Initialize the SHAR Cache - if (!strcmp (app.c_str(), SHIBTARGET_SHAR)) { + if (app == SHIBTARGET_SHAR) { const char * cache_type = NULL; if (ini->get_tag (app, SHIBTARGET_TAG_CACHETYPE, true, &tag)) cache_type = tag.c_str(); @@ -341,9 +342,23 @@ void STConfig::init() STConfig::~STConfig() { + delete m_applicationMapper; + for (vector::iterator i=policies.begin(); i!=policies.end(); i++) delete const_cast(*i); - + + for (vector::iterator j=metadatas.begin(); j!=metadatas.end(); j++) + delete (*j); + + for (vector::iterator k=trusts.begin(); k!=trusts.end(); k++) + delete (*k); + + for (vector::iterator l=creds.begin(); l!=creds.end(); l++) + delete (*l); + + for (vector::iterator m=aaps.begin(); m!=aaps.end(); m++) + delete (*m); + delete g_shibTargetCCache; delete ini; @@ -378,3 +393,10 @@ extern "C" ShibSockName shib_target_sockacl(unsigned int index) #endif return (ShibSockName)0; } + +ApplicationMapper::ApplicationMapper() : m_mapper(ShibTargetConfig::getConfig().getApplicationMapper()) +{ + if (!m_mapper) + throw runtime_error("application mapper not initialized, check log for errors"); + m_mapper->lock(); +} diff --git a/shib-target/shib-resource.cpp b/shib-target/shib-resource.cpp deleted file mode 100644 index 91ffe78..0000000 --- a/shib-target/shib-resource.cpp +++ /dev/null @@ -1,179 +0,0 @@ -/* - * The Shibboleth License, Version 1. - * Copyright (c) 2002 - * University Corporation for Advanced Internet Development, Inc. - * All rights reserved - * - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution, if any, must include - * the following acknowledgment: "This product includes software developed by - * the University Corporation for Advanced Internet Development - * Internet2 Project. Alternately, this acknowledegement - * may appear in the software itself, if and wherever such third-party - * acknowledgments normally appear. - * - * Neither the name of Shibboleth nor the names of its contributors, nor - * Internet2, nor the University Corporation for Advanced Internet Development, - * Inc., nor UCAID may be used to endorse or promote products derived from this - * software without specific prior written permission. For written permission, - * please contact shibboleth@shibboleth.org - * - * Products derived from this software may not be called Shibboleth, Internet2, - * UCAID, or the University Corporation for Advanced Internet Development, nor - * may Shibboleth appear in their name, without prior written permission of the - * University Corporation for Advanced Internet Development. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK - * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. - * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY - * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/* - * shib-resource.cpp -- an interface to Shibboleth Resources (URL applications) - * - * Created by: Derek Atkins - * - * $Id$ - */ - -#include "internal.h" - -#ifdef HAVE_UNISTD_H -# include -#endif - -#include - -class shibtarget::ResourcePriv -{ -public: - ResourcePriv(const char *str); - ~ResourcePriv(); - - string m_url; - string m_resource; - log4cpp::Category* log; - vector designators; -}; - -ResourcePriv::ResourcePriv(const char *str) -{ - string ctx = "shibtarget.Resource"; - log = &(log4cpp::Category::getInstance(ctx)); - - m_url = str; - - // XXX: The Resource is just the hostname! - const char* colon=strchr(str,':'); - const char* slash=strchr(colon+3,'/'); - m_resource = m_url.substr(0, slash-str); - - log->info("creating resource: \"%s\" -> \"%s\"", str, m_resource.c_str()); - - // Now figure out the designators - string server = m_url.substr(colon-str+3, slash-(colon+3)); - - log->debug("server is \"%s\"", server.c_str()); - - ShibTargetConfig& config = ShibTargetConfig::getConfig(); - ShibINI& ini = config.getINI(); - - string tag; - if (ini.get_tag (server, SHIBTARGET_TAG_REQATTRS, true, &tag)) { - // Now parse the request attributes tag... - - log->debug("Request Attributes: \"%s\"", tag.c_str()); - - auto_ptr tag_str(strdup(tag.c_str())); - - char *tags = tag_str.get(), *tagptr = NULL, *the_tag; -#ifdef HAVE_STRTOK_R - while ((the_tag = strtok_r(tags, " \t\r\n", &tagptr)) != NULL && *the_tag) { -#else - while ((the_tag = strtok(tags, " \t\r\n")) != NULL && *the_tag) { -#endif - // Make sure we don't loop ad-infinitum - tags = NULL; - - log->debug ("Parsed attribute string: \"%s\"", the_tag); - log->debug ("tagptr = %p", tagptr); - - // transcode the attribute string from the tag - auto_ptr temp(XMLString::transcode(the_tag)); - - // Now create the SAML Attribute from this name - try { - SAMLAttribute *attr = - new SAMLAttribute(temp.get(), - shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI); - if (attr) - designators.push_back(attr); - } catch ( ... ) { } - } - } else - log->debug ("No request-attributes found"); -} - -ResourcePriv::~ResourcePriv() -{ - for (vector::iterator i = designators.begin(); - i != designators.end(); i++) - delete *i; -} - -// Internal Class Definition - -Resource::Resource(const char *resource_url) -{ - // XXX: Perform some computation based on the URL... - m_priv = new ResourcePriv(resource_url); -} - -Resource::Resource(string resource_url) -{ - m_priv = new ResourcePriv(resource_url.c_str()); -} - -Resource::~Resource() -{ - delete m_priv; -} - -const char* Resource::getResource() const -{ - return m_priv->m_resource.c_str(); -} - -const char* Resource::getURL() const -{ - return m_priv->m_url.c_str(); -} - -bool Resource::equals(Resource* r2) const -{ - return (!strcmp (m_priv->m_url.c_str(), r2->m_priv->m_url.c_str())); -} - -Iterator Resource::getDesignators() const -{ - return Iterator(m_priv->designators); -} diff --git a/shib-target/shib-resourceentry.cpp b/shib-target/shib-resourceentry.cpp index 4fc9ff0..5218fbe 100644 --- a/shib-target/shib-resourceentry.cpp +++ b/shib-target/shib-resourceentry.cpp @@ -84,8 +84,7 @@ public: ResourceEntryPriv::ResourceEntryPriv() : m_response(NULL), defaultLife(-1) { - string ctx = "shibtarget::ResourceEntry"; - log = &(log4cpp::Category::getInstance(ctx)); + log = &(log4cpp::Category::getInstance("shibtarget::ResourceEntry")); createTime = time(NULL); // Compute and cache the default life for this Resource Entry @@ -105,36 +104,68 @@ ResourceEntryPriv::~ResourceEntryPriv() delete m_response; } -ResourceEntry::ResourceEntry(const Resource &resource, +ResourceEntry::ResourceEntry(const char* resource, const SAMLSubject& p_subject, CCache *m_cache, const Iterator AAbindings) { saml::NDC ndc("ResourceEntry()"); + string tag; + ShibTargetConfig& conf=ShibTargetConfig::getConfig(); + ShibINI& ini = conf.getINI(); + if (!ini.get_tag(resource, "providerID", true, &tag)) + throw ShibTargetException(SHIBRPC_INTERNAL_ERROR,string("unable to determine ProviderID for request, not set?")); + auto_ptr_XMLCh providerID(tag.c_str()); + + vector designators; auto_ptr priv(new ResourceEntryPriv()); - auto_ptr resourceURL(XMLString::transcode(resource.getURL())); + // Look up attributes to request based on resource ID + if (ini.get_tag (resource, SHIBTARGET_TAG_REQATTRS, true, &tag)) { + // Now parse the request attributes tag... + priv->log->debug("Request Attributes: \"%s\"", tag.c_str()); + + auto_ptr tag_str(strdup(tag.c_str())); + char *tags = tag_str.get(), *tagptr = NULL, *the_tag; +#ifdef HAVE_STRTOK_R + while ((the_tag = strtok_r(tags, " \t\r\n", &tagptr)) != NULL && *the_tag) { +#else + while ((the_tag = strtok(tags, " \t\r\n")) != NULL && *the_tag) { +#endif + // Make sure we don't loop ad-infinitum + tags = NULL; + + priv->log->debug ("Parsed attribute string: \"%s\"", the_tag); + priv->log->debug ("tagptr = %p", tagptr); + + // transcode the attribute string from the tag + auto_ptr_XMLCh temp(the_tag); - // Clone the subject... - // 1) I know the static_cast is safe from clone() - // 2) the AttributeQuery will destroy this new subject. - auto_ptr subject(static_cast(p_subject.clone())); + // Now create the SAML Attribute from this name + designators.push_back(new SAMLAttributeDesignator(temp.get(),shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI)); + } + } + else + priv->log->debug ("No request-attributes found, requesting any/all"); // Build a SAML Request.... - SAMLAttributeQuery* q=new SAMLAttributeQuery(subject.get(),resourceURL.get(), - resource.getDesignators().clone()); - subject.release(); + SAMLAttributeQuery* q=new SAMLAttributeQuery( + static_cast(p_subject.clone()),providerID.get(),designators + ); auto_ptr req(new SAMLRequest(EMPTY(QName),q)); // Try this request against all the bindings in the AuthenticationStatement // (i.e. send it to each AA in the list of bindings) SAMLResponse* response = NULL; - OriginMetadata site(p_subject.getNameQualifier()); + OriginMetadata site(conf.getMetadataProviders(),p_subject.getNameQualifier()); if (site.fail()) throw MetadataException("unable to locate origin site's metadata during attribute query"); - auto_ptr caller(XMLString::transcode(resource.getResource())); - auto_ptr pBinding(SAMLBindingFactory::getInstance(caller.get(),site)); + auto_ptr pBinding( + SAMLBindingFactory::getInstance( + conf.getMetadataProviders(),conf.getTrustProviders(),conf.getCredentialProviders(),providerID.get(),site + ) + ); while (!response && AAbindings.hasNext()) { SAMLAuthorityBinding* binding = AAbindings.next(); @@ -147,7 +178,20 @@ ResourceEntry::ResourceEntry(const Resource &resource, // Make sure we got a response if (!response) { priv->log->info ("No Response"); - throw new ShibTargetException(); + throw ShibTargetException(); + } + + // Run it through the AAP. Note that we could end up with an empty response! + Iterator a=response->getAssertions(); + for (unsigned long i=0; i < a.size();) { + try { + AAP::apply(conf.getAAPProviders(),site,*(a[i])); + i++; + } + catch (SAMLException&) { + priv->log->info("no statements remain, removing assertion"); + response->removeAssertion(i); + } } priv->m_response = response; @@ -182,7 +226,7 @@ bool ResourceEntry::isValid(int slop) #endif char timebuf[32]; strftime(timebuf,32,"%Y-%m-%dT%H:%M:%SZ",ptime); - auto_ptr timeptr(XMLString::transcode(timebuf)); + auto_ptr_XMLCh timeptr(timebuf); XMLDateTime curDateTime(timeptr.get()); curDateTime.parseDateTime(); @@ -197,12 +241,12 @@ bool ResourceEntry::isValid(int slop) const XMLDateTime* thistime = assertion->getNotOnOrAfter(); // If there is no time, then just continue and ignore this assertion. - if (! thistime) + if (!thistime) continue; count++; - auto_ptr nowptr(XMLString::transcode(curDateTime.toString())); - auto_ptr assnptr(XMLString::transcode(thistime->toString())); + auto_ptr_char nowptr(curDateTime.getRawData()); + auto_ptr_char assnptr(thistime->getRawData()); m_priv->log->debug ("comparing now (%s) to %s", nowptr.get(), assnptr.get()); int result=XMLDateTime::compareOrder(&curDateTime, thistime); diff --git a/shib-target/shib-rm.cpp b/shib-target/shib-rm.cpp index 5584e02..9932ff3 100644 --- a/shib-target/shib-rm.cpp +++ b/shib-target/shib-rm.cpp @@ -99,8 +99,7 @@ RM::~RM() delete m_priv; } -RPCError* RM::getAssertions(const char* cookie, const char* ip, - const char* url, +RPCError* RM::getAssertions(const char* cookie, const char* ip, const char* resource, vector &assertions, SAMLAuthenticationStatement **statement) { @@ -117,19 +116,14 @@ RPCError* RM::getAssertions(const char* cookie, const char* ip, return new RPCError(-1, "No IP Address"); } - if (!url || *url == '\0') { - m_priv->log->error ("no URL"); - return new RPCError(-1, "Invalid URL Resource"); - } - - m_priv->log->info ("request from %s for \"%s\"", ip, url); + m_priv->log->info ("request from %s for \"%s\"", ip, resource); m_priv->log->debug ("session cookie: %s", cookie); shibrpc_get_assertions_args_1 arg; arg.cookie.cookie = (char*)cookie; arg.cookie.client_addr = (char*)ip; arg.checkIPAddress = m_priv->m_config.checkIPAddress; - arg.url = (char *)url; + arg.application_id = (char *)resource; shibrpc_get_assertions_ret_1 ret; memset (&ret, 0, sizeof(ret)); @@ -185,7 +179,7 @@ RPCError* RM::getAssertions(const char* cookie, const char* ip, while (conds.hasNext()) { SAMLAudienceRestrictionCondition* cond=dynamic_cast(conds.next()); - if (!cond->eval(ShibTargetConfig::getConfig().getPolicies())) + if (!cond->eval(dynamic_cast(ShibTargetConfig::getConfig()).getPolicies())) { m_priv->log->warn("Assertion failed AudienceRestrictionCondition check, skipping it..."); ok=false; @@ -242,22 +236,7 @@ void RM::serialize(SAMLAssertion &assertion, string &result) os << assertion; unsigned int outlen; char* assn = (char*) os.str().c_str(); - XMLByte* serialized = Base64::encode(reinterpret_cast(assn), - os.str().length(), &outlen); + XMLByte* serialized = Base64::encode(reinterpret_cast(assn), os.str().length(), &outlen); result = (char*) serialized; -} - -Iterator RM::getAttributes(SAMLAssertion &assertion) -{ - // XXX: Only deal with a single statement!!!! - Iterator i = assertion.getStatements(); - if (i.hasNext()) { - SAMLAttributeStatement* s = - static_cast(const_cast(i.next())); - - if (s) - return s->getAttributes(); - } - - return EMPTY(SAMLAttribute*); + XMLString::release(&serialized); } diff --git a/shib-target/shib-rpcerror.cpp b/shib-target/shib-rpcerror.cpp index 123c516..3c44b01 100644 --- a/shib-target/shib-rpcerror.cpp +++ b/shib-target/shib-rpcerror.cpp @@ -312,7 +312,7 @@ string RPCError::getOriginErrorURL() string res="No URL Available"; if (m_priv->origin) { - OriginMetadata mapper(m_priv->origin); + OriginMetadata mapper(ShibTargetConfig::getConfig().getMetadataProviders(),m_priv->origin); if (!mapper.fail()) { const char* temp=mapper->getErrorURL(); @@ -328,7 +328,7 @@ string RPCError::getOriginContactName() string res="No Name Available"; if (m_priv->origin) { - OriginMetadata mapper(m_priv->origin); + OriginMetadata mapper(ShibTargetConfig::getConfig().getMetadataProviders(),m_priv->origin); Iterator i= mapper.fail() ? EMPTY(const IContactInfo*) : mapper->getContacts(); while (i.hasNext()) @@ -349,7 +349,7 @@ string RPCError::getOriginContactEmail() string res="No Email Available"; if (m_priv->origin) { - OriginMetadata mapper(m_priv->origin); + OriginMetadata mapper(ShibTargetConfig::getConfig().getMetadataProviders(),m_priv->origin); Iterator i= mapper.fail() ? EMPTY(const IContactInfo*) : mapper->getContacts(); while (i.hasNext()) diff --git a/shib-target/shib-shire.cpp b/shib-target/shib-shire.cpp index 7131843..1c79832 100644 --- a/shib-target/shib-shire.cpp +++ b/shib-target/shib-shire.cpp @@ -66,29 +66,29 @@ class shibtarget::SHIREPriv { public: - SHIREPriv(RPCHandle *rpc, SHIREConfig cfg, string shire_url); + SHIREPriv(RPCHandle *rpc, SHIREConfig cfg, const char* shire_url); ~SHIREPriv(); RPCHandle * m_rpc; SHIREConfig m_config; - string m_url; + string m_shire_url; log4cpp::Category* log; }; -SHIREPriv::SHIREPriv(RPCHandle *rpc, SHIREConfig cfg, string shire_url) +SHIREPriv::SHIREPriv(RPCHandle *rpc, SHIREConfig cfg, const char* shire_url) { string ctx = "shibtarget.SHIRE"; log = &(log4cpp::Category::getInstance(ctx)); m_rpc = rpc; m_config = cfg; - m_url = shire_url; + m_shire_url = shire_url; } SHIREPriv::~SHIREPriv() {} -SHIRE::SHIRE(RPCHandle *rpc, SHIREConfig cfg, string shire_url) +SHIRE::SHIRE(RPCHandle *rpc, SHIREConfig cfg, const char* shire_url) { m_priv = new SHIREPriv(rpc, cfg, shire_url); m_priv->log->info ("New SHIRE handle created: %p", m_priv); @@ -100,7 +100,7 @@ SHIRE::~SHIRE() } -RPCError* SHIRE::sessionIsValid(const char* cookie, const char* ip, const char* url) +RPCError* SHIRE::sessionIsValid(const char* cookie, const char* ip, const char* application_id) { saml::NDC ndc("sessionIsValid"); @@ -115,7 +115,7 @@ RPCError* SHIRE::sessionIsValid(const char* cookie, const char* ip, const char* } // make sure we pass _something_ to the server - if (!url) url = ""; + if (!application_id) application_id = ""; m_priv->log->info ("is session valid: %s", ip); m_priv->log->debug ("session cookie: %s", cookie); @@ -124,7 +124,7 @@ RPCError* SHIRE::sessionIsValid(const char* cookie, const char* ip, const char* arg.cookie.cookie = (char*)cookie; arg.cookie.client_addr = (char *)ip; - arg.url = (char *)url; + arg.application_id = (char *)application_id; arg.lifetime = m_priv->m_config.lifetime; arg.timeout = m_priv->m_config.timeout; arg.checkIPAddress = m_priv->m_config.checkIPAddress; @@ -170,7 +170,7 @@ RPCError* SHIRE::sessionIsValid(const char* cookie, const char* ip, const char* return retval; } -RPCError* SHIRE::sessionCreate(const char* post, const char* ip, string& cookie) +RPCError* SHIRE::sessionCreate(const char* post, const char* ip, const char* application_id, string& cookie) { saml::NDC ndc("sessionCreate"); @@ -184,10 +184,14 @@ RPCError* SHIRE::sessionCreate(const char* post, const char* ip, string& cookie) return new RPCError(-1, "Invalid IP Address"); } + // make sure we pass _something_ to the server + if (!application_id) application_id = ""; + m_priv->log->info ("create session for user at %s", ip); shibrpc_new_session_args_1 arg; - arg.shire_location = (char*) (m_priv->m_url.c_str()); + arg.shire_location = (char*) (m_priv->m_shire_url.c_str()); + arg.application_id = (char*) application_id; arg.saml_post = (char*)post; arg.client_addr = (char*)ip; arg.checkIPAddress = m_priv->m_config.checkIPAddress; diff --git a/shib-target/shib-target.h b/shib-target/shib-target.h index 86a3147..9aee470 100644 --- a/shib-target/shib-target.h +++ b/shib-target/shib-target.h @@ -160,6 +160,10 @@ SHIBTARGET_EXPORTS void shib_sock_close (ShibSocket s, ShibSockName name); #define SHIBTARGET_TAG_SAMLCOMPAT "SAMLCompat" #define SHIBTARGET_TAG_METADATA "metadata" +#define SHIBTARGET_TAG_TRUST "trust" +#define SHIBTARGET_TAG_CREDS "credentials" +#define SHIBTARGET_TAG_AAP "aap" +#define SHIBTARGET_TAG_APPMAPPER "applicationMap" #define SHIBTARGET_TAG_DEFAULTLIFE "defaultLife" @@ -182,23 +186,6 @@ SHIBTARGET_EXPORTS ShibSockName shib_target_sockacl(unsigned int index); namespace shibtarget { - class ResourcePriv; - class SHIBTARGET_EXPORTS Resource - { - public: - Resource(const char* resource_url); - Resource(std::string resource_url); - ~Resource(); - - const char* getResource() const; - const char* getURL() const; - bool equals(Resource*) const; - saml::Iterator getDesignators() const; - - private: - ResourcePriv *m_priv; - }; - class RPCHandleInternal; class SHIBTARGET_EXPORTS RPCHandle { @@ -360,16 +347,11 @@ namespace shibtarget { class SHIBTARGET_EXPORTS SHIRE { public: - SHIRE(RPCHandle *rpc, SHIREConfig config, std::string shire_url); + SHIRE(RPCHandle *rpc, SHIREConfig config, const char* shire_url); ~SHIRE(); - RPCError* sessionIsValid(const char* cookie, const char* ip, const char* url); - RPCError* sessionCreate(const char* post, const char* ip, - std::string &cookie); - - //ShibTargetResponse* is_valid(ShibINI& ini, const char* serverName, - // const char *cookie, const char *target_url); - //ShibTargetResponse* create(); + RPCError* sessionIsValid(const char* cookie, const char* ip, const char* application_id); + RPCError* sessionCreate(const char* post, const char* ip, const char* application_id, std::string &cookie); private: SHIREPriv *m_priv; @@ -388,12 +370,10 @@ namespace shibtarget { RM(RPCHandle *rpc, RMConfig config); ~RM(); - RPCError* getAssertions(const char* cookie, const char* ip, - const char* url, + RPCError* getAssertions(const char* cookie, const char* ip, const char* application_id, std::vector &assertions, saml::SAMLAuthenticationStatement **statement = NULL); static void serialize(saml::SAMLAssertion &assertion, std::string &result); - static saml::Iterator getAttributes(saml::SAMLAssertion &assertion); private: RMPriv *m_priv; }; @@ -493,6 +473,35 @@ namespace shibtarget { void init(std::string& file, bool case_sensitive); }; + // Abstract API to map URLs to application names + struct SHIBTARGET_EXPORTS IApplicationMapper : public virtual shibboleth::ILockable + { + virtual const char* getApplicationFromURL(const char* url) const=0; + virtual const XMLCh* getXMLChApplicationFromURL(const char* url) const=0; + virtual const char* getApplicationFromParsedURL( + const char* scheme, const char* hostname, unsigned int port, const char* path=NULL + ) const=0; + virtual const XMLCh* getXMLChApplicationFromParsedURL( + const char* scheme, const char* hostname, unsigned int port, const char* path=NULL + ) const=0; + virtual ~IApplicationMapper() {} + }; + + // A helper class to wrap the lock/unlock sequence. + class SHIBTARGET_EXPORTS ApplicationMapper + { + public: + ApplicationMapper(); + ~ApplicationMapper() {if (m_mapper) m_mapper->unlock();} + const IApplicationMapper* operator->() const {return m_mapper;} + operator const IApplicationMapper*() const {return m_mapper;} + + private: + ApplicationMapper(const ApplicationMapper&); + void operator=(const ApplicationMapper&); + IApplicationMapper* m_mapper; + }; + class SHIBTARGET_EXPORTS ShibTargetConfig { public: @@ -501,9 +510,13 @@ namespace shibtarget { static ShibTargetConfig& getConfig(); virtual void init() = 0; virtual void shutdown() = 0; + virtual shibtarget::ShibINI& getINI() const = 0; + virtual IApplicationMapper* getApplicationMapper() const = 0; + virtual saml::Iterator getMetadataProviders() const = 0; + virtual saml::Iterator getTrustProviders() const = 0; + virtual saml::Iterator getCredentialProviders() const = 0; + virtual saml::Iterator getAAPProviders() const = 0; virtual ~ShibTargetConfig() {} - virtual ShibINI& getINI() = 0; - virtual saml::Iterator getPolicies() = 0; }; } // namespace diff --git a/shib-target/shibrpc-server.cpp b/shib-target/shibrpc-server.cpp index 4be0296..c99cd57 100644 --- a/shib-target/shibrpc-server.cpp +++ b/shib-target/shibrpc-server.cpp @@ -169,9 +169,8 @@ shibrpc_session_is_valid_1_svc(shibrpc_session_is_valid_args_1 *argp, // and now try to prefetch the attributes .. this could cause an // "error", which is why we call it here. try { - log.debug ("resource: %s", argp->url); - Resource r(argp->url); - entry->preFetch(r,15); // give a 15-second window for the RM + log.debug ("resource: %s", argp->application_id); + entry->preFetch(argp->application_id, 15); // give a 15-second window for the RM } catch (SAMLException &e) { log.debug ("prefetch failed with a SAML Exception: %s", e.what()); @@ -226,15 +225,16 @@ shibrpc_new_session_1_svc(shibrpc_new_session_args_1 *argp, auto_ptr_XMLCh location(argp->shire_location); // Pull in the Policies - Iterator policies=ShibTargetConfig::getConfig().getPolicies(); + Iterator policies=dynamic_cast(ShibTargetConfig::getConfig()).getPolicies(); // And grab the Profile // XXX: Create a "Global" POSTProfile instance per location... log.debug ("create the POST profile (%d policies)", policies.size()); - ShibPOSTProfile *profile = - ShibPOSTProfileFactory::getInstance(policies, - location.get(), - 3600); + ShibPOSTProfile *profile = ShibPOSTProfileFactory::getInstance( + ShibTargetConfig::getConfig().getMetadataProviders(), + ShibTargetConfig::getConfig().getTrustProviders(), + policies,location.get(),3600 + ); SAMLResponse* r = NULL; const SAMLAuthenticationStatement* auth_st = NULL; @@ -375,7 +375,7 @@ shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp, log.debug ("get attrs for client at %s", argp->cookie.client_addr); log.debug ("cookie: %s", argp->cookie.cookie); - log.debug ("resource: %s", argp->url); + log.debug ("resource: %s", argp->application_id); // Find this session CCacheEntry* entry = g_shibTargetCCache->find(argp->cookie.cookie); @@ -404,8 +404,7 @@ shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp, try { // grab the attributes for this resource - Resource resource(argp->url); - Iterator iter = entry->getAssertions(resource); + Iterator iter = entry->getAssertions(argp->application_id); u_int size = iter.size(); result->assertions.assertions_len = size; @@ -413,17 +412,16 @@ shibrpc_get_assertions_1_svc(shibrpc_get_assertions_args_1 *argp, if (size) { // Build the response section - ShibRpcXML* av = - (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML)); + ShibRpcXML* av = (ShibRpcXML*) malloc (size * sizeof (ShibRpcXML)); result->assertions.assertions_val = av; // and then serialize them all... u_int i = 0; while (iter.hasNext()) { - SAMLAssertion* as = iter.next(); - ostringstream os; - os << *as; - av[i++].xml_string = strdup(os.str().c_str()); + SAMLAssertion* as = iter.next(); + ostringstream os; + os << *as; + av[i++].xml_string = strdup(os.str().c_str()); } } } catch (SAMLException& e) { diff --git a/shib-target/shibrpc-svc.c b/shib-target/shibrpc-svc.c index f9af9cb..74c22b4 100644 --- a/shib-target/shibrpc-svc.c +++ b/shib-target/shibrpc-svc.c @@ -70,15 +70,15 @@ shibrpc_prog_1(struct svc_req *rqstp, register SVCXPRT *transp) return; } memset ((char *)&argument, 0, sizeof (argument)); - if (!svc_getargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + if (!svc_getargs (transp, _xdr_argument, (caddr_t) &argument)) { svcerr_decode (transp); return; } retval = (bool_t) (*local)((char *)&argument, (void *)&result, rqstp); - if (retval > 0 && !svc_sendreply(transp, (xdrproc_t) _xdr_result, (char *)&result)) { + if (retval > 0 && !svc_sendreply(transp, _xdr_result, (char *)&result)) { svcerr_systemerr (transp); } - if (!svc_freeargs (transp, (xdrproc_t) _xdr_argument, (caddr_t) &argument)) { + if (!svc_freeargs (transp, _xdr_argument, (caddr_t) &argument)) { fprintf (stderr, "%s", "unable to free arguments"); exit (1); } diff --git a/shib-target/shibrpc-xdr.c b/shib-target/shibrpc-xdr.c index d0dd1eb..c786c18 100644 --- a/shib-target/shibrpc-xdr.c +++ b/shib-target/shibrpc-xdr.c @@ -80,7 +80,7 @@ xdr_shibrpc_session_is_valid_args_1 (XDR *xdrs, shibrpc_session_is_valid_args_1 if (!xdr_ShibRpcHttpCookie_1 (xdrs, &objp->cookie)) return FALSE; - if (!xdr_string (xdrs, &objp->url, ~0)) + if (!xdr_string (xdrs, &objp->application_id, ~0)) return FALSE; if (!xdr_bool (xdrs, &objp->checkIPAddress)) return FALSE; @@ -106,6 +106,8 @@ xdr_shibrpc_new_session_args_1 (XDR *xdrs, shibrpc_new_session_args_1 *objp) { register int32_t *buf; + if (!xdr_string (xdrs, &objp->application_id, ~0)) + return FALSE; if (!xdr_string (xdrs, &objp->shire_location, ~0)) return FALSE; if (!xdr_string (xdrs, &objp->saml_post, ~0)) @@ -138,7 +140,7 @@ xdr_shibrpc_get_assertions_args_1 (XDR *xdrs, shibrpc_get_assertions_args_1 *obj return FALSE; if (!xdr_bool (xdrs, &objp->checkIPAddress)) return FALSE; - if (!xdr_string (xdrs, &objp->url, ~0)) + if (!xdr_string (xdrs, &objp->application_id, ~0)) return FALSE; return TRUE; } diff --git a/shib-target/shibrpc.h b/shib-target/shibrpc.h index 3b1f47c..c60f1b1 100644 --- a/shib-target/shibrpc.h +++ b/shib-target/shibrpc.h @@ -8,9 +8,7 @@ #include -#ifndef WIN32 #include -#endif #ifdef __cplusplus extern "C" { @@ -60,7 +58,7 @@ typedef struct ShibRpcXML ShibRpcXML; struct shibrpc_session_is_valid_args_1 { ShibRpcHttpCookie_1 cookie; - char *url; + char *application_id; bool_t checkIPAddress; long lifetime; long timeout; @@ -73,6 +71,7 @@ struct shibrpc_session_is_valid_ret_1 { typedef struct shibrpc_session_is_valid_ret_1 shibrpc_session_is_valid_ret_1; struct shibrpc_new_session_args_1 { + char *application_id; char *shire_location; char *saml_post; char *client_addr; @@ -89,7 +88,7 @@ typedef struct shibrpc_new_session_ret_1 shibrpc_new_session_ret_1; struct shibrpc_get_assertions_args_1 { ShibRpcHttpCookie_1 cookie; bool_t checkIPAddress; - char *url; + char *application_id; }; typedef struct shibrpc_get_assertions_args_1 shibrpc_get_assertions_args_1; diff --git a/shib-target/shibrpc.x b/shib-target/shibrpc.x index 3348b76..decff43 100644 --- a/shib-target/shibrpc.x +++ b/shib-target/shibrpc.x @@ -65,7 +65,7 @@ struct ShibRpcXML { struct shibrpc_session_is_valid_args_1 { ShibRpcHttpCookie_1 cookie; - string url<>; + string application_id<>; bool checkIPAddress; long lifetime; long timeout; @@ -76,6 +76,7 @@ struct shibrpc_session_is_valid_ret_1 { }; struct shibrpc_new_session_args_1 { + string application_id<>; string shire_location<>; string saml_post<>; string client_addr<>; @@ -91,7 +92,7 @@ struct shibrpc_new_session_ret_1 { struct shibrpc_get_assertions_args_1 { ShibRpcHttpCookie_1 cookie; bool checkIPAddress; - string url<>; + string application_id<>; }; struct shibrpc_get_assertions_ret_1 { diff --git a/shib-target/shibtarget.dsp b/shib-target/shibtarget.dsp index f36c435..f34611f 100644 --- a/shib-target/shibtarget.dsp +++ b/shib-target/shibtarget.dsp @@ -115,10 +115,6 @@ SOURCE=".\shib-mlp.cpp" # End Source File # Begin Source File -SOURCE=".\shib-resource.cpp" -# End Source File -# Begin Source File - SOURCE=".\shib-resourceentry.cpp" # End Source File # Begin Source File @@ -173,5 +169,13 @@ SOURCE=".\shibrpc-xdr.c" SOURCE=.\shibrpc.h # End Source File +# Begin Source File + +SOURCE=.\XML.cpp +# End Source File +# Begin Source File + +SOURCE=.\XMLApplicationMapper.cpp +# End Source File # End Target # End Project diff --git a/shib/AAP.cpp b/shib/AAP.cpp index b7ba035..f91ae77 100644 --- a/shib/AAP.cpp +++ b/shib/AAP.cpp @@ -70,14 +70,12 @@ using namespace std; namespace shibboleth { - class XMLAAPImpl + class XMLAAPImpl : public ReloadableXMLFileImpl { public: XMLAAPImpl(const char* pathname); ~XMLAAPImpl(); - void regAttributes() const; - class AttributeRule : public IAttributeRule { public: @@ -89,18 +87,19 @@ namespace shibboleth { const char* getFactory() const { return m_factory.get(); } const char* getAlias() const { return m_alias.get(); } const char* getHeader() const { return m_header.get(); } - bool accept(const XMLCh* originSite, const DOMElement* e) const; + void apply(const IOriginSite* originSite, SAMLAttribute& attribute) const; enum value_type { literal, regexp, xpath }; private: const XMLCh* m_name; const XMLCh* m_namespace; - auto_ptr m_factory; - auto_ptr m_alias; - auto_ptr m_header; + auto_ptr_char m_factory; + auto_ptr_char m_alias; + auto_ptr_char m_header; value_type toValueType(const DOMElement* e); - bool scopeCheck(const XMLCh* originSite, const DOMElement* e) const; + bool scopeCheck(const IOriginSite* originSite, const DOMElement* e) const; + bool accept(const IOriginSite* originSite, const DOMElement* e) const; struct SiteRule { @@ -128,50 +127,51 @@ namespace shibboleth { typedef map attrmap_t; #endif attrmap_t m_attrMap; - DOMDocument* m_doc; }; - class XMLAAP : public IAAP + class XMLAAP : public IAAP, public ReloadableXMLFile { public: - XMLAAP(const char* pathname); - ~XMLAAP() { delete m_lock; delete m_impl; } + XMLAAP(const char* pathname) : ReloadableXMLFile(pathname) {} + ~XMLAAP() {} - void lock(); - void unlock() { m_lock->unlock(); } const IAttributeRule* lookup(const XMLCh* attrName, const XMLCh* attrNamespace=NULL) const; const IAttributeRule* lookup(const char* alias) const; - saml::Iterator getAttributeRules() const; + Iterator getAttributeRules() const; - private: - std::string m_source; - time_t m_filestamp; - RWLock* m_lock; - XMLAAPImpl* m_impl; + protected: + virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const; }; } extern "C" IAAP* XMLAAPFactory(const char* source) { - return new XMLAAP(source); + XMLAAP* aap=new XMLAAP(source); + try + { + aap->getImplementation(); + } + catch (...) + { + delete aap; + throw; + } + return aap; +} + +ReloadableXMLFileImpl* XMLAAP::newImplementation(const char* pathname) const +{ + return new XMLAAPImpl(pathname); } -XMLAAPImpl::XMLAAPImpl(const char* pathname) : m_doc(NULL) +XMLAAPImpl::XMLAAPImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { NDC ndc("XMLAAPImpl"); Category& log=Category::getInstance(SHIB_LOGCAT".XMLAAPImpl"); - saml::XML::Parser p; try { - static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull}; - URLInputSource src(base,pathname); - Wrapper4InputSource dsrc(&src,false); - m_doc=p.parse(dsrc); - - log.infoStream() << "Loaded and parsed AAP file (" << pathname << ")" << CategoryStream::ENDLINE; - DOMElement* e = m_doc->getDocumentElement(); if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) || XMLString::compareString(SHIB_L(AttributeAcceptancePolicy),e->getLocalName())) @@ -189,12 +189,12 @@ XMLAAPImpl::XMLAAPImpl(const char* pathname) : m_doc(NULL) xstring key=rule->getName(); key=key + chBang + chBang + (rule->getNamespace() ? rule->getNamespace() : Constants::SHIB_ATTRIBUTE_NAMESPACE_URI); #else - auto_ptr aname(XMLString::transcode(rule->getName())); + auto_ptr_char aname(rule->getName()); string key(aname.get()); key+="!!"; if (rule->getNamespace()) { - auto_ptr ans(XMLString::transcode(rule->getNamespace())); + auto_ptr_char ans(rule->getNamespace()); key+=ans.get(); } else @@ -208,7 +208,7 @@ XMLAAPImpl::XMLAAPImpl(const char* pathname) : m_doc(NULL) } catch (SAMLException& e) { - log.errorStream() << "XML error while parsing AAP: " << e.what() << CategoryStream::ENDLINE; + log.errorStream() << "Error while parsing AAP: " << e.what() << CategoryStream::ENDLINE; for (attrmap_t::iterator i=m_attrMap.begin(); i!=m_attrMap.end(); i++) delete i->second; if (m_doc) @@ -230,28 +230,13 @@ XMLAAPImpl::XMLAAPImpl(const char* pathname) : m_doc(NULL) XMLAAPImpl::~XMLAAPImpl() { for (attrmap_t::iterator i=m_attrMap.begin(); i!=m_attrMap.end(); i++) - { - SAMLAttribute::unregFactory(i->second->getName(),i->second->getNamespace()); delete i->second; - } - if (m_doc) - m_doc->release(); -} - -void XMLAAPImpl::regAttributes() const -{ - for (attrmap_t::const_iterator i=m_attrMap.begin(); i!=m_attrMap.end(); i++) - { - SAMLAttributeFactory* f=ShibConfig::getConfig().getAttributeFactory(i->second->getFactory()); - if (f) - SAMLAttribute::regFactory(i->second->getName(),i->second->getNamespace(),f); - } } XMLAAPImpl::AttributeRule::AttributeRule(const DOMElement* e) : - m_factory(e->hasAttributeNS(NULL,SHIB_L(Factory)) ? XMLString::transcode(e->getAttributeNS(NULL,SHIB_L(Factory))) : NULL), - m_alias(e->hasAttributeNS(NULL,SHIB_L(Alias)) ? XMLString::transcode(e->getAttributeNS(NULL,SHIB_L(Alias))) : NULL), - m_header(e->hasAttributeNS(NULL,SHIB_L(Header)) ? XMLString::transcode(e->getAttributeNS(NULL,SHIB_L(Header))) : NULL) + m_factory(e->hasAttributeNS(NULL,SHIB_L(Factory)) ? e->getAttributeNS(NULL,SHIB_L(Factory)) : NULL), + m_alias(e->hasAttributeNS(NULL,SHIB_L(Alias)) ? e->getAttributeNS(NULL,SHIB_L(Alias)) : NULL), + m_header(e->hasAttributeNS(NULL,SHIB_L(Header)) ? e->getAttributeNS(NULL,SHIB_L(Header)) : NULL) { static const XMLCh wTrue[] = {chLatin_t, chLatin_r, chLatin_u, chLatin_e, chNull}; @@ -317,7 +302,7 @@ XMLAAPImpl::AttributeRule::AttributeRule(const DOMElement* e) : m_siteMap[srulename]=SiteRule(); SiteRule& srule=m_siteMap[srulename]; #else - auto_ptr srulename2(XMLString::transcode(srulename)); + auto_ptr_char srulename2(srulename); m_siteMap[srulename2.get()]=SiteRule(); SiteRule& srule=m_siteMap[srulename2.get()]; #endif @@ -370,106 +355,38 @@ XMLAAPImpl::AttributeRule::value_type XMLAAPImpl::AttributeRule::toValueType(con throw MalformedException("Found an invalid value or scope rule type."); } -XMLAAP::XMLAAP(const char* pathname) : m_filestamp(0), m_source(pathname), m_impl(NULL) -{ -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(pathname, &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(pathname, &stat_buf) == 0) -#endif - m_filestamp=stat_buf.st_mtime; - m_impl=new XMLAAPImpl(pathname); - SAMLConfig::getConfig().saml_lock(); - m_impl->regAttributes(); - SAMLConfig::getConfig().saml_unlock(); - m_lock=RWLock::create(); -} - -void XMLAAP::lock() -{ - m_lock->rdlock(); - - // Check if we need to refresh. -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(m_source.c_str(), &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(m_source.c_str(), &stat_buf) == 0) -#endif - { - if (m_filestamp>0 && m_filestampunlock(); - m_lock->wrlock(); - if (m_filestamp>0 && m_filestampregAttributes(); - SAMLConfig::getConfig().saml_unlock(); - m_filestamp=stat_buf.st_mtime; - m_lock->unlock(); - } - catch(SAMLException& e) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLAAP").error("failed to reload AAP, sticking with what we have: %s", e.what()); - } - catch(...) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLAAP").error("caught an unknown exception, sticking with what we have"); - } - } - else - { - m_lock->unlock(); - } - m_lock->rdlock(); - } - } -} - const IAttributeRule* XMLAAP::lookup(const XMLCh* attrName, const XMLCh* attrNamespace) const { #ifdef HAVE_GOOD_STL xstring key=attrName; key=key + chBang + chBang + (attrNamespace ? attrNamespace : Constants::SHIB_ATTRIBUTE_NAMESPACE_URI); #else - auto_ptr aname(XMLString::transcode(attrName)); + auto_ptr_char aname(attrName); string key=aname.get(); key+="!!"; if (attrNamespace) { - auto_ptr ans(XMLString::transcode(attrNamespace)); + auto_ptr_char ans(attrNamespace); key+=ans.get(); } else key+="urn:mace:shibboleth:1.0:attributeNamespace:uri"; #endif - XMLAAPImpl::attrmap_t::const_iterator i=m_impl->m_attrMap.find(key); - return (i==m_impl->m_attrMap.end()) ? NULL : i->second; + XMLAAPImpl* impl=dynamic_cast(getImplementation()); + XMLAAPImpl::attrmap_t::const_iterator i=impl->m_attrMap.find(key); + return (i==impl->m_attrMap.end()) ? NULL : i->second; } const IAttributeRule* XMLAAP::lookup(const char* alias) const { - map::const_iterator i=m_impl->m_aliasMap.find(alias); - return (i==m_impl->m_aliasMap.end()) ? NULL : i->second; + XMLAAPImpl* impl=dynamic_cast(getImplementation()); + map::const_iterator i=impl->m_aliasMap.find(alias); + return (i==impl->m_aliasMap.end()) ? NULL : i->second; } Iterator XMLAAP::getAttributeRules() const { - return m_impl->m_attrs; + return dynamic_cast(getImplementation())->m_attrs; } namespace { @@ -491,7 +408,7 @@ namespace { } } -bool XMLAAPImpl::AttributeRule::scopeCheck(const XMLCh* originSite, const DOMElement* e) const +bool XMLAAPImpl::AttributeRule::scopeCheck(const IOriginSite* originSite, const DOMElement* e) const { // Are we scoped? const XMLCh* scope=e->getAttributeNS(NULL,SHIB_L(Scope)); @@ -513,8 +430,8 @@ bool XMLAAPImpl::AttributeRule::scopeCheck(const XMLCh* originSite, const DOMEle { if (log.isWarnEnabled()) { - auto_ptr temp(XMLString::transcode(m_name)); - auto_ptr temp2(XMLString::transcode(scope)); + auto_ptr_char temp(m_name); + auto_ptr_char temp2(scope); log.warn("attribute %s scope {%s} denied by any-site AAP, rejecting it",temp.get(),temp2.get()); } return false; @@ -524,9 +441,9 @@ bool XMLAAPImpl::AttributeRule::scopeCheck(const XMLCh* originSite, const DOMEle } #ifdef HAVE_GOOD_STL - const XMLCh* os=originSite; + const XMLCh* os=originSite->getName(); #else - auto_ptr pos(XMLString::transcode(originSite)); + auto_ptr_char pos(originSite->getName()); const char* os=pos.get(); #endif sitemap_t::const_iterator srule=m_siteMap.find(os); @@ -540,8 +457,8 @@ bool XMLAAPImpl::AttributeRule::scopeCheck(const XMLCh* originSite, const DOMEle { if (log.isWarnEnabled()) { - auto_ptr temp(XMLString::transcode(m_name)); - auto_ptr temp2(XMLString::transcode(scope)); + auto_ptr_char temp(m_name); + auto_ptr_char temp2(scope); log.warn("attribute %s scope {%s} denied by site AAP, rejecting it",temp.get(),temp2.get()); } return false; @@ -581,9 +498,7 @@ bool XMLAAPImpl::AttributeRule::scopeCheck(const XMLCh* originSite, const DOMEle } // If we still can't decide, defer to site metadata. - OriginMetadata mapper(originSite); - Iterator > domains= - (mapper.fail()) ? Iterator >() : mapper->getSecurityDomains(); + Iterator > domains=originSite->getSecurityDomains(); while (domains.hasNext()) { const pair& p=domains.next(); @@ -596,22 +511,22 @@ bool XMLAAPImpl::AttributeRule::scopeCheck(const XMLCh* originSite, const DOMEle if (log.isWarnEnabled()) { - auto_ptr temp(XMLString::transcode(m_name)); - auto_ptr temp2(XMLString::transcode(scope)); + auto_ptr_char temp(m_name); + auto_ptr_char temp2(scope); log.warn("attribute %s scope {%s} not accepted",temp.get(),temp2.get()); } return false; } -bool XMLAAPImpl::AttributeRule::accept(const XMLCh* originSite, const DOMElement* e) const +bool XMLAAPImpl::AttributeRule::accept(const IOriginSite* originSite, const DOMElement* e) const { NDC ndc("accept"); Category& log=Category::getInstance(SHIB_LOGCAT".XMLAAPImpl"); if (log.isDebugEnabled()) { - auto_ptr temp(XMLString::transcode(m_name)); - auto_ptr temp2(XMLString::transcode(originSite)); + auto_ptr_char temp(m_name); + auto_ptr_char temp2(originSite->getName()); log.debug("evaluating value for attribute %s from site %s",temp.get(),temp2.get()); } @@ -639,9 +554,9 @@ bool XMLAAPImpl::AttributeRule::accept(const XMLCh* originSite, const DOMElement } #ifdef HAVE_GOOD_STL - const XMLCh* os=originSite; + const XMLCh* os=originSite->getName(); #else - auto_ptr pos(XMLString::transcode(originSite)); + auto_ptr_char pos(originSite->getName()); const char* os=pos.get(); #endif sitemap_t::const_iterator srule=m_siteMap.find(os); @@ -649,8 +564,8 @@ bool XMLAAPImpl::AttributeRule::accept(const XMLCh* originSite, const DOMElement { if (log.isWarnEnabled()) { - auto_ptr temp(XMLString::transcode(m_name)); - auto_ptr temp2(XMLString::transcode(originSite)); + auto_ptr_char temp(m_name); + auto_ptr_char temp2(originSite->getName()); log.warn("site %s not found in attribute %s ruleset, any value is rejected",temp2.get(),temp.get()); } return false; @@ -676,10 +591,25 @@ bool XMLAAPImpl::AttributeRule::accept(const XMLCh* originSite, const DOMElement if (log.isWarnEnabled()) { - auto_ptr temp(XMLString::transcode(m_name)); - auto_ptr temp2(XMLString::transcode(n->getNodeValue())); + auto_ptr_char temp(m_name); + auto_ptr_char temp2(n->getNodeValue()); log.warn("%sattribute %s value {%s} could not be validated by AAP, rejecting it", (bSimple ? "" : "complex "),temp.get(),temp2.get()); } return false; } + +void XMLAAPImpl::AttributeRule::apply(const IOriginSite* originSite, SAMLAttribute& attribute) const +{ + // Check each value. + Iterator vals=attribute.getValueElements(); + for (unsigned int i=0; i < vals.size();) { + if (!accept(originSite,vals[i])) + attribute.removeValue(i); + else + i++; + } + + // Now see if we trashed it irrevocably. + attribute.checkValidity(); +} diff --git a/shib/ClubShibPOSTProfile.cpp b/shib/ClubShibPOSTProfile.cpp index 6d3dcfa..d31036f 100644 --- a/shib/ClubShibPOSTProfile.cpp +++ b/shib/ClubShibPOSTProfile.cpp @@ -61,11 +61,15 @@ using namespace shibboleth; using namespace saml; using namespace std; -ClubShibPOSTProfile::ClubShibPOSTProfile(const Iterator& policies, const XMLCh* receiver, int ttlSeconds) - : ShibPOSTProfile(policies,receiver,ttlSeconds) {} +ClubShibPOSTProfile::ClubShibPOSTProfile( + const Iterator& metadatas, const Iterator& trusts, + const Iterator& policies, const XMLCh* receiver, int ttlSeconds + ) : ShibPOSTProfile(metadatas, trusts, policies,receiver,ttlSeconds) {} -ClubShibPOSTProfile::ClubShibPOSTProfile(const Iterator& policies, const XMLCh* issuer) - : ShibPOSTProfile(policies,issuer) {} +ClubShibPOSTProfile::ClubShibPOSTProfile( + const Iterator& metadatas, const Iterator& creds, + const Iterator& policies, const XMLCh* issuer + ) : ShibPOSTProfile(metadatas,creds,policies,issuer) {} ClubShibPOSTProfile::~ClubShibPOSTProfile() {} diff --git a/shib/Makefile.am b/shib/Makefile.am index 18a8e2e..3224917 100644 --- a/shib/Makefile.am +++ b/shib/Makefile.am @@ -14,6 +14,7 @@ libshib_la_SOURCES = \ Constants.cpp \ CredResolvers.cpp \ Metadata.cpp \ + ReloadableXMLFile.cpp \ SAMLBindingFactory.cpp \ ShibConfig.cpp \ ShibPOSTProfile.cpp \ diff --git a/shib/Metadata.cpp b/shib/Metadata.cpp index d3cab8b..0b6791c 100644 --- a/shib/Metadata.cpp +++ b/shib/Metadata.cpp @@ -56,17 +56,18 @@ */ #include "internal.h" +#include using namespace shibboleth; using namespace saml; using namespace std; -OriginMetadata::OriginMetadata(const XMLCh* site) : m_mapper(NULL), m_site(NULL) +OriginMetadata::OriginMetadata(const Iterator& metadatas, const XMLCh* site) : m_mapper(NULL), m_site(NULL) { - Iterator it=ShibConfig::getConfig().getMetadataProviders(); - while (it.hasNext()) + metadatas.reset(); + while (metadatas.hasNext()) { - IMetadata* i=it.next(); + IMetadata* i=metadatas.next(); i->lock(); if (m_site=dynamic_cast(i->lookup(site))) { @@ -91,10 +92,10 @@ Iterator Trust::getCertificates(const XMLCh* subject) m_mapper=NULL; } - Iterator it=ShibConfig::getConfig().getTrustProviders(); - while (it.hasNext()) + m_trusts.reset(); + while (m_trusts.hasNext()) { - ITrust* i=it.next(); + ITrust* i=m_trusts.next(); i->lock(); Iterator iter=i->getCertificates(subject); if (iter.size()) @@ -110,10 +111,10 @@ Iterator Trust::getCertificates(const XMLCh* subject) bool Trust::validate(const ISite* site, Iterator certs) const { bool ret=false; - Iterator it=ShibConfig::getConfig().getTrustProviders(); - while (!ret && it.hasNext()) + m_trusts.reset(); + while (!ret && m_trusts.hasNext()) { - ITrust* i=it.next(); + ITrust* i=m_trusts.next(); i->lock(); ret=i->validate(site,certs); i->unlock(); @@ -124,10 +125,10 @@ bool Trust::validate(const ISite* site, Iterator certs) const bool Trust::validate(const ISite* site, Iterator certs) const { bool ret=false; - Iterator it=ShibConfig::getConfig().getTrustProviders(); - while (!ret && it.hasNext()) + m_trusts.reset(); + while (!ret && m_trusts.hasNext()) { - ITrust* i=it.next(); + ITrust* i=m_trusts.next(); i->lock(); ret=i->validate(site,certs); i->unlock(); @@ -138,10 +139,10 @@ bool Trust::validate(const ISite* site, Iterator certs) const bool Trust::attach(const ISite* site, SSL_CTX* ctx) const { bool ret=false; - Iterator it=ShibConfig::getConfig().getTrustProviders(); - while (!ret && it.hasNext()) + m_trusts.reset(); + while (!ret && m_trusts.hasNext()) { - ITrust* i=it.next(); + ITrust* i=m_trusts.next(); i->lock(); ret=i->attach(site,ctx); i->unlock(); @@ -155,13 +156,13 @@ Trust::~Trust() m_mapper->unlock(); } -bool Credentials::attach(const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx) +bool Credentials::attach(const saml::Iterator& creds, const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx) { bool ret=false; - Iterator it=ShibConfig::getConfig().getCredentialProviders(); - while (!ret && it.hasNext()) + creds.reset(); + while (!ret && creds.hasNext()) { - ICredentials* i=it.next(); + ICredentials* i=creds.next(); i->lock(); ret=i->attach(subject,relyingParty,ctx); i->unlock(); @@ -170,12 +171,12 @@ bool Credentials::attach(const XMLCh* subject, const ISite* relyingParty, SSL_CT return ret; } -AAP::AAP(const XMLCh* attrName, const XMLCh* attrNamespace) : m_mapper(NULL), m_rule(NULL) +AAP::AAP(const saml::Iterator& aaps, const XMLCh* attrName, const XMLCh* attrNamespace) : m_mapper(NULL), m_rule(NULL) { - Iterator it=ShibConfig::getConfig().getAAPProviders(); - while (it.hasNext()) + aaps.reset(); + while (aaps.hasNext()) { - IAAP* i=it.next(); + IAAP* i=aaps.next(); i->lock(); if (m_rule=i->lookup(attrName,attrNamespace)) { @@ -186,12 +187,12 @@ AAP::AAP(const XMLCh* attrName, const XMLCh* attrNamespace) : m_mapper(NULL), m_ } } -AAP::AAP(const char* alias) : m_mapper(NULL), m_rule(NULL) +AAP::AAP(const saml::Iterator& aaps, const char* alias) : m_mapper(NULL), m_rule(NULL) { - Iterator it=ShibConfig::getConfig().getAAPProviders(); - while (it.hasNext()) + aaps.reset(); + while (aaps.hasNext()) { - IAAP* i=it.next(); + IAAP* i=aaps.next(); i->lock(); if (m_rule=i->lookup(alias)) { @@ -207,3 +208,56 @@ AAP::~AAP() if (m_mapper) m_mapper->unlock(); } + +void AAP::apply(const saml::Iterator& aaps, const IOriginSite* originSite, saml::SAMLAssertion& assertion) +{ + saml::NDC("apply"); + log4cpp::Category& log=log4cpp::Category::getInstance(SHIB_LOGCAT".AAP"); + + // Check each statement. + Iterator statements=assertion.getStatements(); + for (unsigned int scount=0; scount < statements.size();) { + SAMLAttributeStatement* s=dynamic_cast(statements[scount]); + if (!s) + continue; + + // Check each attribute. + Iterator attrs=s->getAttributes(); + for (unsigned int acount=0; acount < attrs.size();) { + SAMLAttribute* a=attrs[acount]; + + AAP rule(aaps,a->getName(),a->getNamespace()); + if (rule.fail()) { + if (log.isWarnEnabled()) { + auto_ptr_char temp(a->getName()); + log.warn("no rule found for attribute (%s), filtering it out",temp.get()); + } + s->removeAttribute(acount); + continue; + } + + try { + rule->apply(originSite,*a); + acount++; + } + catch (SAMLException&) { + // The attribute is now defunct. + log.info("no values remain, removing attribute"); + s->removeAttribute(acount); + } + } + + try { + s->checkValidity(); + scount++; + } + catch (SAMLException&) { + // The statement is now defunct. + log.info("no attributes remain, removing statement"); + assertion.removeStatement(scount); + } + } + + // Now see if we trashed it irrevocably. + assertion.checkValidity(); +} diff --git a/shib/ReloadableXMLFile.cpp b/shib/ReloadableXMLFile.cpp new file mode 100644 index 0000000..f054ff0 --- /dev/null +++ b/shib/ReloadableXMLFile.cpp @@ -0,0 +1,176 @@ +/* + * The Shibboleth License, Version 1. + * Copyright (c) 2002 + * University Corporation for Advanced Internet Development, Inc. + * All rights reserved + * + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution, if any, must include + * the following acknowledgment: "This product includes software developed by + * the University Corporation for Advanced Internet Development + * Internet2 Project. Alternately, this acknowledegement + * may appear in the software itself, if and wherever such third-party + * acknowledgments normally appear. + * + * Neither the name of Shibboleth nor the names of its contributors, nor + * Internet2, nor the University Corporation for Advanced Internet Development, + * Inc., nor UCAID may be used to endorse or promote products derived from this + * software without specific prior written permission. For written permission, + * please contact shibboleth@shibboleth.org + * + * Products derived from this software may not be called Shibboleth, Internet2, + * UCAID, or the University Corporation for Advanced Internet Development, nor + * may Shibboleth appear in their name, without prior written permission of the + * University Corporation for Advanced Internet Development. + * + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A + * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK + * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. + * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY + * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* ReloadableXMLFile.cpp - basic implementation of a reloadable XML config file + + Scott Cantor + 1/6/04 + + $History:$ +*/ + +#include "internal.h" + +#include +#include + +#include +#include + +using namespace shibboleth; +using namespace saml; +using namespace log4cpp; +using namespace std; + +ReloadableXMLFileImpl::ReloadableXMLFileImpl(const char* pathname) : m_doc(NULL) +{ + NDC ndc("ReloadableXMLFileImpl"); + Category& log=Category::getInstance(SHIB_LOGCAT".ReloadableXMLFileImpl"); + + saml::XML::Parser p; + try + { + static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull}; + URLInputSource src(base,pathname); + Wrapper4InputSource dsrc(&src,false); + m_doc=p.parse(dsrc); + + log.infoStream() << "Loaded and parsed XML file (" << pathname << ")" << CategoryStream::ENDLINE; + } + catch (SAMLException& e) + { + log.errorStream() << "XML error while parsing configuration file: " << e.what() << CategoryStream::ENDLINE; + if (m_doc) + m_doc->release(); + throw; + } + catch (...) + { + log.error("Unexpected error while parsing configuration file"); + if (m_doc) + m_doc->release(); + throw; + } +} + +ReloadableXMLFileImpl::~ReloadableXMLFileImpl() +{ + if (m_doc) + m_doc->release(); +} + +ReloadableXMLFile::ReloadableXMLFile(const char* pathname) : m_filestamp(0), m_source(pathname), m_impl(NULL), m_lock(NULL) +{ +#ifdef WIN32 + struct _stat stat_buf; + if (_stat(pathname, &stat_buf) == 0) +#else + struct stat stat_buf; + if (stat(pathname, &stat_buf) == 0) +#endif + m_filestamp=stat_buf.st_mtime; + m_lock=RWLock::create(); +} + +void ReloadableXMLFile::lock() +{ + m_lock->rdlock(); + + // Check if we need to refresh. +#ifdef WIN32 + struct _stat stat_buf; + if (_stat(m_source.c_str(), &stat_buf) == 0) +#else + struct stat stat_buf; + if (stat(m_source.c_str(), &stat_buf) == 0) +#endif + { + if (m_filestamp>0 && m_filestampunlock(); + m_lock->wrlock(); + if (m_filestamp>0 && m_filestampunlock(); + } + catch(SAMLException& e) + { + m_lock->unlock(); + saml::NDC ndc("lock"); + Category::getInstance(SHIB_LOGCAT".ReloadableXMLFile").error("failed to reload config file, sticking with what we have: %s", e.what()); + } + catch(...) + { + m_lock->unlock(); + saml::NDC ndc("lock"); + Category::getInstance(SHIB_LOGCAT".ReloadableXMLFile").error("caught an unknown exception, sticking with what we have"); + } + } + else + { + m_lock->unlock(); + } + m_lock->rdlock(); + } + } +} + +ReloadableXMLFileImpl* ReloadableXMLFile::getImplementation() const +{ + if (!m_impl) + m_impl=newImplementation(m_source.c_str()); + return m_impl; +} diff --git a/shib/SAMLBindingFactory.cpp b/shib/SAMLBindingFactory.cpp index 12aff70..74a24c9 100644 --- a/shib/SAMLBindingFactory.cpp +++ b/shib/SAMLBindingFactory.cpp @@ -61,10 +61,15 @@ using namespace shibboleth; using namespace saml; -SAMLBinding* SAMLBindingFactory::getInstance(const XMLCh* subject, const ISite* relyingParty, const XMLCh* protocol) +SAMLBinding* SAMLBindingFactory::getInstance( + const saml::Iterator& metadatas, + const saml::Iterator& trusts, + const saml::Iterator& creds, + const XMLCh* subject, const ISite* relyingParty, const XMLCh* protocol + ) { if (!protocol || XMLString::compareString(protocol,SAMLBinding::SAML_SOAP_HTTPS)) throw UnsupportedProtocolException("SAMLBindingFactory::getInstance() unable to find binding implementation for specified protocol"); - return new ShibSOAPBinding(subject, relyingParty); + return new ShibSOAPBinding(metadatas, trusts, creds, subject, relyingParty); } diff --git a/shib/ScopedAttribute.cpp b/shib/ScopedAttribute.cpp index 272f918..d9891f1 100644 --- a/shib/ScopedAttribute.cpp +++ b/shib/ScopedAttribute.cpp @@ -65,11 +65,16 @@ using namespace saml; using namespace log4cpp; using namespace std; +void ScopedAttribute::valueToDOM(unsigned int index, DOMElement* e) const +{ + SAMLAttribute::valueToDOM(index,e); + e->setAttributeNS(NULL,SHIB_L(Scope),m_scopes[index]); +} ScopedAttribute::ScopedAttribute(const XMLCh* name, const XMLCh* ns, long lifetime, const saml::Iterator& scopes, const saml::Iterator& values) - : SimpleAttribute(name,ns,lifetime,values) + : SAMLAttribute(name,ns,NULL,lifetime,values) { if (scopes.size()!=values.size()) throw MalformedException(SAMLException::RESPONDER,"ScopedAttribute() requires the number of scopes to equal the number of values"); @@ -78,7 +83,23 @@ ScopedAttribute::ScopedAttribute(const XMLCh* name, const XMLCh* ns, long lifeti m_scopes.push_back(XMLString::replicate(scopes.next())); } -ScopedAttribute::ScopedAttribute(DOMElement* e) : SimpleAttribute(e) {} +ScopedAttribute::ScopedAttribute(DOMElement* e) : SAMLAttribute(e) +{ + // Default scope comes from subject. + DOMNodeList* nlist= + static_cast(e->getParentNode())->getElementsByTagNameNS(saml::XML::SAML_NS,L(NameIdentifier)); + if (!nlist || nlist->getLength() != 1) + throw MalformedException(SAMLException::RESPONDER,"ScopedAttribute() can't find saml:NameIdentifier in enclosing statement"); + m_originSite=static_cast(nlist->item(0))->getAttributeNS(NULL,L(NameQualifier)); + + e=saml::XML::getFirstChildElement(e,saml::XML::SAML_NS,L(AttributeValue)); + while (e) + { + DOMAttr* scope=e->getAttributeNodeNS(NULL,SHIB_L(Scope)); + m_scopes.push_back(scope ? scope->getNodeValue() : &chNull); + e=saml::XML::getNextSiblingElement(e,saml::XML::SAML_NS,L(AttributeValue)); + } +} ScopedAttribute::~ScopedAttribute() { @@ -99,17 +120,6 @@ ScopedAttribute::~ScopedAttribute() } } -bool ScopedAttribute::addValue(DOMElement* e) -{ - if (SAMLAttribute::addValue(e)) - { - DOMAttr* scope=e->getAttributeNodeNS(NULL,SHIB_L(Scope)); - m_scopes.push_back(scope ? scope->getNodeValue() : &chNull); - return true; - } - return false; -} - Iterator ScopedAttribute::getValues() const { static XMLCh at[]={chAt, chNull}; @@ -146,27 +156,28 @@ Iterator ScopedAttribute::getSingleByteValues() const return Iterator(m_sbValues); } -SAMLObject* ScopedAttribute::clone() const +void ScopedAttribute::setValues(const Iterator& values) { - return new ScopedAttribute(m_name,m_namespace,m_lifetime,m_scopes,m_values); + throw SAMLException("unsupported operation"); } -DOMNode* ScopedAttribute::toDOM(DOMDocument* doc,bool xmlns) const +void ScopedAttribute::addValue(const XMLCh* value) { - SimpleAttribute::toDOM(doc,xmlns); + throw SAMLException("unsupported operation"); +} - int i=0; - DOMNode* n=m_root->getFirstChild(); - while (n) - { - if (n->getNodeType()==DOMNode::ELEMENT_NODE) - { - static_cast(n)->setAttributeNS(NULL,SHIB_L(Scope),m_scopes[i]); - i++; - } - n=n->getNextSibling(); +void ScopedAttribute::removeValue(unsigned int index) +{ + SAMLAttribute::removeValue(index); + + if (m_bOwnStrings) { + XMLCh* p=const_cast(m_scopes[index]); + XMLString::release(&p); } - - return m_root; + m_scopes.erase(m_scopes.begin()+index); } +SAMLObject* ScopedAttribute::clone() const +{ + return new ScopedAttribute(m_name,m_namespace,m_lifetime,m_scopes,m_values); +} diff --git a/shib/ShibConfig.cpp b/shib/ShibConfig.cpp index 552eaa6..0ad1415 100644 --- a/shib/ShibConfig.cpp +++ b/shib/ShibConfig.cpp @@ -95,7 +95,7 @@ extern "C" SAMLAttribute* ShibAttributeFactory(DOMElement* e) n=n->getNextSibling(); if (n && static_cast(n)->hasAttributeNS(NULL,SHIB_L(Scope))) return new ScopedAttribute(e); - return new SimpleAttribute(e); + return new SAMLAttribute(e); } @@ -109,6 +109,8 @@ bool ShibInternalConfig::init() // Register extension schema. saml::XML::registerSchema(XML::SHIB_NS,XML::SHIB_SCHEMA_ID); + SAMLAttribute::setFactory(&ShibAttributeFactory); + // Register metadata factories (some duplicates for backward-compatibility) regFactory("edu.internet2.middleware.shibboleth.metadata.XML",&XMLMetadataFactory); regFactory("edu.internet2.middleware.shibboleth.metadata.provider.XML",&XMLMetadataFactory); @@ -119,23 +121,10 @@ bool ShibInternalConfig::init() regFactory("edu.internet2.middleware.shibboleth.creds.provider.KeyInfoResolver",&KeyInfoResolverFactory); regFactory("edu.internet2.middleware.shibboleth.target.AAP.XML",&XMLAAPFactory); regFactory("edu.internet2.middleware.shibboleth.target.AAP.provider.XML",&XMLAAPFactory); - regFactory("edu.internet2.middleware.shibboleth.target.AttributeFactory",&ShibAttributeFactory); return true; } -void ShibInternalConfig::term() -{ - for (vector::iterator i=m_providers.begin(); i!=m_providers.end(); i++) - delete *i; - for (vector::iterator j=m_trust_providers.begin(); j!=m_trust_providers.end(); j++) - delete *j; - for (vector::iterator k=m_cred_providers.begin(); k!=m_cred_providers.end(); k++) - delete *k; - for (vector::iterator l=m_aap_providers.begin(); l!=m_aap_providers.end(); l++) - delete *l; -} - void ShibInternalConfig::regFactory(const char* type, MetadataFactory* factory) { if (type && factory) @@ -169,12 +158,6 @@ void ShibInternalConfig::regFactory(const char* type, AAPFactory* factory) m_aapFactoryMap[type]=factory; } -void ShibInternalConfig::regFactory(const char* type, SAMLAttributeFactory* factory) -{ - if (type && factory) - m_attrFactoryMap[type]=factory; -} - void ShibInternalConfig::unregFactory(const char* type) { if (type) @@ -187,87 +170,64 @@ void ShibInternalConfig::unregFactory(const char* type) } } -SAMLAttributeFactory* ShibInternalConfig::getAttributeFactory(const char* type) const +IMetadata* ShibInternalConfig::newMetadata(const char* type, const char* source) const { - AttributeFactoryMap::const_iterator i = - m_attrFactoryMap.find((type && *type) ? type : "edu.internet2.middleware.shibboleth.target.AttributeFactory"); - if (i==m_attrFactoryMap.end()) + MetadataFactoryMap::const_iterator i=m_metadataFactoryMap.find(type); + if (i==m_metadataFactoryMap.end()) { - NDC ndc("getAttributeFactory"); - Category::getInstance(SHIB_LOGCAT".ShibInternalConfig").error("unknown attribute factory: %s",type); + NDC ndc("newMetadata"); + Category::getInstance(SHIB_LOGCAT".ShibInternalConfig").error("unknown metadata type: %s",type); return NULL; } - return i->second; + return i->second(source); } -CredResolverFactory* ShibInternalConfig::getCredResolverFactory(const char* type) const +ITrust* ShibInternalConfig::newTrust(const char* type, const char* source) const { - CredResolverFactoryMap::const_iterator i=m_credResolverFactoryMap.find(type); - if (i==m_credResolverFactoryMap.end()) + TrustFactoryMap::const_iterator i=m_trustFactoryMap.find(type); + if (i==m_trustFactoryMap.end()) { - NDC ndc("getCredResolverFactory"); - Category::getInstance(SHIB_LOGCAT".ShibInternalConfig").error("unknown cred resolver factory: %s",type); + NDC ndc("newTrust"); + Category::getInstance(SHIB_LOGCAT".ShibInternalConfig").error("unknown trust type: %s",type); return NULL; } - return i->second; + return i->second(source); } -bool ShibInternalConfig::addMetadata(const char* type, const char* source) +ICredentials* ShibInternalConfig::newCredentials(const char* type, const char* source) const { - saml::NDC ndc("addMetadata"); - - bool ret=false; - try + CredentialsFactoryMap::const_iterator i=m_credFactoryMap.find(type); + if (i==m_credFactoryMap.end()) { - MetadataFactoryMap::const_iterator i=m_metadataFactoryMap.find(type); - if (i!=m_metadataFactoryMap.end()) - { - m_providers.push_back((i->second)(source)); - ret=true; - } - else - { - TrustFactoryMap::const_iterator j=m_trustFactoryMap.find(type); - if (j!=m_trustFactoryMap.end()) - { - m_trust_providers.push_back((j->second)(source)); - ret=true; - } - else - { - CredentialsFactoryMap::const_iterator k=m_credFactoryMap.find(type); - if (k!=m_credFactoryMap.end()) - { - m_cred_providers.push_back((k->second)(source)); - ret=true; - } - else - { - AAPFactoryMap::const_iterator l=m_aapFactoryMap.find(type); - if (l!=m_aapFactoryMap.end()) - { - m_aap_providers.push_back((l->second)(source)); - ret=true; - } - else - throw MetadataException("ShibConfig::addMetadata() unable to locate a metadata factory of the requested type"); - } - } - } + NDC ndc("newCredentials"); + Category::getInstance(SHIB_LOGCAT".ShibInternalConfig").error("unknown credentials type: %s",type); + return NULL; } - catch (SAMLException& e) + return i->second(source); +} + +IAAP* ShibInternalConfig::newAAP(const char* type, const char* source) const +{ + AAPFactoryMap::const_iterator i=m_aapFactoryMap.find(type); + if (i==m_aapFactoryMap.end()) { - Category::getInstance(SHIB_LOGCAT".ShibConfig").error( - "failed to add %s provider to system using source '%s': %s", type, source, e.what() - ); + NDC ndc("newAAP"); + Category::getInstance(SHIB_LOGCAT".ShibInternalConfig").error("unknown AAP type: %s",type); + return NULL; } - catch (...) + return i->second(source); +} + +ICredResolver* ShibInternalConfig::newCredResolver(const char* type, const DOMElement* source) const +{ + CredResolverFactoryMap::const_iterator i=m_credResolverFactoryMap.find(type); + if (i==m_credResolverFactoryMap.end()) { - Category::getInstance(SHIB_LOGCAT".ShibConfig").error( - "failed to add %s provider to system using source '%s': unknown exception", type, source - ); + NDC ndc("newCredResolver"); + Category::getInstance(SHIB_LOGCAT".ShibInternalConfig").error("unknown cred resolver type: %s",type); + return NULL; } - return ret; + return i->second(source); } ShibConfig& ShibConfig::getConfig() diff --git a/shib/ShibPOSTProfile.cpp b/shib/ShibPOSTProfile.cpp index 5583ce6..432091e 100644 --- a/shib/ShibPOSTProfile.cpp +++ b/shib/ShibPOSTProfile.cpp @@ -65,42 +65,34 @@ using namespace shibboleth; using namespace saml; using namespace std; -ShibPOSTProfile::ShibPOSTProfile(const Iterator& policies, const XMLCh* receiver, int ttlSeconds) - : m_ttlSeconds(ttlSeconds), m_algorithm(SIGNATURE_RSA), m_issuer(NULL) +static Iterator emptyTrusts; +static Iterator emptyCreds; + +ShibPOSTProfile::ShibPOSTProfile( + const Iterator& metadatas, const Iterator& trusts, + const Iterator& policies, const XMLCh* receiver, int ttlSeconds + ) + : m_ttlSeconds(ttlSeconds), m_algorithm(SIGNATURE_RSA), m_issuer(NULL), m_receiver(receiver), + m_metadatas(metadatas), m_trusts(trusts), m_policies(policies), m_creds(emptyCreds) { if (!receiver || !*receiver || ttlSeconds <= 0) throw SAMLException(SAMLException::REQUESTER, "ShibPOSTProfile() found a null or invalid argument"); - - m_receiver = XMLString::replicate(receiver); - - while (policies.hasNext()) - m_policies.push_back(XMLString::replicate(policies.next())); } -ShibPOSTProfile::ShibPOSTProfile(const Iterator& policies, const XMLCh* issuer) - : m_ttlSeconds(0), m_algorithm(SIGNATURE_RSA), m_receiver(NULL) +ShibPOSTProfile::ShibPOSTProfile( + const saml::Iterator& metadatas, const saml::Iterator& creds, + const Iterator& policies, const XMLCh* issuer + ) + : m_ttlSeconds(0), m_algorithm(SIGNATURE_RSA), m_receiver(NULL), m_issuer(issuer), + m_policies(policies), m_metadatas(metadatas), m_creds(creds), m_trusts(emptyTrusts) { if (!issuer || !*issuer) throw SAMLException(SAMLException::REQUESTER, "ShibPOSTProfile() found a null or invalid argument"); - - m_issuer = XMLString::replicate(issuer); - - while (policies.hasNext()) - m_policies.push_back(XMLString::replicate(policies.next())); -} - -ShibPOSTProfile::~ShibPOSTProfile() -{ - delete[] m_issuer; - delete[] m_receiver; - - for (vector::iterator i=m_policies.begin(); i!=m_policies.end(); i++) - delete[] const_cast(*i); } const SAMLAssertion* ShibPOSTProfile::getSSOAssertion(const SAMLResponse& r) { - return SAMLPOSTProfile::getSSOAssertion(r,Iterator(m_policies)); + return SAMLPOSTProfile::getSSOAssertion(r,m_policies); } const SAMLAuthenticationStatement* ShibPOSTProfile::getSSOStatement(const SAMLAssertion& a) @@ -168,7 +160,7 @@ SAMLResponse* ShibPOSTProfile::accept(const XMLByte* buf, XMLCh** originSitePtr) // Is this a trusted HS? const IAuthority* hs=NULL; - OriginMetadata mapper(originSite); + OriginMetadata mapper(m_metadatas,originSite); Iterator hsi=mapper.fail() ? Iterator() : mapper->getHandleServices(); bool bFound = false; while (!bFound && hsi.hasNext()) @@ -180,7 +172,7 @@ SAMLResponse* ShibPOSTProfile::accept(const XMLByte* buf, XMLCh** originSitePtr) if (!bFound) throw TrustException(SAMLException::RESPONDER, "ShibPOSTProfile::accept() detected an untrusted HS for the origin site"); - Trust t; + Trust t(m_trusts); Iterator certs=t.getCertificates(hs->getName()); Iterator certs2=t.getCertificates(originSite); @@ -300,13 +292,13 @@ void ShibPOSTProfile::verifySignature( certs.push_back(obj.getX509Certificate(i)); // Compare the name in the end entity certificate to the signer's name. - auto_ptr temp(XMLString::transcode(certs[0])); + auto_ptr_char temp(certs[0]); X509* x=B64_to_X509(temp.get()); if (!x) throw TrustException("ShibPOSTProfile::verifySignature() unable to decode X.509 signing certificate"); bool match=false; - auto_ptr sn(XMLString::transcode(signerName)); + auto_ptr_char sn(signerName); char data[256]; X509_NAME* subj; @@ -357,7 +349,7 @@ void ShibPOSTProfile::verifySignature( throw TrustException("ShibPOSTProfile::verifySignature() cannot match CN or subjectAltName against signer"); // Ask the site to determine the trustworthiness of the certificate. - if (!originSite->validate(certs)) + if (!originSite->validate(m_trusts,certs)) throw TrustException("ShibPOSTProfile::verifySignature() cannot validate the provided signing certificate(s)"); } } diff --git a/shib/ShibPOSTProfileFactory.cpp b/shib/ShibPOSTProfileFactory.cpp index 79661ed..1be5019 100644 --- a/shib/ShibPOSTProfileFactory.cpp +++ b/shib/ShibPOSTProfileFactory.cpp @@ -61,12 +61,18 @@ using namespace shibboleth; using namespace saml; using namespace std; -ShibPOSTProfile* ShibPOSTProfileFactory::getInstance(const Iterator& policies, const XMLCh* receiver, int ttlSeconds) +ShibPOSTProfile* ShibPOSTProfileFactory::getInstance( + const Iterator& metadatas, const Iterator& trusts, + const Iterator& policies, const XMLCh* receiver, int ttlSeconds + ) { - return new ClubShibPOSTProfile(policies,receiver,ttlSeconds); + return new ClubShibPOSTProfile(metadatas,trusts,policies,receiver,ttlSeconds); } -ShibPOSTProfile* ShibPOSTProfileFactory::getInstance(const Iterator& policies, const XMLCh* issuer) +ShibPOSTProfile* ShibPOSTProfileFactory::getInstance( + const Iterator& metadatas, const Iterator& creds, + const Iterator& policies, const XMLCh* issuer + ) { - return new ClubShibPOSTProfile(policies,issuer); + return new ClubShibPOSTProfile(metadatas,creds,policies,issuer); } diff --git a/shib/ShibSOAPBinding.cpp b/shib/ShibSOAPBinding.cpp index 72cb63b..cde6083 100644 --- a/shib/ShibSOAPBinding.cpp +++ b/shib/ShibSOAPBinding.cpp @@ -78,13 +78,13 @@ bool shibboleth::ssl_ctx_callback(void* ssl_ctx, void* userptr) try { ShibSOAPBinding* b = reinterpret_cast(userptr); - if (!Credentials::attach(b->m_subject, b->m_relyingParty, reinterpret_cast(ssl_ctx))) + if (!Credentials::attach(b->m_creds, b->m_subject, b->m_relyingParty, reinterpret_cast(ssl_ctx))) { NDC("ssl_ctx_callback"); Category::getInstance(SHIB_LOGCAT".ShibSOAPBinding").warn("found no appropriate credentials to attach, request will be anonymous"); } - Trust t; + Trust t(b->m_trusts); if (!t.attach(b->m_relyingParty, reinterpret_cast(ssl_ctx))) { NDC("ssl_ctx_callback"); diff --git a/shib/SimpleAttribute.cpp b/shib/SimpleAttribute.cpp deleted file mode 100644 index 1ecbe91..0000000 --- a/shib/SimpleAttribute.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* - * The Shibboleth License, Version 1. - * Copyright (c) 2002 - * University Corporation for Advanced Internet Development, Inc. - * All rights reserved - * - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution, if any, must include - * the following acknowledgment: "This product includes software developed by - * the University Corporation for Advanced Internet Development - * Internet2 Project. Alternately, this acknowledegement - * may appear in the software itself, if and wherever such third-party - * acknowledgments normally appear. - * - * Neither the name of Shibboleth nor the names of its contributors, nor - * Internet2, nor the University Corporation for Advanced Internet Development, - * Inc., nor UCAID may be used to endorse or promote products derived from this - * software without specific prior written permission. For written permission, - * please contact shibboleth@shibboleth.org - * - * Products derived from this software may not be called Shibboleth, Internet2, - * UCAID, or the University Corporation for Advanced Internet Development, nor - * may Shibboleth appear in their name, without prior written permission of the - * University Corporation for Advanced Internet Development. - * - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A - * PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE DISCLAIMED AND THE ENTIRE RISK - * OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. - * IN NO EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY - * CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC. BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -/* SimpleAttribute.cpp - simple attribute implementation with AAP-support - - Scott Cantor - 12/19/02 - - $History:$ -*/ - -#include "internal.h" - -using namespace shibboleth; - -SimpleAttribute::SimpleAttribute(const XMLCh* name, const XMLCh* ns, long lifetime, const saml::Iterator& values) - : SAMLAttribute(name,ns,&Constants::SHIB_ATTRIBUTE_VALUE_TYPE,lifetime,values), m_originSite(NULL) {} - -SimpleAttribute::SimpleAttribute(DOMElement* e) : SAMLAttribute(e) -{ - // Default scope comes from subject. - DOMNodeList* nlist= - static_cast(e->getParentNode())->getElementsByTagNameNS(saml::XML::SAML_NS,L(NameIdentifier)); - if (!nlist || nlist->getLength() != 1) - throw saml::MalformedException(saml::SAMLException::RESPONDER,"SimpleAttribute() can't find saml:NameIdentifier in enclosing statement"); - m_originSite=static_cast(nlist->item(0))->getAttributeNS(NULL,L(NameQualifier)); -} - -SimpleAttribute::~SimpleAttribute() {} - -saml::SAMLObject* SimpleAttribute::clone() const -{ - SimpleAttribute* dest=new SimpleAttribute(m_name,m_namespace,m_lifetime); - dest->m_values.assign(m_values.begin(),m_values.end()); - return dest; -} - -bool SimpleAttribute::accept(DOMElement* e) const -{ - AAP aap(m_name,m_namespace); - return aap.fail() ? false : aap->accept(m_originSite,e); -} diff --git a/shib/XMLCredentials.cpp b/shib/XMLCredentials.cpp index e9a0cd8..e539463 100644 --- a/shib/XMLCredentials.cpp +++ b/shib/XMLCredentials.cpp @@ -72,7 +72,7 @@ using namespace std; namespace shibboleth { - class XMLCredentialsImpl + class XMLCredentialsImpl : public ReloadableXMLFileImpl { public: XMLCredentialsImpl(const char* pathname); @@ -93,31 +93,40 @@ namespace shibboleth { vector m_keyuses; typedef multimap,KeyUse*> BindingMap; BindingMap m_bindings; - - DOMDocument* m_doc; }; - class XMLCredentials : public ICredentials + class XMLCredentials : public ICredentials, public ReloadableXMLFile { public: - XMLCredentials(const char* pathname); - ~XMLCredentials() { delete m_lock; delete m_impl; } + XMLCredentials(const char* pathname) : ReloadableXMLFile(pathname) {} + ~XMLCredentials() {} + bool attach(const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx) const; - private: - void lock(); - void unlock() { m_lock->unlock(); } - std::string m_source; - time_t m_filestamp; - RWLock* m_lock; - XMLCredentialsImpl* m_impl; + protected: + virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const; }; } extern "C" ICredentials* XMLCredentialsFactory(const char* source) { - return new XMLCredentials(source); + XMLCredentials* creds=new XMLCredentials(source); + try + { + creds->getImplementation(); + } + catch (...) + { + delete creds; + throw; + } + return creds; +} + +ReloadableXMLFileImpl* XMLCredentials::newImplementation(const char* pathname) const +{ + return new XMLCredentialsImpl(pathname); } XMLCredentialsImpl::KeyUse::KeyUse(resolvermap_t& resolverMap, const XMLCh* keyref, const XMLCh* certref) : m_key(NULL), m_cert(NULL) @@ -138,21 +147,13 @@ XMLCredentialsImpl::KeyUse::KeyUse(resolvermap_t& resolverMap, const XMLCh* keyr } } -XMLCredentialsImpl::XMLCredentialsImpl(const char* pathname) : m_doc(NULL) +XMLCredentialsImpl::XMLCredentialsImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { NDC ndc("XMLCredentialsImpl"); Category& log=Category::getInstance(SHIB_LOGCAT".XMLCredentialsImpl"); - saml::XML::Parser p; try { - static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull}; - URLInputSource src(base,pathname); - Wrapper4InputSource dsrc(&src,false); - m_doc=p.parse(dsrc); - - log.infoStream() << "Loaded and parsed creds file (" << pathname << ")" << CategoryStream::ENDLINE; - DOMElement* e = m_doc->getDocumentElement(); if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) || XMLString::compareString(SHIB_L(Credentials),e->getLocalName())) @@ -165,24 +166,24 @@ XMLCredentialsImpl::XMLCredentialsImpl(const char* pathname) : m_doc(NULL) DOMElement* child=saml::XML::getFirstChildElement(e); while (!saml::XML::isElementNamed(child,XML::SHIB_NS,SHIB_L(KeyUse))) { - CredResolverFactory* factory=NULL; + string cr_type; auto_ptr id(XMLString::transcode(child->getAttributeNS(NULL,SHIB_L(Id)))); if (saml::XML::isElementNamed(child,XML::SHIB_NS,SHIB_L(FileCredResolver))) - factory=ShibConfig::getConfig().getCredResolverFactory("edu.internet2.middleware.shibboleth.creds.provider.FileCredResolver"); + cr_type="edu.internet2.middleware.shibboleth.creds.provider.FileCredResolver"; else if (saml::XML::isElementNamed(child,saml::XML::XMLSIG_NS,L(KeyInfo))) - factory=ShibConfig::getConfig().getCredResolverFactory("edu.internet2.middleware.shibboleth.creds.provider.KeyInfoResolver"); + cr_type="edu.internet2.middleware.shibboleth.creds.provider.KeyInfoResolver"; else if (saml::XML::isElementNamed(child,XML::SHIB_NS,SHIB_L(CustomCredResolver))) { - auto_ptr c(XMLString::transcode(child->getAttributeNS(NULL,SHIB_L(Class)))); - factory=ShibConfig::getConfig().getCredResolverFactory(c.get()); + auto_ptr_char c(child->getAttributeNS(NULL,SHIB_L(Class))); + cr_type=c.get(); } - if (factory) + if (!cr_type.empty()) { try { - ICredResolver* cr=(*factory)(child); + ICredResolver* cr=ShibConfig::getConfig().newCredResolver(cr_type.c_str(),child); m_resolverMap[id.get()]=cr; } catch (SAMLException& e) @@ -251,7 +252,7 @@ XMLCredentialsImpl::XMLCredentialsImpl(const char* pathname) : m_doc(NULL) } catch (SAMLException& e) { - log.errorStream() << "XML error while parsing creds configuration: " << e.what() << CategoryStream::ENDLINE; + log.errorStream() << "Error while parsing creds configuration: " << e.what() << CategoryStream::ENDLINE; for (vector::iterator i=m_keyuses.begin(); i!=m_keyuses.end(); i++) delete (*i); for (resolvermap_t::iterator j=m_resolverMap.begin(); j!=m_resolverMap.end(); j++) @@ -260,6 +261,7 @@ XMLCredentialsImpl::XMLCredentialsImpl(const char* pathname) : m_doc(NULL) m_doc->release(); throw; } +#ifndef _DEBUG catch (...) { log.error("Unexpected error while parsing creds configuration"); @@ -271,6 +273,7 @@ XMLCredentialsImpl::XMLCredentialsImpl(const char* pathname) : m_doc(NULL) m_doc->release(); throw; } +#endif } XMLCredentialsImpl::~XMLCredentialsImpl() @@ -279,81 +282,15 @@ XMLCredentialsImpl::~XMLCredentialsImpl() delete (*i); for (resolvermap_t::iterator j=m_resolverMap.begin(); j!=m_resolverMap.end(); j++) delete j->second; - if (m_doc) - m_doc->release(); -} - -XMLCredentials::XMLCredentials(const char* pathname) : m_filestamp(0), m_source(pathname), m_impl(NULL) -{ -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(pathname, &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(pathname, &stat_buf) == 0) -#endif - m_filestamp=stat_buf.st_mtime; - m_impl=new XMLCredentialsImpl(pathname); - m_lock=RWLock::create(); -} - -void XMLCredentials::lock() -{ - m_lock->rdlock(); - - // Check if we need to refresh. -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(m_source.c_str(), &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(m_source.c_str(), &stat_buf) == 0) -#endif - { - if (m_filestamp>0 && m_filestampunlock(); - m_lock->wrlock(); - if (m_filestamp>0 && m_filestampunlock(); - } - catch(SAMLException& e) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLCredentials").error("failed to reload credentials metadata, sticking with what we have: %s", e.what()); - } - catch(...) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLCredentials").error("caught an unknown exception, sticking with what we have"); - } - } - else - { - m_lock->unlock(); - } - m_lock->rdlock(); - } - } } - bool XMLCredentials::attach(const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx) const { NDC ndc("attach"); // Use the matching bindings. - for (XMLCredentialsImpl::BindingMap::const_iterator i=m_impl->m_bindings.begin(); i!=m_impl->m_bindings.end(); i++) + XMLCredentialsImpl* impl=dynamic_cast(getImplementation()); + for (XMLCredentialsImpl::BindingMap::const_iterator i=impl->m_bindings.begin(); i!=impl->m_bindings.end(); i++) { bool match=false; diff --git a/shib/XMLMetadata.cpp b/shib/XMLMetadata.cpp index af1cc41..3a1194f 100644 --- a/shib/XMLMetadata.cpp +++ b/shib/XMLMetadata.cpp @@ -71,7 +71,7 @@ using namespace std; namespace shibboleth { - class XMLMetadataImpl + class XMLMetadataImpl : public ReloadableXMLFileImpl { public: XMLMetadataImpl(const char* pathname); @@ -81,43 +81,44 @@ namespace shibboleth { { public: ContactInfo(ContactType type, const XMLCh* name, const XMLCh* email) - : m_type(type), m_name(XMLString::transcode(name)), m_email(XMLString::transcode(email)) {} + : m_type(type), m_name(name), m_email(email) {} ContactType getType() const { return m_type; } - const char* getName() const { return m_name.get(); } + const char* getName() const { return m_name.get(); } const char* getEmail() const { return m_email.get(); } private: ContactType m_type; - std::auto_ptr m_name, m_email; + auto_ptr_char m_name, m_email; }; class Authority : public IAuthority { public: - Authority(const XMLCh* name, const XMLCh* url) : m_name(name), m_url(XMLString::transcode(url)) {} + Authority(const XMLCh* name, const XMLCh* url) : m_name(name), m_url(url) {} const XMLCh* getName() const { return m_name; } const char* getURL() const { return m_url.get(); } private: const XMLCh* m_name; - auto_ptr m_url; + auto_ptr_char m_url; }; class OriginSite : public IOriginSite { public: - OriginSite(const XMLCh* name, const XMLCh* errorURL) - : m_name(name), m_errorURL(XMLString::transcode(errorURL)) {} + OriginSite(const XMLCh* name, const XMLCh* errorURL) : m_name(name), m_errorURL(errorURL) {} ~OriginSite(); const XMLCh* getName() const {return m_name;} Iterator getGroups() const {return m_groups;} Iterator getContacts() const {return m_contacts;} const char* getErrorURL() const {return m_errorURL.get();} - bool validate(Iterator certs) const {Trust t; return t.validate(this,certs);} - bool validate(Iterator certs) const {Trust t; return t.validate(this,certs);} + bool validate(const saml::Iterator& trusts, const Iterator& certs) const + {Trust t(trusts); return t.validate(this,certs);} + bool validate(const saml::Iterator& trusts, const Iterator& certs) const + {Trust t(trusts); return t.validate(this,certs);} Iterator getHandleServices() const {return m_handleServices;} Iterator getAttributeAuthorities() const {return m_attributes;} Iterator > getSecurityDomains() const {return m_domains;} @@ -125,7 +126,7 @@ namespace shibboleth { private: friend class XMLMetadataImpl; const XMLCh* m_name; - auto_ptr m_errorURL; + auto_ptr_char m_errorURL; vector m_contacts; vector m_handleServices; vector m_attributes; @@ -139,30 +140,39 @@ namespace shibboleth { typedef map sitemap_t; #endif sitemap_t m_sites; - DOMDocument* m_doc; }; - class XMLMetadata : public IMetadata + class XMLMetadata : public IMetadata, public ReloadableXMLFile { public: - XMLMetadata(const char* pathname); - ~XMLMetadata() { delete m_lock; delete m_impl; } + XMLMetadata(const char* pathname) : ReloadableXMLFile(pathname) {} + ~XMLMetadata() {} - void lock(); - void unlock() { m_lock->unlock(); } const ISite* lookup(const XMLCh* site) const; - - private: - std::string m_source; - time_t m_filestamp; - RWLock* m_lock; - XMLMetadataImpl* m_impl; + + protected: + virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const; }; } extern "C" IMetadata* XMLMetadataFactory(const char* source) { - return new XMLMetadata(source); + XMLMetadata* m=new XMLMetadata(source); + try + { + m->getImplementation(); + } + catch (...) + { + delete m; + throw; + } + return m; +} + +ReloadableXMLFileImpl* XMLMetadata::newImplementation(const char* pathname) const +{ + return new XMLMetadataImpl(pathname); } XMLMetadataImpl::OriginSite::~OriginSite() @@ -175,21 +185,13 @@ XMLMetadataImpl::OriginSite::~OriginSite() delete const_cast(*k); } -XMLMetadataImpl::XMLMetadataImpl(const char* pathname) : m_doc(NULL) +XMLMetadataImpl::XMLMetadataImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { NDC ndc("XMLMetadataImpl"); Category& log=Category::getInstance(SHIB_LOGCAT".XMLMetadataImpl"); - saml::XML::Parser p; try { - static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull}; - URLInputSource src(base,pathname); - Wrapper4InputSource dsrc(&src,false); - m_doc=p.parse(dsrc); - - log.infoStream() << "Loaded and parsed site file (" << pathname << ")" << CategoryStream::ENDLINE; - DOMElement* e = m_doc->getDocumentElement(); if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) || XMLString::compareString(XML::Literals::SiteGroup,e->getLocalName())) @@ -288,7 +290,7 @@ XMLMetadataImpl::XMLMetadataImpl(const char* pathname) : m_doc(NULL) } catch (SAMLException& e) { - log.errorStream() << "XML error while parsing site configuration: " << e.what() << CategoryStream::ENDLINE; + log.errorStream() << "Error while parsing site configuration: " << e.what() << CategoryStream::ENDLINE; for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++) delete i->second; if (m_doc) @@ -310,81 +312,16 @@ XMLMetadataImpl::~XMLMetadataImpl() { for (sitemap_t::iterator i=m_sites.begin(); i!=m_sites.end(); i++) delete i->second; - if (m_doc) - m_doc->release(); -} - -XMLMetadata::XMLMetadata(const char* pathname) : m_filestamp(0), m_source(pathname), m_impl(NULL) -{ -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(pathname, &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(pathname, &stat_buf) == 0) -#endif - m_filestamp=stat_buf.st_mtime; - m_impl=new XMLMetadataImpl(pathname); - m_lock=RWLock::create(); -} - -void XMLMetadata::lock() -{ - m_lock->rdlock(); - - // Check if we need to refresh. -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(m_source.c_str(), &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(m_source.c_str(), &stat_buf) == 0) -#endif - { - if (m_filestamp>0 && m_filestampunlock(); - m_lock->wrlock(); - if (m_filestamp>0 && m_filestampunlock(); - } - catch(SAMLException& e) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLMetadata").error("failed to reload metadata, sticking with what we have: %s", e.what()); - } - catch(...) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLMetadata").error("caught an unknown exception, sticking with what we have"); - } - } - else - { - m_lock->unlock(); - } - m_lock->rdlock(); - } - } } const ISite* XMLMetadata::lookup(const XMLCh* site) const { + XMLMetadataImpl* impl=dynamic_cast(getImplementation()); #ifdef HAVE_GOOD_STL - XMLMetadataImpl::sitemap_t::const_iterator i=m_impl->m_sites.find(site); + XMLMetadataImpl::sitemap_t::const_iterator i=impl->m_sites.find(site); #else auto_ptr temp(XMLString::transcode(site)); - XMLMetadataImpl::sitemap_t::const_iterator i=m_impl->m_sites.find(temp.get()); + XMLMetadataImpl::sitemap_t::const_iterator i=impl->m_sites.find(temp.get()); #endif - return (i==m_impl->m_sites.end()) ? NULL : i->second; + return (i==impl->m_sites.end()) ? NULL : i->second; } diff --git a/shib/XMLTrust.cpp b/shib/XMLTrust.cpp index c0a6b1b..b106bae 100644 --- a/shib/XMLTrust.cpp +++ b/shib/XMLTrust.cpp @@ -82,7 +82,7 @@ int verify_callback(int ok, X509_STORE_CTX* store) namespace shibboleth { - class XMLTrustImpl + class XMLTrustImpl : public ReloadableXMLFileImpl { public: XMLTrustImpl(const char* pathname); @@ -104,35 +104,44 @@ namespace shibboleth { vector m_keyauths; typedef map,KeyAuthority*> BindingMap; BindingMap m_bindings; - - DOMDocument* m_doc; }; - class XMLTrust : public ITrust + class XMLTrust : public ITrust, public ReloadableXMLFile { public: - XMLTrust(const char* pathname); - ~XMLTrust() { delete m_lock; delete m_impl; } + XMLTrust(const char* pathname) : ReloadableXMLFile(pathname) {} + ~XMLTrust() {} - void lock(); - void unlock() { m_lock->unlock(); } saml::Iterator getCertificates(const XMLCh* subject) const; - bool validate(const ISite* site, saml::Iterator certs) const; - bool validate(const ISite* site, saml::Iterator certs) const; + bool validate(const ISite* site, const saml::Iterator& certs) const; + bool validate(const ISite* site, const saml::Iterator& certs) const; bool attach(const ISite* site, SSL_CTX* ctx) const; - private: - std::string m_source; - time_t m_filestamp; - RWLock* m_lock; - XMLTrustImpl* m_impl; + protected: + virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const; }; } extern "C" ITrust* XMLTrustFactory(const char* source) { - return new XMLTrust(source); + XMLTrust* t=new XMLTrust(source); + try + { + t->getImplementation(); + } + catch (...) + { + delete t; + throw; + } + return t; +} + + +ReloadableXMLFileImpl* XMLTrust::newImplementation(const char* pathname) const +{ + return new XMLTrustImpl(pathname); } X509_STORE* XMLTrustImpl::KeyAuthority::getX509Store(bool cached) @@ -194,21 +203,13 @@ XMLTrustImpl::KeyAuthority::~KeyAuthority() delete (*j); } -XMLTrustImpl::XMLTrustImpl(const char* pathname) : m_doc(NULL) +XMLTrustImpl::XMLTrustImpl(const char* pathname) : ReloadableXMLFileImpl(pathname) { NDC ndc("XMLTrustImpl"); Category& log=Category::getInstance(SHIB_LOGCAT".XMLTrustImpl"); - saml::XML::Parser p; try { - static XMLCh base[]={chLatin_f, chLatin_i, chLatin_l, chLatin_e, chColon, chForwardSlash, chForwardSlash, chForwardSlash, chNull}; - URLInputSource src(base,pathname); - Wrapper4InputSource dsrc(&src,false); - m_doc=p.parse(dsrc); - - log.infoStream() << "Loaded and parsed trust file (" << pathname << ")" << CategoryStream::ENDLINE; - DOMElement* e = m_doc->getDocumentElement(); if (XMLString::compareString(XML::SHIB_NS,e->getNamespaceURI()) || XMLString::compareString(SHIB_L(Trust),e->getLocalName())) @@ -268,7 +269,7 @@ XMLTrustImpl::XMLTrustImpl(const char* pathname) : m_doc(NULL) } catch (SAMLException& e) { - log.errorStream() << "XML error while parsing trust configuration: " << e.what() << CategoryStream::ENDLINE; + log.errorStream() << "Error while parsing trust configuration: " << e.what() << CategoryStream::ENDLINE; for (vector::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++) delete (*i); if (m_doc) @@ -290,78 +291,13 @@ XMLTrustImpl::~XMLTrustImpl() { for (vector::iterator i=m_keyauths.begin(); i!=m_keyauths.end(); i++) delete (*i); - if (m_doc) - m_doc->release(); -} - -XMLTrust::XMLTrust(const char* pathname) : m_filestamp(0), m_source(pathname), m_impl(NULL) -{ -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(pathname, &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(pathname, &stat_buf) == 0) -#endif - m_filestamp=stat_buf.st_mtime; - m_impl=new XMLTrustImpl(pathname); - m_lock=RWLock::create(); -} - -void XMLTrust::lock() -{ - m_lock->rdlock(); - - // Check if we need to refresh. -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(m_source.c_str(), &stat_buf) == 0) -#else - struct stat stat_buf; - if (stat(m_source.c_str(), &stat_buf) == 0) -#endif - { - if (m_filestamp>0 && m_filestampunlock(); - m_lock->wrlock(); - if (m_filestamp>0 && m_filestampunlock(); - } - catch(SAMLException& e) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLTrust").error("failed to reload trust metadata, sticking with what we have: %s", e.what()); - } - catch(...) - { - m_lock->unlock(); - saml::NDC ndc("lock"); - Category::getInstance(SHIB_LOGCAT".XMLTrust").error("caught an unknown exception, sticking with what we have"); - } - } - else - { - m_lock->unlock(); - } - m_lock->rdlock(); - } - } } Iterator XMLTrust::getCertificates(const XMLCh* subject) const { // Find the first matching entity binding. - for (XMLTrustImpl::BindingMap::const_iterator i=m_impl->m_bindings.begin(); i!=m_impl->m_bindings.end(); i++) + XMLTrustImpl* impl=dynamic_cast(getImplementation()); + for (XMLTrustImpl::BindingMap::const_iterator i=impl->m_bindings.begin(); i!=impl->m_bindings.end(); i++) { if (i->second->m_type!=XMLTrustImpl::KeyAuthority::entity) continue; @@ -400,7 +336,8 @@ bool XMLTrust::attach(const ISite* site, SSL_CTX* ctx) const NDC ndc("attach"); // Use the matching bindings. - for (XMLTrustImpl::BindingMap::const_iterator i=m_impl->m_bindings.begin(); i!=m_impl->m_bindings.end(); i++) + XMLTrustImpl* impl=dynamic_cast(getImplementation()); + for (XMLTrustImpl::BindingMap::const_iterator i=impl->m_bindings.begin(); i!=impl->m_bindings.end(); i++) { if (i->second->m_type!=XMLTrustImpl::KeyAuthority::authority) continue; @@ -461,7 +398,7 @@ bool XMLTrust::attach(const ISite* site, SSL_CTX* ctx) const return false; } -bool XMLTrust::validate(const ISite* site, Iterator certs) const +bool XMLTrust::validate(const ISite* site, const Iterator& certs) const { vector temp; while (certs.hasNext()) @@ -469,14 +406,14 @@ bool XMLTrust::validate(const ISite* site, Iterator certs) cons return validate(site,temp); } -bool XMLTrust::validate(const ISite* site, Iterator certs) const +bool XMLTrust::validate(const ISite* site, const Iterator& certs) const { NDC ndc("validate"); STACK_OF(X509)* chain=sk_X509_new_null(); while (certs.hasNext()) { - auto_ptr temp(XMLString::transcode(certs.next())); + auto_ptr_char temp(certs.next()); X509* x=B64_to_X509(temp.get()); if (!x) { @@ -487,7 +424,8 @@ bool XMLTrust::validate(const ISite* site, Iterator certs) const } // Use the matching bindings. - for (XMLTrustImpl::BindingMap::const_iterator i=m_impl->m_bindings.begin(); i!=m_impl->m_bindings.end(); i++) + XMLTrustImpl* impl=dynamic_cast(getImplementation()); + for (XMLTrustImpl::BindingMap::const_iterator i=impl->m_bindings.begin(); i!=impl->m_bindings.end(); i++) { if (i->second->m_type!=XMLTrustImpl::KeyAuthority::authority) continue; @@ -514,7 +452,7 @@ bool XMLTrust::validate(const ISite* site, Iterator certs) const } catch (XMLException& ex) { - auto_ptr tmp(XMLString::transcode(ex.getMessage())); + auto_ptr_char tmp(ex.getMessage()); Category& log=Category::getInstance(SHIB_LOGCAT".XMLTrust"); log.errorStream() << "caught exception while parsing regular expression: " << tmp.get() << CategoryStream::ENDLINE; diff --git a/shib/internal.h b/shib/internal.h index 32b1b59..4a8fe67 100644 --- a/shib/internal.h +++ b/shib/internal.h @@ -71,7 +71,6 @@ #endif #include "shib.h" -#include "shib-threads.h" #include @@ -82,11 +81,17 @@ namespace shibboleth class ClubShibPOSTProfile : public ShibPOSTProfile { public: - ClubShibPOSTProfile(const saml::Iterator& policies, const XMLCh* receiver, int ttlSeconds); - ClubShibPOSTProfile(const saml::Iterator& policies, const XMLCh* issuer); + ClubShibPOSTProfile( + const saml::Iterator& metadatas, const saml::Iterator& trusts, + const saml::Iterator& policies, const XMLCh* receiver, int ttlSeconds + ); + ClubShibPOSTProfile( + const saml::Iterator& metadatas, const saml::Iterator& creds, + const saml::Iterator& policies, const XMLCh* issuer + ); virtual ~ClubShibPOSTProfile(); - virtual saml::SAMLResponse* prepare( + saml::SAMLResponse* prepare( const XMLCh* recipient, const XMLCh* name, const XMLCh* nameQualifier, @@ -101,7 +106,7 @@ namespace shibboleth ); protected: - virtual void verifySignature( + void verifySignature( const saml::SAMLSignedObject& obj, const IOriginSite* originSite, const XMLCh* signerName, @@ -111,7 +116,13 @@ namespace shibboleth class ShibSOAPBinding : public saml::SAMLSOAPBinding { public: - ShibSOAPBinding(const XMLCh* subject, const ISite* relyingParty) : m_subject(subject), m_relyingParty(relyingParty) {} + ShibSOAPBinding( + const saml::Iterator& metadatas, + const saml::Iterator& trusts, + const saml::Iterator& creds, + const XMLCh* subject, + const ISite* relyingParty + ) : m_metadatas(metadatas), m_creds(creds), m_trusts(trusts), m_subject(subject), m_relyingParty(relyingParty) {} virtual ~ShibSOAPBinding() {} virtual saml::SAMLResponse* send( @@ -124,6 +135,9 @@ namespace shibboleth friend bool ssl_ctx_callback(void* ssl_ctx, void* userptr); const XMLCh* m_subject; const ISite* m_relyingParty; + const saml::Iterator& m_metadatas; + const saml::Iterator& m_trusts; + const saml::Iterator& m_creds; }; class ShibInternalConfig : public ShibConfig @@ -132,24 +146,20 @@ namespace shibboleth ShibInternalConfig() {} bool init(); - void term(); + void term() {} void regFactory(const char* type, MetadataFactory* factory); void regFactory(const char* type, TrustFactory* factory); void regFactory(const char* type, CredentialsFactory* factory); void regFactory(const char* type, CredResolverFactory* factory); void regFactory(const char* type, AAPFactory* factory); - void regFactory(const char* type, saml::SAMLAttributeFactory* factory); void unregFactory(const char* type); - bool addMetadata(const char* type, const char* source); - - saml::Iterator getMetadataProviders() const {return m_providers;} - saml::Iterator getTrustProviders() const {return m_trust_providers;} - saml::Iterator getCredentialProviders() const {return m_cred_providers;} - CredResolverFactory* getCredResolverFactory(const char* type) const; - saml::Iterator getAAPProviders() const {return m_aap_providers;} - saml::SAMLAttributeFactory* getAttributeFactory(const char* type) const; + IMetadata* newMetadata(const char* type, const char* source) const; + ITrust* newTrust(const char* type, const char* source) const; + ICredentials* newCredentials(const char* type, const char* source) const; + IAAP* newAAP(const char* type, const char* source) const; + ICredResolver* newCredResolver(const char* type, const DOMElement* source) const; private: friend class OriginMetadata; @@ -162,17 +172,11 @@ namespace shibboleth typedef std::map CredentialsFactoryMap; typedef std::map CredResolverFactoryMap; typedef std::map AAPFactoryMap; - typedef std::map AttributeFactoryMap; MetadataFactoryMap m_metadataFactoryMap; TrustFactoryMap m_trustFactoryMap; CredentialsFactoryMap m_credFactoryMap; CredResolverFactoryMap m_credResolverFactoryMap; AAPFactoryMap m_aapFactoryMap; - AttributeFactoryMap m_attrFactoryMap; - std::vector m_providers; - std::vector m_trust_providers; - std::vector m_cred_providers; - std::vector m_aap_providers; }; // OpenSSL Utilities diff --git a/shib/shib.dsp b/shib/shib.dsp index 08242d1..03f2e22 100644 --- a/shib/shib.dsp +++ b/shib/shib.dsp @@ -43,7 +43,7 @@ RSC=rc.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SHIB_EXPORTS" /YX /FD /c -# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I "." /I "..\..\..\opensaml\c" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GR /GX /O2 /I ".." /I "." /I "..\..\..\opensaml\c" /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" @@ -69,7 +69,7 @@ LINK32=link.exe # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SHIB_EXPORTS" /YX /FD /GZ /c -# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I "." /I "..\..\..\opensaml\c" /D "_WINDOWS" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_AFXDLL" /FR /YX /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GR /GX /ZI /Od /I ".." /I "." /I "..\..\..\opensaml\c" /D "_WINDOWS" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_AFXDLL" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" @@ -117,6 +117,10 @@ SOURCE=.\Metadata.cpp # End Source File # Begin Source File +SOURCE=.\ReloadableXMLFile.cpp +# End Source File +# Begin Source File + SOURCE=.\SAMLBindingFactory.cpp # End Source File # Begin Source File @@ -153,10 +157,6 @@ SOURCE=.\ShibSOAPBinding.cpp # End Source File # Begin Source File -SOURCE=.\SimpleAttribute.cpp -# End Source File -# Begin Source File - SOURCE=.\XML.cpp # End Source File # Begin Source File diff --git a/shib/shib.h b/shib/shib.h index 0083591..6379a1d 100644 --- a/shib/shib.h +++ b/shib/shib.h @@ -60,6 +60,7 @@ #define __shib_h__ #include +#include #include #ifdef WIN32 @@ -95,6 +96,13 @@ namespace shibboleth // Metadata abstract interfaces + struct SHIB_EXPORTS ILockable + { + virtual void lock()=0; + virtual void unlock()=0; + virtual ~ILockable() {} + }; + struct SHIB_EXPORTS IContactInfo { enum ContactType { technical, administrative, billing, other }; @@ -103,15 +111,17 @@ namespace shibboleth virtual const char* getEmail() const=0; virtual ~IContactInfo() {} }; - + + struct SHIB_EXPORTS ITrust; // forward decl + struct SHIB_EXPORTS ISite { virtual const XMLCh* getName() const=0; virtual saml::Iterator getGroups() const=0; virtual saml::Iterator getContacts() const=0; virtual const char* getErrorURL() const=0; - virtual bool validate(saml::Iterator certs) const=0; - virtual bool validate(saml::Iterator certs) const=0; + virtual bool validate(const saml::Iterator& trusts, const saml::Iterator& certs) const=0; + virtual bool validate(const saml::Iterator& trusts, const saml::Iterator& certs) const=0; virtual ~ISite() {} }; @@ -130,29 +140,23 @@ namespace shibboleth virtual ~IOriginSite() {} }; - struct SHIB_EXPORTS IMetadata + struct SHIB_EXPORTS IMetadata : public virtual ILockable { - virtual void lock()=0; - virtual void unlock()=0; virtual const ISite* lookup(const XMLCh* site) const=0; virtual ~IMetadata() {} }; - struct SHIB_EXPORTS ITrust + struct SHIB_EXPORTS ITrust : public virtual ILockable { - virtual void lock()=0; - virtual void unlock()=0; virtual saml::Iterator getCertificates(const XMLCh* subject) const=0; - virtual bool validate(const ISite* site, saml::Iterator certs) const=0; - virtual bool validate(const ISite* site, saml::Iterator certs) const=0; + virtual bool validate(const ISite* site, const saml::Iterator& certs) const=0; + virtual bool validate(const ISite* site, const saml::Iterator& certs) const=0; virtual bool attach(const ISite* site, SSL_CTX* ctx) const=0; virtual ~ITrust() {} }; - struct SHIB_EXPORTS ICredentials + struct SHIB_EXPORTS ICredentials : public virtual ILockable { - virtual void lock()=0; - virtual void unlock()=0; virtual bool attach(const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx) const=0; virtual ~ICredentials() {} }; @@ -173,14 +177,12 @@ namespace shibboleth virtual const char* getFactory() const=0; virtual const char* getAlias() const=0; virtual const char* getHeader() const=0; - virtual bool accept(const XMLCh* originSite, const DOMElement* e) const=0; + virtual void apply(const IOriginSite* originSite, saml::SAMLAttribute& attribute) const=0; virtual ~IAttributeRule() {} }; - struct SHIB_EXPORTS IAAP + struct SHIB_EXPORTS IAAP : public virtual ILockable { - virtual void lock()=0; - virtual void unlock()=0; virtual const IAttributeRule* lookup(const XMLCh* attrName, const XMLCh* attrNamespace=NULL) const=0; virtual const IAttributeRule* lookup(const char* alias) const=0; virtual saml::Iterator getAttributeRules() const=0; @@ -204,22 +206,7 @@ namespace shibboleth template class SHIB_EXPORTS saml::ArrayIterator; #endif - class SHIB_EXPORTS SimpleAttribute : public saml::SAMLAttribute - { - public: - SimpleAttribute(const XMLCh* name, const XMLCh* ns, long lifetime=0, - const saml::Iterator& values=EMPTY(const XMLCh*)); - SimpleAttribute(DOMElement* e); - virtual saml::SAMLObject* clone() const; - virtual ~SimpleAttribute(); - - protected: - virtual bool accept(DOMElement* e) const; - - const XMLCh* m_originSite; - }; - - class SHIB_EXPORTS ScopedAttribute : public SimpleAttribute + class SHIB_EXPORTS ScopedAttribute : public saml::SAMLAttribute { public: ScopedAttribute(const XMLCh* name, const XMLCh* ns, long lifetime=0, @@ -228,15 +215,18 @@ namespace shibboleth ScopedAttribute(DOMElement* e); virtual ~ScopedAttribute(); - virtual DOMNode* toDOM(DOMDocument* doc=NULL, bool xmlns=true) const; virtual saml::SAMLObject* clone() const; virtual saml::Iterator getValues() const; virtual saml::Iterator getSingleByteValues() const; + virtual void setValues(const saml::Iterator& values=EMPTY(const XMLCh*)); + virtual void addValue(const XMLCh* value); + virtual void removeValue(unsigned int index); protected: - virtual bool addValue(DOMElement* e); + virtual void valueToDOM(unsigned int index, DOMElement* e) const; + const XMLCh* m_originSite; std::vector m_scopes; mutable std::vector m_scopedValues; }; @@ -244,9 +234,15 @@ namespace shibboleth class SHIB_EXPORTS ShibPOSTProfile { public: - ShibPOSTProfile(const saml::Iterator& policies, const XMLCh* receiver, int ttlSeconds); - ShibPOSTProfile(const saml::Iterator& policies, const XMLCh* issuer); - virtual ~ShibPOSTProfile(); + ShibPOSTProfile( + const saml::Iterator& metadatas, const saml::Iterator& trusts, + const saml::Iterator& policies, const XMLCh* receiver, int ttlSeconds + ); + ShibPOSTProfile( + const saml::Iterator& metadatas, const saml::Iterator& creds, + const saml::Iterator& policies, const XMLCh* issuer + ); + virtual ~ShibPOSTProfile() {} virtual const saml::SAMLAssertion* getSSOAssertion(const saml::SAMLResponse& r); virtual const saml::SAMLAuthenticationStatement* getSSOStatement(const saml::SAMLAssertion& a); @@ -276,21 +272,26 @@ namespace shibboleth XSECCryptoKey* knownKey=NULL); signatureMethod m_algorithm; - std::vector m_policies; - XMLCh* m_issuer; - XMLCh* m_receiver; + const saml::Iterator& m_policies; + const saml::Iterator& m_metadatas; + const saml::Iterator& m_trusts; + const saml::Iterator& m_creds; + const XMLCh* m_issuer; + const XMLCh* m_receiver; int m_ttlSeconds; - - private: - ShibPOSTProfile(const ShibPOSTProfile&) {} - ShibPOSTProfile& operator=(const ShibPOSTProfile&) {return *this;} }; class SHIB_EXPORTS ShibPOSTProfileFactory { public: - static ShibPOSTProfile* getInstance(const saml::Iterator& policies, const XMLCh* receiver, int ttlSeconds); - static ShibPOSTProfile* getInstance(const saml::Iterator& policies, const XMLCh* issuer); + static ShibPOSTProfile* getInstance( + const saml::Iterator& metadatas, const saml::Iterator& trusts, + const saml::Iterator& policies, const XMLCh* receiver, int ttlSeconds + ); + static ShibPOSTProfile* getInstance( + const saml::Iterator& metadatas, const saml::Iterator& creds, + const saml::Iterator& policies, const XMLCh* issuer + ); }; // Glue classes between abstract metadata and concrete providers @@ -298,7 +299,7 @@ namespace shibboleth class SHIB_EXPORTS OriginMetadata { public: - OriginMetadata(const XMLCh* site); + OriginMetadata(const saml::Iterator& metadatas, const XMLCh* site); ~OriginMetadata(); bool fail() const {return m_mapper==NULL;} const IOriginSite* operator->() const {return m_site;} @@ -314,7 +315,7 @@ namespace shibboleth class SHIB_EXPORTS Trust { public: - Trust() : m_mapper(NULL) {} + Trust(const saml::Iterator& trusts) : m_trusts(trusts), m_mapper(NULL) {} ~Trust(); saml::Iterator getCertificates(const XMLCh* subject); bool validate(const ISite* site, saml::Iterator certs) const; @@ -325,24 +326,29 @@ namespace shibboleth Trust(const Trust&); void operator=(const Trust&); ITrust* m_mapper; + const saml::Iterator& m_trusts; }; class SHIB_EXPORTS Credentials { public: - static bool attach(const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx); + static bool attach( + const saml::Iterator& creds, const XMLCh* subject, const ISite* relyingParty, SSL_CTX* ctx + ); }; class SHIB_EXPORTS AAP { public: - AAP(const XMLCh* attrName, const XMLCh* attrNamespace=NULL); - AAP(const char* alias); + AAP(const saml::Iterator& aaps, const XMLCh* attrName, const XMLCh* attrNamespace=NULL); + AAP(const saml::Iterator& aaps, const char* alias); ~AAP(); bool fail() const {return m_mapper==NULL;} const IAttributeRule* operator->() const {return m_rule;} operator const IAttributeRule*() const {return m_rule;} + static void apply(const saml::Iterator& aaps, const IOriginSite* originSite, saml::SAMLAssertion& assertion); + private: AAP(const AAP&); void operator=(const AAP&); @@ -354,7 +360,7 @@ namespace shibboleth typedef IMetadata* MetadataFactory(const char* source); typedef ITrust* TrustFactory(const char* source); typedef ICredentials* CredentialsFactory(const char* source); - typedef ICredResolver* CredResolverFactory(const DOMElement* e); + typedef ICredResolver* CredResolverFactory(const DOMElement* source); typedef IAAP* AAPFactory(const char* source); } @@ -371,24 +377,20 @@ namespace shibboleth // enables runtime and clients to access configuration static ShibConfig& getConfig(); - // allows pluggable implementations of metadata + // allows pluggable implementations of metadata and configuration data virtual void regFactory(const char* type, MetadataFactory* factory)=0; virtual void regFactory(const char* type, TrustFactory* factory)=0; virtual void regFactory(const char* type, CredentialsFactory* factory)=0; virtual void regFactory(const char* type, CredResolverFactory* factory)=0; virtual void regFactory(const char* type, AAPFactory* factory)=0; - virtual void regFactory(const char* type, saml::SAMLAttributeFactory* factory)=0; virtual void unregFactory(const char* type)=0; - // builds a specific metadata lookup object - virtual bool addMetadata(const char* type, const char* source)=0; - - virtual saml::Iterator getMetadataProviders() const=0; - virtual saml::Iterator getTrustProviders() const=0; - virtual saml::Iterator getCredentialProviders() const=0; - virtual CredResolverFactory* getCredResolverFactory(const char* type) const=0; - virtual saml::Iterator getAAPProviders() const=0; - virtual saml::SAMLAttributeFactory* getAttributeFactory(const char* type) const=0; + // build a specific metadata lookup object + virtual IMetadata* newMetadata(const char* type, const char* source) const=0; + virtual ITrust* newTrust(const char* type, const char* source) const=0; + virtual ICredentials* newCredentials(const char* type, const char* source) const=0; + virtual IAAP* newAAP(const char* type, const char* source) const=0; + virtual ICredResolver* newCredResolver(const char* type, const DOMElement* source) const=0; }; struct SHIB_EXPORTS Constants @@ -398,7 +400,7 @@ namespace shibboleth static const XMLCh XMLSIG_RETMETHOD_RAWX509[]; // DER X.509 defined by xmlsig - static saml::QName SHIB_ATTRIBUTE_VALUE_TYPE; + static saml::QName SHIB_ATTRIBUTE_VALUE_TYPE; }; class SHIB_EXPORTS XML @@ -478,10 +480,50 @@ namespace shibboleth { public: static saml::SAMLBinding* getInstance( + const saml::Iterator& metadatas, + const saml::Iterator& trusts, + const saml::Iterator& creds, const XMLCh* subject, const ISite* relyingParty, const XMLCh* protocol=saml::SAMLBinding::SAML_SOAP_HTTPS); }; + + /* Helper classes for implementing reloadable XML-based config files + The ILockable interface will usually be inherited twice, once as + part of the external interface to clients and once as an implementation + detail of the reloading class below. + */ + + class SHIB_EXPORTS ReloadableXMLFileImpl + { + public: + ReloadableXMLFileImpl(const char* pathname); + virtual ~ReloadableXMLFileImpl(); + + protected: + DOMDocument* m_doc; + }; + + class SHIB_EXPORTS ReloadableXMLFile : protected virtual ILockable + { + public: + ReloadableXMLFile(const char* pathname); + ~ReloadableXMLFile() { delete m_lock; delete m_impl; } + + virtual void lock(); + virtual void unlock() { m_lock->unlock(); } + + ReloadableXMLFileImpl* getImplementation() const; + + protected: + virtual ReloadableXMLFileImpl* newImplementation(const char* pathname) const=0; + + private: + std::string m_source; + time_t m_filestamp; + RWLock* m_lock; + mutable ReloadableXMLFileImpl* m_impl; + }; } #endif -- 2.1.4