2 * Copyright 2001-2005 Internet2
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * MemoryListener.cpp -- An actual implementation of the IListener functional methods
29 using namespace shibboleth;
30 using namespace shibtarget;
31 using namespace shibtarget::logging;
34 class MemoryListener : public virtual IListener
37 MemoryListener(const DOMElement* e) : log(&Category::getInstance(SHIBT_LOGCAT".Listener")) {}
40 bool create(ShibSocket& s) const {return true;}
41 bool bind(ShibSocket& s, bool force=false) const {return true;}
42 bool connect(ShibSocket& s) const {return true;}
43 bool close(ShibSocket& s) const {return true;}
44 bool accept(ShibSocket& listener, ShibSocket& s) const {return true;}
47 const IApplication* application,
48 int supported_profiles,
49 const char* recipient,
54 std::string& provider_id
58 const IApplication* application,
61 ISessionCacheEntry** pentry
65 const IApplication* application,
69 void ping(int& i) const;
76 IPlugIn* MemoryListenerFactory(const DOMElement* e)
78 return new MemoryListener(e);
81 void MemoryListener::sessionNew(
82 const IApplication* app,
83 int supported_profiles,
84 const char* recipient,
93 saml::NDC ndc("sessionNew");
96 log->debug("creating session for %s", ip);
97 log->debug("recipient: %s", recipient);
98 log->debug("application: %s", app->getId());
100 auto_ptr_XMLCh wrecipient(recipient);
102 // Access the application config. It's already locked behind us.
103 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
104 IConfig* conf=stc.getINI();
106 bool checkIPAddress=true;
107 const IPropertySet* props=app->getPropertySet("Sessions");
109 pair<bool,bool> pcheck=props->getBool("checkAddress");
111 checkIPAddress = pcheck.second;
114 pair<bool,bool> checkReplay=pair<bool,bool>(false,false);
115 props=app->getPropertySet("Sessions");
117 checkReplay=props->getBool("checkReplay");
119 const IRoleDescriptor* role=NULL;
120 Metadata m(app->getMetadataProviders());
121 SAMLBrowserProfile::BrowserProfileResponse bpr;
123 if (supported_profiles & SAML11_POST || supported_profiles & SAML10_POST)
124 allowed |= SAMLBrowserProfile::Post;
125 if (supported_profiles & SAML11_ARTIFACT || supported_profiles & SAML10_ARTIFACT)
126 allowed |= SAMLBrowserProfile::Artifact;
127 int minorVersion=(supported_profiles & SAML11_ARTIFACT || supported_profiles & SAML11_POST) ? 1 : 0;
130 auto_ptr<SAMLBrowserProfile::ArtifactMapper> artifactMapper(app->getArtifactMapper());
132 // Try and run the profile.
133 log->debug("executing browser profile...");
134 bpr=app->getBrowserProfile()->receive(
138 (!checkReplay.first || checkReplay.second) ? conf->getReplayCache() : NULL,
139 artifactMapper.get(),
143 // Blow it away to clear any locks that might be held.
144 delete artifactMapper.release();
146 // Try and map to metadata (again).
147 // Once the metadata layer is in the SAML core, the repetition should be fixed.
148 const IEntityDescriptor* provider=m.lookup(bpr.assertion->getIssuer());
149 if (!provider && bpr.authnStatement->getSubject()->getNameIdentifier() &&
150 bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier())
151 provider=m.lookup(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
153 const IIDPSSODescriptor* IDP=provider->getIDPSSODescriptor(
154 minorVersion==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
159 // This isn't likely, since the profile must have found a role.
161 MetadataException ex("Unable to locate role-specific metadata for identity provider.");
162 annotateException(&ex,provider); // throws it
165 // Maybe verify the origin address....
166 if (checkIPAddress) {
167 log->debug("verifying client address");
168 // Verify the client address exists
169 const XMLCh* wip = bpr.authnStatement->getSubjectIP();
171 // Verify the client address matches authentication
172 auto_ptr_char this_ip(wip);
173 if (strcmp(ip, this_ip.get())) {
174 FatalProfileException ex(
175 SESSION_E_ADDRESSMISMATCH,
176 "Your client's current address ($1) differs from the one used when you authenticated "
177 "to your identity provider. To correct this problem, you may need to bypass a proxy server. "
178 "Please contact your local support staff or help desk for assistance.",
181 annotateException(&ex,role); // throws it
186 // Verify condition(s) on authentication assertion.
187 // Attribute assertions get filtered later, essentially just like an AAP.
188 Iterator<SAMLCondition*> conditions=bpr.assertion->getConditions();
189 while (conditions.hasNext()) {
190 SAMLCondition* cond=conditions.next();
191 const SAMLAudienceRestrictionCondition* ac=dynamic_cast<const SAMLAudienceRestrictionCondition*>(cond);
195 log->error("Unrecognized Condition in authentication assertion (%s), tossing it.",os.str().c_str());
196 FatalProfileException ex("Unable to create session due to unrecognized condition in authentication assertion.");
197 annotateException(&ex,role); // throws it
199 else if (!ac->eval(app->getAudiences())) {
202 log->error("Unacceptable AudienceRestrictionCondition in authentication assertion (%s), tossing it.",os.str().c_str());
203 FatalProfileException ex("Unable to create session due to unacceptable AudienceRestrictionCondition in authentication assertion.");
204 annotateException(&ex,role); // throws it
208 catch (SAMLException&) {
214 log->error("caught unknown exception");
216 SAMLException e("An unexpected error occurred while creating your session.");
217 annotateException(&e,role);
221 // It passes all our tests -- create a new session.
222 log->info("creating new session");
224 // Are attributes present?
225 bool attributesPushed=false;
226 Iterator<SAMLAssertion*> assertions=bpr.response->getAssertions();
227 while (!attributesPushed && assertions.hasNext()) {
228 Iterator<SAMLStatement*> statements=assertions.next()->getStatements();
229 while (!attributesPushed && statements.hasNext()) {
230 if (dynamic_cast<SAMLAttributeStatement*>(statements.next()))
231 attributesPushed=true;
235 auto_ptr_char oname(role->getEntityDescriptor()->getId());
236 auto_ptr_char hname(bpr.authnStatement->getSubject()->getNameIdentifier()->getName());
239 // Create a new session key.
240 cookie = conf->getSessionCache()->generateKey();
242 // Insert into cache.
243 auto_ptr<SAMLAuthenticationStatement> as(static_cast<SAMLAuthenticationStatement*>(bpr.authnStatement->clone()));
244 conf->getSessionCache()->insert(
248 (bpr.profile==SAMLBrowserProfile::Post) ?
249 (minorVersion==1 ? SAML11_POST : SAML10_POST) : (minorVersion==1 ? SAML11_ARTIFACT : SAML10_ARTIFACT),
252 (attributesPushed ? bpr.response : NULL),
255 as.release(); // owned by cache now
257 catch (SAMLException&) {
263 log->error("caught unknown exception");
265 SAMLException e("An unexpected error occurred while creating your session.");
266 annotateException(&e,role);
271 provider_id = oname.get();
273 // Maybe delete the response...
274 if (!attributesPushed)
277 log->debug("new session id: %s", cookie.c_str());
279 // Transaction Logging
280 stc.getTransactionLog().infoStream() <<
281 "New session (ID: " <<
283 ") with (applicationId: " <<
285 ") for principal from (IdP: " <<
287 ") at (ClientAddress: " <<
289 ") with (NameIdentifier: " <<
293 stc.releaseTransactionLog();
296 void MemoryListener::sessionGet(
297 const IApplication* app,
300 ISessionCacheEntry** pentry
304 saml::NDC ndc("sessionGet");
308 log->debug("checking for session: %s@%s", cookie, ip);
310 // See if the session exists...
312 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
313 IConfig* conf=stc.getINI();
314 log->debug("application: %s", app->getId());
316 bool consistentIPAddress=true;
317 int lifetime=0,timeout=0;
318 const IPropertySet* props=app->getPropertySet("Sessions");
320 pair<bool,unsigned int> p=props->getUnsignedInt("lifetime");
323 p=props->getUnsignedInt("timeout");
326 pair<bool,bool> pcheck=props->getBool("consistentAddress");
328 consistentIPAddress = pcheck.second;
331 *pentry = conf->getSessionCache()->find(cookie,app);
333 // If not, leave now..
335 log->debug("session not found");
336 throw InvalidSessionException("No session exists for key value ($session_id)",namedparams(1,"session_id",cookie));
339 // TEST the session...
341 // Verify the address is the same
342 if (consistentIPAddress) {
343 log->debug("Checking address against %s", (*pentry)->getClientAddress());
344 if (strcmp(ip, (*pentry)->getClientAddress())) {
345 log->debug("client address mismatch");
346 InvalidSessionException ex(
347 SESSION_E_ADDRESSMISMATCH,
348 "Your IP address ($1) does not match the address recorded at the time the session was established.",
351 Metadata m(app->getMetadataProviders());
352 annotateException(&ex,m.lookup((*pentry)->getProviderId())); // throws it
356 // and that the session is still valid...
357 if (!(*pentry)->isValid(lifetime,timeout)) {
358 log->debug("session expired");
359 InvalidSessionException ex(SESSION_E_EXPIRED, "Your session has expired, and you must re-authenticate.");
360 Metadata m(app->getMetadataProviders());
361 annotateException(&ex,m.lookup((*pentry)->getProviderId())); // throws it
364 catch (SAMLException&) {
367 conf->getSessionCache()->remove(cookie);
369 // Transaction Logging
370 stc.getTransactionLog().infoStream() <<
371 "Destroyed invalid session (ID: " <<
373 ") with (applicationId: " <<
375 "), request was from (ClientAddress: " <<
378 stc.releaseTransactionLog();
382 log->error("caught unknown exception");
384 InvalidSessionException ex("An unexpected error occurred while validating your session, and you must re-authenticate.");
385 Metadata m(app->getMetadataProviders());
386 annotateException(&ex,m.lookup((*pentry)->getProviderId()),false);
390 conf->getSessionCache()->remove(cookie);
392 // Transaction Logging
393 stc.getTransactionLog().infoStream() <<
394 "Destroyed invalid session (ID: " <<
396 ") with (applicationId: " <<
398 "), request was from (ClientAddress: " <<
401 stc.releaseTransactionLog();
410 log->debug("session ok");
413 void MemoryListener::sessionEnd(
414 const IApplication* application,
419 saml::NDC ndc("sessionEnd");
422 log->debug("removing session: %s", cookie);
424 STConfig& stc=static_cast<STConfig&>(ShibTargetConfig::getConfig());
425 stc.getINI()->getSessionCache()->remove(cookie);
427 // Transaction Logging
428 stc.getTransactionLog().infoStream() << "Destroyed session (ID: " << cookie << ")";
429 stc.releaseTransactionLog();
432 void MemoryListener::ping(int& i) const