// per-process configuration
extern "C" module MODULE_VAR_EXPORT eduPerson_module;
-extern "C" SAMLAttribute* scopedFactory(IDOM_Element* e)
+extern "C" SAMLAttribute* EPPNFactory(IDOM_Element* e)
{
- return new ScopedAttribute(e);
+ return new EPPNAttribute(e);
}
-extern "C" SAMLAttribute* unscopedFactory(IDOM_Element* e)
+extern "C" SAMLAttribute* AffiliationFactory(IDOM_Element* e)
{
- return new SAMLAttribute(e);
+ return new AffiliationAttribute(e);
}
-XMLCh* eduPersonPrincipalName=NULL;
-XMLCh* eduPersonAffiliation=NULL;
-XMLCh* eduPersonEntitlement=NULL;
+extern "C" SAMLAttribute* PrimaryAffiliationFactory(IDOM_Element* e)
+{
+ return new PrimaryAffiliationAttribute(e);
+}
+
+extern "C" SAMLAttribute* EntitlementFactory(IDOM_Element* e)
+{
+ return new EntitlementAttribute(e);
+}
/*
* eduPerson_child_init()
extern "C" void eduPerson_child_init(server_rec* s, pool* p)
{
// Register extension schema and attribute factories.
- saml::XML::registerSchema(EDUPERSON_NS,EDUPERSON_SCHEMA_ID);
-
- eduPersonPrincipalName=XMLString::transcode("urn:mace:eduPerson:1.0:eduPersonPrincipalName");
- eduPersonAffiliation=XMLString::transcode("urn:mace:eduPerson:1.0:eduPersonAffiliation");
- eduPersonEntitlement=XMLString::transcode("urn:mace:eduPerson:1.0:eduPersonEntitlement");
+ saml::XML::registerSchema(eduPerson::XML::EDUPERSON_NS,eduPerson::XML::EDUPERSON_SCHEMA_ID);
- SAMLAttribute::regFactory(eduPersonPrincipalName,Constants::SHIB_ATTRIBUTE_NAMESPACE_URI,&scopedFactory);
- SAMLAttribute::regFactory(eduPersonAffiliation,Constants::SHIB_ATTRIBUTE_NAMESPACE_URI,&scopedFactory);
- SAMLAttribute::regFactory(eduPersonEntitlement,Constants::SHIB_ATTRIBUTE_NAMESPACE_URI,&unscopedFactory);
+ SAMLAttribute::regFactory(eduPerson::Constants::EDUPERSON_PRINCIPAL_NAME,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI,
+ &EPPNFactory);
+ SAMLAttribute::regFactory(eduPerson::Constants::EDUPERSON_AFFILIATION,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI,
+ &AffiliationFactory);
+ SAMLAttribute::regFactory(eduPerson::Constants::EDUPERSON_PRIMARY_AFFILIATION,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI,
+ &PrimaryAffiliationFactory);
+ SAMLAttribute::regFactory(eduPerson::Constants::EDUPERSON_ENTITLEMENT,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI,
+ &EntitlementFactory);
std::fprintf(stderr,"eduPerson_child_init() done\n");
}
*/
extern "C" void eduPerson_child_exit(server_rec* s, pool* p)
{
- SAMLAttribute::unregFactory(eduPersonPrincipalName,Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
- SAMLAttribute::unregFactory(eduPersonAffiliation,Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
- SAMLAttribute::unregFactory(eduPersonEntitlement,Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
+ SAMLAttribute::unregFactory(eduPerson::Constants::EDUPERSON_PRINCIPAL_NAME,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
+ SAMLAttribute::unregFactory(eduPerson::Constants::EDUPERSON_AFFILIATION,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
+ SAMLAttribute::unregFactory(eduPerson::Constants::EDUPERSON_PRIMARY_AFFILIATION,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
+ SAMLAttribute::unregFactory(eduPerson::Constants::EDUPERSON_ENTITLEMENT,
+ shibboleth::Constants::SHIB_ATTRIBUTE_NAMESPACE_URI);
std::fprintf(stderr,"eduPerson_child_exit() done\n");
}
bool isSessionValid(time_t lifetime, time_t timeout);
const XMLCh* getHandle() { return m_handle.c_str(); }
const XMLCh* getOriginSite() { return m_originSite.c_str(); }
+ const char* getClientAddress() { return m_clientAddress.c_str(); }
private:
void populate(const char* resource_url);
xstring m_originSite;
xstring m_handle;
SAMLAuthorityBinding* m_binding;
+ string m_clientAddress;
SAMLResponse* m_response;
SAMLAssertion* m_assertion;
time_t m_sessionCreated;
location.reset(XMLString::transcode(ap_getword(r->pool,&token,'=')));
else if (!strcmp("Time",w))
m_sessionCreated=atoi(ap_getword(r->pool,&token,'='));
- else if (!strcmp ("EOF",w))
+ else if (!strcmp("ClientAddress",w))
+ m_clientAddress=ap_getword(r->pool,&token,'=');
+ else if (!strcmp("EOF",w))
break;
}
ap_cfg_closefile(f);
return;
auto_ptr<XMLCh> resource(XMLString::transcode(resource_url));
- static const XMLCh* policies[] = { Constants::POLICY_CLUBSHIB };
+ static const XMLCh* policies[] = { shibboleth::Constants::POLICY_CLUBSHIB };
static const saml::QName* respondWiths[] = { &g_respondWith };
// Build a SAML Request and send it to the AA.
// per-process configuration
extern "C" module MODULE_VAR_EXPORT shib_module;
-char *g_szSchemaPath = "/usr/local/shib/schemas/";
+char* g_szSchemaPath = "/usr/local/shib/schemas/";
+char* g_szSSLCertFile="";
+char* g_szSSLKeyFile="";
+char* g_szSSLKeyPass="";
+char* g_szSSLCAList="";
map<string,string> g_mapAttribNameToHeader;
map<string,string> g_mapAttribRuleToHeader;
command_rec shib_cmds[] = {
{"ShibSchemaPath", (config_fn_t)ap_set_global_string_slot, &g_szSchemaPath,
RSRC_CONF, TAKE1, "Path to XML schema files."},
+ {"ShibSSLCertFile", (config_fn_t)ap_set_global_string_slot, &g_szSSLCertFile,
+ RSRC_CONF, TAKE1, "File containing SHAR's client certificate for contacting AA."},
+ {"ShibSSLKeyFile", (config_fn_t)ap_set_global_string_slot, &g_szSSLKeyFile,
+ RSRC_CONF, TAKE1, "File containing SHAR's private key for contacting AA."},
+ {"ShibSSLKeyPass", (config_fn_t)ap_set_global_string_slot, &g_szSSLKeyPass,
+ RSRC_CONF, TAKE1, "File containing passphrase for SHAR's private key."},
+ {"ShibSSLCAList", (config_fn_t)ap_set_global_string_slot, &g_szSSLCAList,
+ RSRC_CONF, TAKE1, "File containing list of CAs to trust when validating AA credentials."},
{"ShibMapAttribute", (config_fn_t)ap_set_attribute_mapping, NULL,
RSRC_CONF, TAKE23, "Define request header name and 'require' alias for an attribute."},
OR_AUTHCFG, FLAG, "Respond to AuthType Basic and convert to shib?"},
{"ShibCheckAddress", (config_fn_t)ap_set_flag_slot,
(void *) XtOffsetOf (shib_dir_config, bCheckAddress),
- OR_AUTHCFG, FLAG, "Verify IP address of requestor matches token?"},
+ OR_AUTHCFG, FLAG, "Verify IP address of requester matches token?"},
{"ShibAuthLifetime", (config_fn_t)set_lifetime, NULL,
OR_AUTHCFG, TAKE1, "Lifetime of session in seconds."},
{"ShibAuthTimeout", (config_fn_t)set_timeout, NULL,
static DummyMapper mapper;
SAMLconf.schema_dir=g_szSchemaPath;
+ SAMLconf.ssl_certfile=g_szSSLCertFile;
+ SAMLconf.ssl_keyfile=g_szSSLKeyFile;
+ SAMLconf.ssl_keypass=g_szSSLKeyPass;
+ SAMLconf.ssl_calist=g_szSSLCAList;
if (!SAMLConfig::init(&SAMLconf))
{
std::fprintf(stderr,"shib_child_init() failed to initialize OpenSAML\n");
return DECLINED;
}
+ // SSL check.
+ if (dc->bSSLOnly && strcmp(ap_http_method(r),"https"))
+ {
+ ap_log_rerror(APLOG_MARK,APLOG_ERR,r,"shib_check_user() blocked non-SSL access");
+ return SERVER_ERROR;
+ }
+
// We're in charge, so check for cookie.
const char* session_id=NULL;
const char* cookies=ap_table_get(r->headers_in,"Cookie");
if (!cookies || !(session_id=strstr(cookies,sc->szCookieName)))
{
// Redirect to WAYF.
- char* wayf=ap_pstrcat (r->pool,sc->szWAYFLocation,"?shire=",url_encode(r,sc->szSHIRELocation),
- "&target=",url_encode(r,targeturl),NULL);
- ap_table_setn (r->headers_out,"Location",wayf);
+ char* wayf=ap_pstrcat(r->pool,sc->szWAYFLocation,
+ "?shire=",url_encode(r,sc->szSHIRELocation),
+ "&target=",url_encode(r,targeturl),NULL);
+ ap_table_setn(r->headers_out,"Location",wayf);
return REDIRECT;
}
if (cookieend)
*cookieend = '\0'; /* Ignore anyting after a ; */
session_id=cookiebuf;
-
+
// The caching logic is the heart of the "SHAR".
+ CCacheEntry* entry=NULL;
try
{
- CCacheEntry* entry=CCache::g_Cache->find(session_id);
+ entry=CCache::g_Cache->find(session_id);
if (!entry)
{
- // Construct the path to the session file
+ // Construct the path to the session file
char* sessionFile=ap_pstrcat(r->pool,sc->szSHIRESessionPath,"/",session_id,NULL);
- entry=new CCacheEntry(r,sessionFile);
- CCache::g_Cache->insert(session_id,entry);
+ try
+ {
+ entry=new CCacheEntry(r,sessionFile);
+ CCache::g_Cache->insert(session_id,entry);
+ }
+ catch (runtime_error e)
+ {
+ char* wayf=ap_pstrcat(r->pool,sc->szWAYFLocation,
+ "?shire=",url_encode(r,sc->szSHIRELocation),
+ "&target=",url_encode(r,targeturl),NULL);
+ ap_table_setn(r->headers_out,"Location",wayf);
+ return REDIRECT;
+ }
auto_ptr<char> h(XMLString::transcode(entry->getHandle()));
auto_ptr<char> d(XMLString::transcode(entry->getOriginSite()));
ap_log_rerror(APLOG_MARK,APLOG_INFO,r,
}
else if (!entry->isSessionValid(dc->secLifetime,dc->secTimeout))
{
+ ap_log_rerror(APLOG_MARK,APLOG_INFO,r,"shib_check_user() expired session");
CCache::g_Cache->remove(session_id);
delete entry;
- char* wayf=ap_pstrcat (r->pool,sc->szWAYFLocation,
- "?shire=",url_encode(r,sc->szSHIRELocation),
- "&target=",url_encode(r,targeturl),NULL);
- ap_table_setn (r->headers_out,"Location",wayf);
- return REDIRECT;
+ char* wayf=ap_pstrcat(r->pool,sc->szWAYFLocation,
+ "?shire=",url_encode(r,sc->szSHIRELocation),
+ "&target=",url_encode(r,targeturl),NULL);
+ ap_table_setn(r->headers_out,"Location",wayf);
+ return REDIRECT;
}
+ else if (dc->bCheckAddress && entry->getClientAddress() &&
+ strcmp(entry->getClientAddress(),r->connection->remote_ip))
+ {
+ ap_log_rerror(APLOG_MARK,APLOG_INFO,r,"shib_check_user() detected bad address, expected %s",
+ entry->getClientAddress());
+ CCache::g_Cache->remove(session_id);
+ delete entry;
+ return SERVER_ERROR;
+ }
ap_table_unset(r->headers_in,"Shib-Attributes"); // per arch-doc, until we populate it
Iterator<SAMLAttribute*> i=entry->getAttributes(targeturl);
if (iname!=g_mapAttribNames.end())
{
string hname=g_mapAttribNameToHeader[iname->second];
- Iterator<xstring> vals=attr->getValues();
+ Iterator<string> vals=attr->getSingleByteValues();
if (hname=="REMOTE_USER" && vals.hasNext())
- {
- auto_ptr<char> username(XMLString::transcode(vals.next().c_str()));
- r->connection->user=ap_pstrdup(r->connection->pool,username.get());
- }
+ r->connection->user=ap_pstrdup(r->connection->pool,vals.next().c_str());
else
{
char* header=ap_pstrdup(r->pool," ");
while (vals.hasNext())
- {
- auto_ptr<char> wide(XMLString::transcode(vals.next().c_str()));
- header=ap_pstrcat(r->pool,header,wide.get()," ",NULL);
- }
+ header=ap_pstrcat(r->pool,header,vals.next().c_str()," ",NULL);
ap_table_setn(r->headers_in,hname.c_str(),header);
}
}
}
return OK;
}
- catch (SAMLException & e)
+ catch (SAMLException& e)
{
- ap_log_rerror(APLOG_MARK,APLOG_ERR,r,
- "shib_check_user() SAML exception: %s",e.what());
+ ap_log_rerror(APLOG_MARK,APLOG_ERR,r,"shib_check_user() SAML exception: %s",e.what());
+ // Strictly speaking, an error here or below just means no attributes are available.
+ // That's a matter for the RM to deal with, not us. The one exception is an invalid
+ // handle status from the AA, which is a signal to destroy the session.
+ Iterator<saml::QName> i=e.getCodes();
+ int c=0;
+ while (i.hasNext())
+ {
+ c++;
+ saml::QName q=i.next();
+ if (c==1 && !XMLString::compareString(q.getNamespaceURI(),saml::XML::SAMLP_NS) &&
+ !XMLString::compareString(q.getLocalName(),L(Requester)))
+ continue;
+ else if (c==2 && !XMLString::compareString(q.getNamespaceURI(),shibboleth::XML::SHIB_NS) &&
+ !XMLString::compareString(q.getLocalName(),shibboleth::XML::Literals::InvalidHandle))
+ {
+ ap_log_rerror(APLOG_MARK,APLOG_INFO,r,"shib_check_user() told by AA to discard handle");
+ CCache::g_Cache->remove(session_id);
+ delete entry;
+ char* wayf=ap_pstrcat(r->pool,sc->szWAYFLocation,
+ "?shire=",url_encode(r,sc->szSHIRELocation),
+ "&target=",url_encode(r,targeturl),NULL);
+ ap_table_setn(r->headers_out,"Location",wayf);
+ return REDIRECT;
+ }
+ break;
+ }
+ return OK;
}
- catch (XMLException & e)
+ catch (XMLException& e)
{
auto_ptr<char> msg(XMLString::transcode(e.getMessage()));
- ap_log_rerror(APLOG_MARK,APLOG_ERR,r,
- "shib_check_user() Xerxes XML exception: %s",msg.get());
+ ap_log_rerror(APLOG_MARK,APLOG_ERR,r,"shib_check_user() Xerxes XML exception: %s",msg.get());
+ return OK;
}
catch (...)
{
- ap_log_rerror(APLOG_MARK,APLOG_ERR,r,
- "shib_check_user() unknown exception: %s");
+ ap_log_rerror(APLOG_MARK,APLOG_ERR,r,"shib_check_user() unknown exception");
}
return SERVER_ERROR;