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 * listener.cpp -- implementation of IListener functional methods that includes ADFS support
27 #include <log4cpp/FixedContextCategory.hh>
28 #include <xercesc/framework/MemBufInputSource.hpp>
31 using namespace log4cpp;
33 using namespace shibboleth;
34 using namespace shibtarget;
38 class ADFSListener : public virtual IListener
41 ADFSListener(const DOMElement* e) : log(&Category::getInstance(ADFS_LOGCAT".Listener")) {}
44 bool create(ShibSocket& s) const {return true;}
45 bool bind(ShibSocket& s, bool force=false) const {return true;}
46 bool connect(ShibSocket& s) const {return true;}
47 bool close(ShibSocket& s) const {return true;}
48 bool accept(ShibSocket& listener, ShibSocket& s) const {return true;}
51 const IApplication* application,
52 int supported_profiles,
53 const char* recipient,
58 std::string& provider_id
62 const IApplication* application,
65 ISessionCacheEntry** pentry
69 const IApplication* application,
73 void ping(int& i) const;
80 IPlugIn* ADFSListenerFactory(const DOMElement* e)
82 return new ADFSListener(e);
85 void ADFSListener::sessionNew(
86 const IApplication* app,
87 int supported_profiles,
88 const char* recipient,
97 saml::NDC ndc("sessionNew");
100 log->debug("creating session for %s", ip);
101 log->debug("recipient: %s", recipient);
102 log->debug("application: %s", app->getId());
104 auto_ptr_XMLCh wrecipient(recipient);
106 // Access the application config. It's already locked behind us.
107 ShibTargetConfig& stc=ShibTargetConfig::getConfig();
108 IConfig* conf=stc.getINI();
110 bool checkIPAddress=true;
111 const IPropertySet* props=app->getPropertySet("Sessions");
113 pair<bool,bool> pcheck=props->getBool("checkAddress");
115 checkIPAddress = pcheck.second;
118 pair<bool,bool> checkReplay=pair<bool,bool>(false,false);
119 props=app->getPropertySet("Sessions");
121 checkReplay=props->getBool("checkReplay");
123 const IRoleDescriptor* role=NULL;
124 Metadata m(app->getMetadataProviders());
127 SAMLBrowserProfile::BrowserProfileResponse bpr;
129 // For now, just branch off to handle ADFS inline, I'll wrap all this up later.
130 if (supported_profiles & ADFS_SSO) {
131 log->debug("executing ADFS profile...");
132 CgiParse parser(packet,strlen(packet));
133 const char* param=parser.get_value("wa");
134 if (param && !strcmp(param,"wsignin1.0")) {
136 param=parser.get_value("wresult");
138 throw FatalProfileException("ADFS profile required wresult parameter not found");
140 log->debug("decoded ADFS Token response:\n%s",param);
141 // wresult should carry an wst:RequestSecurityTokenResponse message so we parse it manually
142 DOMDocument* rdoc=NULL;
145 static const XMLCh systemId[]={chLatin_W, chLatin_S, chDash, chLatin_T, chLatin_r, chLatin_u, chLatin_s, chLatin_t, chNull};
146 MemBufInputSource membufsrc(reinterpret_cast<const XMLByte*>(param),strlen(param),systemId,false);
147 Wrapper4InputSource dsrc(&membufsrc,false);
150 // Process the wrapper and extract the assertion.
151 if (saml::XML::isElementNamed(rdoc->getDocumentElement(),adfs::XML::WSTRUST_NS,ADFS_L(RequestSecurityTokenResponse))) {
153 saml::XML::getFirstChildElement(rdoc->getDocumentElement(),adfs::XML::WSTRUST_NS,ADFS_L(RequestedSecurityToken));
155 e=saml::XML::getFirstChildElement(e,saml::XML::SAML_NS,L(Assertion));
158 // Wrap the assertion DOM in a dummy samlp:Response for subsequent processing.
159 // We have to manually create the Response DOM first in order to avoid
160 // corrupting the namespace declarations in the Assertion.
162 static const XMLCh One[]={chDigit_1, chNull};
163 static const XMLCh dummyID[] = {chLatin_A, chLatin_D, chLatin_F, chLatin_S, chNull};
164 static const XMLCh samlp_Success[]=
165 { chLatin_s, chLatin_a, chLatin_m, chLatin_l, chLatin_p, chColon,
166 chLatin_S, chLatin_u, chLatin_c, chLatin_c, chLatin_e, chLatin_s, chLatin_s, chNull };
167 DOMElement* rdom=rdoc->createElementNS(saml::XML::SAMLP_NS,L(Response));
168 rdom->setAttributeNS(saml::XML::XMLNS_NS,L_QNAME(xmlns,samlp),saml::XML::SAMLP_NS);
169 rdom->setAttributeNS(saml::XML::XMLNS_NS,L(xmlns),saml::XML::SAMLP_NS);
170 rdom->setAttributeNS(NULL,L(MajorVersion),One);
171 rdom->setAttributeNS(NULL,L(MinorVersion),One);
172 rdom->setAttributeNS(NULL,L(ResponseID),dummyID);
173 SAMLDateTime issued(time(NULL));
174 issued.parseDateTime();
175 rdom->setAttributeNS(NULL,L(IssueInstant),issued.getRawData());
176 DOMElement* status=rdoc->createElementNS(saml::XML::SAMLP_NS,L(Status));
177 rdom->appendChild(status);
178 DOMElement* code=rdoc->createElementNS(saml::XML::SAMLP_NS,L(StatusCode));
179 code->setAttributeNS(NULL,L(Value),samlp_Success);
180 status->appendChild(code);
181 rdom->appendChild(e); // append the assertion
182 auto_ptr<SAMLResponse> response(new SAMLResponse(rdom));
183 response->setDocument(rdoc); // give the Document to the response object
184 // root the response in the document so the signature will verify
185 rdoc->replaceChild(response->toDOM(rdoc,false),rdoc->getDocumentElement());
188 // Try and map to metadata.
189 SAMLAssertion* assertion=response->getAssertions().next();
190 const IEntityDescriptor* provider=m.lookup(assertion->getIssuer());
192 role=provider->getIDPSSODescriptor(adfs::XML::WSFED_NS);
194 MetadataException ex("unable to locate role-specific metadata for identity provider.");
195 annotateException(&ex,provider); // throws it
199 // Check over the assertion.
200 SAMLAuthenticationStatement* authnStatement=checkAssertionProfile(assertion);
202 if (!checkReplay.first || checkReplay.second) {
203 auto_ptr_char id(assertion->getId());
204 string key(id.get());
206 if (!conf->getReplayCache()->check(key.c_str(),assertion->getNotOnOrAfter()->getEpoch()))
207 throw ReplayedAssertionException(string("Rejecting replayed assertion ID (") + id.get() + ")");
211 log->debug("passing signed ADFS assertion to trust layer");
212 Trust t(app->getTrustProviders());
213 if (!t.validate(*assertion,role)) {
214 log->error("unable to verify signed authentication assertion");
215 throw TrustException("unable to verify signed authentication assertion");
218 // Now dummy up the SAML profile response wrapper.
219 param=parser.get_value("wctx");
222 bpr.profile=SAMLBrowserProfile::Post; // not really, but...
223 bpr.response=response.release();
224 bpr.assertion=assertion;
225 bpr.authnStatement=authnStatement;
227 catch (SAMLException& ex) {
228 annotateException(&ex,role); // throws it
239 if (rdoc) rdoc->release();
243 if (bADFS && !bpr.response)
244 throw FatalProfileException("ADFS profile was indicated, but processing was unsuccesful");
247 // If ADFS wasn't used, proceed to SAML processing up until we reach a common point.
248 int minorVersion = 1;
252 if (supported_profiles & SAML11_POST || supported_profiles & SAML10_POST)
253 allowed |= SAMLBrowserProfile::Post;
254 if (supported_profiles & SAML11_ARTIFACT || supported_profiles & SAML10_ARTIFACT)
255 allowed |= SAMLBrowserProfile::Artifact;
256 minorVersion=(supported_profiles & SAML11_ARTIFACT || supported_profiles & SAML11_POST) ? 1 : 0;
258 auto_ptr<SAMLBrowserProfile::ArtifactMapper> artifactMapper(app->getArtifactMapper());
260 // Try and run the profile.
261 log->debug("executing browser profile...");
262 bpr=app->getBrowserProfile()->receive(
266 (!checkReplay.first || checkReplay.second) ? conf->getReplayCache() : NULL,
267 artifactMapper.get(),
271 // Blow it away to clear any locks that might be held.
272 delete artifactMapper.release();
274 // Try and map to metadata (again).
275 // Once the metadata layer is in the SAML core, the repetition should be fixed.
276 const IEntityDescriptor* provider=m.lookup(bpr.assertion->getIssuer());
277 if (!provider && bpr.authnStatement->getSubject()->getNameIdentifier() &&
278 bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier())
279 provider=m.lookup(bpr.authnStatement->getSubject()->getNameIdentifier()->getNameQualifier());
281 const IIDPSSODescriptor* IDP=provider->getIDPSSODescriptor(
282 minorVersion==1 ? saml::XML::SAML11_PROTOCOL_ENUM : saml::XML::SAML10_PROTOCOL_ENUM
287 // This isn't likely, since the profile must have found a role.
289 MetadataException ex("Unable to locate role-specific metadata for identity provider.");
290 annotateException(&ex,provider); // throws it
294 // At this point, we link back up and do the same work for ADFS and SAML.
296 // Maybe verify the origin address....
297 if (checkIPAddress) {
298 log->debug("verifying client address");
299 // Verify the client address exists
300 const XMLCh* wip = bpr.authnStatement->getSubjectIP();
302 // Verify the client address matches authentication
303 auto_ptr_char this_ip(wip);
304 if (strcmp(ip, this_ip.get())) {
305 FatalProfileException ex(
306 SESSION_E_ADDRESSMISMATCH,
307 "Your client's current address ($1) differs from the one used when you authenticated "
308 "to your identity provider. To correct this problem, you may need to bypass a proxy server. "
309 "Please contact your local support staff or help desk for assistance.",
312 annotateException(&ex,role); // throws it
317 // Verify condition(s) on authentication assertion.
318 // Attribute assertions get filtered later by the AAP.
319 Iterator<SAMLCondition*> conditions=bpr.assertion->getConditions();
320 while (conditions.hasNext()) {
321 SAMLCondition* cond=conditions.next();
322 const SAMLAudienceRestrictionCondition* ac=dynamic_cast<const SAMLAudienceRestrictionCondition*>(cond);
326 log->error("Unrecognized Condition in authentication assertion (%s), tossing it.",os.str().c_str());
327 FatalProfileException ex("unable to create session due to unrecognized condition in authentication assertion.");
328 annotateException(&ex,role); // throws it
330 else if (!ac->eval(app->getAudiences())) {
333 log->error("Unacceptable AudienceRestrictionCondition in authentication assertion (%s), tossing it.",os.str().c_str());
334 FatalProfileException ex("unable to create session due to unacceptable AudienceRestrictionCondition in authentication assertion.");
335 annotateException(&ex,role); // throws it
339 catch (SAMLException&) {
344 log->error("caught unknown exception");
349 SAMLException e("An unexpected error occurred while creating your session.");
350 annotateException(&e,role);
354 // It passes all our tests -- create a new session.
355 log->info("creating new session");
357 // Are attributes present?
358 bool attributesPushed=false;
359 Iterator<SAMLAssertion*> assertions=bpr.response->getAssertions();
360 while (!attributesPushed && assertions.hasNext()) {
361 Iterator<SAMLStatement*> statements=assertions.next()->getStatements();
362 while (!attributesPushed && statements.hasNext()) {
363 if (dynamic_cast<SAMLAttributeStatement*>(statements.next()))
364 attributesPushed=true;
368 auto_ptr_char oname(role->getEntityDescriptor()->getId());
369 auto_ptr_char hname(bpr.authnStatement->getSubject()->getNameIdentifier()->getName());
372 // Create a new session key.
373 cookie = conf->getSessionCache()->generateKey();
375 // Insert into cache.
376 auto_ptr<SAMLAuthenticationStatement> as(static_cast<SAMLAuthenticationStatement*>(bpr.authnStatement->clone()));
377 conf->getSessionCache()->insert(
382 ((bpr.profile==SAMLBrowserProfile::Post) ?
383 (minorVersion==1 ? SAML11_POST : SAML10_POST) : (minorVersion==1 ? SAML11_ARTIFACT : SAML10_ARTIFACT))),
386 (attributesPushed ? bpr.response : NULL),
389 as.release(); // owned by cache now
391 catch (SAMLException&) {
396 log->error("caught unknown exception");
401 SAMLException e("An unexpected error occurred while creating your session.");
402 annotateException(&e,role);
407 provider_id = oname.get();
409 // Maybe delete the response...
410 if (!attributesPushed)
413 log->debug("new session id: %s", cookie.c_str());
415 // Transaction Logging
416 FixedContextCategory tranLog(SHIBTRAN_LOGCAT);
417 tranLog.infoStream() <<
418 "New session (ID: " <<
420 ") with (applicationId: " <<
422 ") for principal from (IdP: " <<
424 ") at (ClientAddress: " <<
426 ") with (NameIdentifier: " <<
429 //stc.releaseTransactionLog();
432 void ADFSListener::sessionGet(
433 const IApplication* app,
436 ISessionCacheEntry** pentry
439 g_MemoryListener->sessionGet(app,cookie,ip,pentry);
442 void ADFSListener::sessionEnd(
443 const IApplication* application,
447 g_MemoryListener->sessionEnd(application,cookie);
450 void ADFSListener::ping(int& i) const
452 g_MemoryListener->ping(i);