https://issues.shibboleth.net/jira/browse/SSPCPP-103
[shibboleth/cpp-sp.git] / adfs / listener.cpp
index 5ed161b..5b6a0a1 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *  Copyright 2001-2005 Internet2
+ *  Copyright 2001-2009 Internet2
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <xercesc/framework/MemBufInputSource.hpp>
 
 using namespace std;
-using namespace log4cpp;
 using namespace saml;
 using namespace shibboleth;
 using namespace shibtarget;
 using namespace adfs;
+using namespace adfs::logging;
 
 namespace {
     class ADFSListener : public virtual IListener
@@ -153,9 +153,39 @@ void ADFSListener::sessionNew(
                     if (e) {
                         e=saml::XML::getFirstChildElement(e,saml::XML::SAML_NS,L(Assertion));
                         if (e) {
-                            auto_ptr<SAMLAssertion> assertion(new SAMLAssertion(e));
+
+                            // Wrap the assertion DOM in a dummy samlp:Response for subsequent processing.
+                            // We have to manually create the Response DOM first in order to avoid
+                            // corrupting the namespace declarations in the Assertion.
+
+                            static const XMLCh One[]={chDigit_1, chNull};
+                            static const XMLCh dummyID[] = {chLatin_A, chLatin_D, chLatin_F, chLatin_S, chNull};
+                            static const XMLCh samlp_Success[]=
+                            { chLatin_s, chLatin_a, chLatin_m, chLatin_l, chLatin_p, chColon,
+                              chLatin_S, chLatin_u, chLatin_c, chLatin_c, chLatin_e, chLatin_s, chLatin_s, chNull };
+                            DOMElement* rdom=rdoc->createElementNS(saml::XML::SAMLP_NS,L(Response));
+                            rdom->setAttributeNS(saml::XML::XMLNS_NS,L_QNAME(xmlns,samlp),saml::XML::SAMLP_NS);
+                            rdom->setAttributeNS(saml::XML::XMLNS_NS,L(xmlns),saml::XML::SAMLP_NS);
+                            rdom->setAttributeNS(NULL,L(MajorVersion),One);
+                            rdom->setAttributeNS(NULL,L(MinorVersion),One);
+                            rdom->setAttributeNS(NULL,L(ResponseID),dummyID);
+                            SAMLDateTime issued(time(NULL));
+                            issued.parseDateTime();
+                            rdom->setAttributeNS(NULL,L(IssueInstant),issued.getRawData());
+                            DOMElement* status=rdoc->createElementNS(saml::XML::SAMLP_NS,L(Status));
+                            rdom->appendChild(status);
+                            DOMElement* code=rdoc->createElementNS(saml::XML::SAMLP_NS,L(StatusCode));
+                            code->setAttributeNS(NULL,L(Value),samlp_Success);
+                            status->appendChild(code);
+                            rdom->appendChild(e);   // append the assertion
+                            auto_ptr<SAMLResponse> response(new SAMLResponse(rdom));
+                            response->setDocument(rdoc);    // give the Document to the response object
+                            // root the response in the document so the signature will verify
+                            rdoc->replaceChild(response->toDOM(rdoc,false),rdoc->getDocumentElement());
+                            rdoc=NULL;
                             
                             // Try and map to metadata.
+                            SAMLAssertion* assertion=response->getAssertions().next();
                             const IEntityDescriptor* provider=m.lookup(assertion->getIssuer());
                             if (provider)
                                 role=provider->getIDPSSODescriptor(adfs::XML::WSFED_NS);
@@ -166,7 +196,7 @@ void ADFSListener::sessionNew(
                             
                             try {
                                 // Check over the assertion.
-                                SAMLAuthenticationStatement* authnStatement=checkAssertionProfile(assertion.get());
+                                SAMLAuthenticationStatement* authnStatement=checkAssertionProfile(assertion);
 
                                 if (!checkReplay.first || checkReplay.second) {
                                     auto_ptr_char id(assertion->getId());
@@ -179,19 +209,11 @@ void ADFSListener::sessionNew(
                                 // Check signature.
                                 log->debug("passing signed ADFS assertion to trust layer");
                                 Trust t(app->getTrustProviders());
-                                if (!t.validate(*(assertion.get()),role)) {
-                                    log->error("unable to verify signed authentication assertion");
+                                if (!t.validate(*assertion,role)) {
+                                    log->error("unable to verify signed ADFS assertion");
                                     throw TrustException("unable to verify signed authentication assertion");
                                 }
-                                
-                                // Wrap the assertion in a dummy samlp:Response for subsequent processing.
-                                // Generate the Response DOM using the assertion's document and then
-                                // transfer ownership of the tree to the Response.
-                                auto_ptr<SAMLResponse> response(new SAMLResponse());
-                                response->addAssertion(assertion.release());
-                                response->toDOM(rdoc);
-                                response->setDocument(rdoc);
-                                rdoc=NULL;
+                                log->info("verified digital signature over ADFS assertion");
                                 
                                 // Now dummy up the SAML profile response wrapper.
                                 param=parser.get_value("wctx");
@@ -199,7 +221,7 @@ void ADFSListener::sessionNew(
                                     bpr.TARGET=param;
                                 bpr.profile=SAMLBrowserProfile::Post;   // not really, but...
                                 bpr.response=response.release();
-                                bpr.assertion=response->getAssertions().next();
+                                bpr.assertion=assertion;
                                 bpr.authnStatement=authnStatement;
                             }
                             catch (SAMLException& ex) {
@@ -278,7 +300,7 @@ void ADFSListener::sessionNew(
             const XMLCh* wip = bpr.authnStatement->getSubjectIP();
             if (wip && *wip) {
                 // Verify the client address matches authentication
-                auto_ptr_char this_ip(ip);
+                auto_ptr_char this_ip(wip);
                 if (strcmp(ip, this_ip.get())) {
                     FatalProfileException ex(
                         SESSION_E_ADDRESSMISMATCH,
@@ -330,7 +352,6 @@ void ADFSListener::sessionNew(
     }
 
     // It passes all our tests -- create a new session.
-    log->info("creating new session");
 
     // Are attributes present?
     bool attributesPushed=false;
@@ -344,7 +365,11 @@ void ADFSListener::sessionNew(
     }
 
     auto_ptr_char oname(role->getEntityDescriptor()->getId());
-    auto_ptr_char hname(bpr.authnStatement->getSubject()->getNameIdentifier()->getName());
+    auto_ptr_char hname(
+        bpr.authnStatement->getSubject()->getNameIdentifier() ?
+            bpr.authnStatement->getSubject()->getNameIdentifier()->getName() :
+                NULL
+        );
 
     try {
         // Create a new session key.
@@ -391,7 +416,8 @@ void ADFSListener::sessionNew(
     log->debug("new session id: %s", cookie.c_str());
   
     // Transaction Logging
-    Category::getInstance(SHIBTRAN_LOGCAT).infoStream() <<
+    FixedContextCategory tranLog(SHIBTRAN_LOGCAT);
+    tranLog.infoStream() <<
         "New session (ID: " <<
             cookie <<
         ") with (applicationId: " <<
@@ -401,7 +427,7 @@ void ADFSListener::sessionNew(
         ") at (ClientAddress: " <<
             ip <<
         ") with (NameIdentifier: " <<
-            hname.get() <<
+            (hname.get() ? hname.get() : "none") <<
         ")";
     //stc.releaseTransactionLog();
 }
@@ -413,113 +439,7 @@ void ADFSListener::sessionGet(
     ISessionCacheEntry** pentry
     ) const
 {
-#ifdef _DEBUG
-    saml::NDC ndc("sessionGet");
-#endif
-
-    *pentry=NULL;
-    log->debug("checking for session: %s@%s", cookie, ip);
-
-    // See if the session exists...
-
-    ShibTargetConfig& stc=ShibTargetConfig::getConfig();
-    IConfig* conf=stc.getINI();
-    log->debug("application: %s", app->getId());
-
-    bool checkIPAddress=true;
-    int lifetime=0,timeout=0;
-    const IPropertySet* props=app->getPropertySet("Sessions");
-    if (props) {
-        pair<bool,unsigned int> p=props->getUnsignedInt("lifetime");
-        if (p.first)
-            lifetime = p.second;
-        p=props->getUnsignedInt("timeout");
-        if (p.first)
-            timeout = p.second;
-        pair<bool,bool> pcheck=props->getBool("checkAddress");
-        if (pcheck.first)
-            checkIPAddress = pcheck.second;
-    }
-    
-    *pentry = conf->getSessionCache()->find(cookie,app);
-
-    // If not, leave now..
-    if (!*pentry) {
-        log->debug("session not found");
-        throw InvalidSessionException("No session exists for key value ($session_id)",namedparams(1,"session_id",cookie));
-    }
-
-    // TEST the session...
-    try {
-        // Verify the address is the same
-        if (checkIPAddress) {
-            log->debug("Checking address against %s", (*pentry)->getClientAddress());
-            if (strcmp(ip, (*pentry)->getClientAddress())) {
-                log->debug("client address mismatch");
-                InvalidSessionException ex(
-                    SESSION_E_ADDRESSMISMATCH,
-                    "Your IP address (%1) does not match the address recorded at the time the session was established.",
-                    params(1,ip)
-                    );
-                Metadata m(app->getMetadataProviders());
-                annotateException(&ex,m.lookup((*pentry)->getProviderId())); // throws it
-            }
-        }
-
-        // and that the session is still valid...
-        if (!(*pentry)->isValid(lifetime,timeout)) {
-            log->debug("session expired");
-            InvalidSessionException ex(SESSION_E_EXPIRED, "Your session has expired, and you must re-authenticate.");
-            Metadata m(app->getMetadataProviders());
-            annotateException(&ex,m.lookup((*pentry)->getProviderId())); // throws it
-        }
-    }
-    catch (SAMLException&) {
-        (*pentry)->unlock();
-        *pentry=NULL;
-        conf->getSessionCache()->remove(cookie);
-      
-        // Transaction Logging
-        Category::getInstance(SHIBTRAN_LOGCAT).infoStream() <<
-            "Destroyed invalid session (ID: " <<
-                cookie <<
-            ") with (applicationId: " <<
-                app->getId() <<
-            "), request was from (ClientAddress: " <<
-                ip <<
-            ")";
-        //stc.releaseTransactionLog();
-        throw;
-    }
-    catch (...) {
-        log->error("caught unknown exception");
-#ifndef _DEBUG
-        InvalidSessionException ex("An unexpected error occurred while validating your session, and you must re-authenticate.");
-        Metadata m(app->getMetadataProviders());
-        annotateException(&ex,m.lookup((*pentry)->getProviderId()),false);
-#endif
-        (*pentry)->unlock();
-        *pentry=NULL;
-        conf->getSessionCache()->remove(cookie);
-
-        // Transaction Logging
-        Category::getInstance(SHIBTRAN_LOGCAT).infoStream() <<
-            "Destroyed invalid session (ID: " <<
-                cookie <<
-            ") with (applicationId: " <<
-                app->getId() <<
-            "), request was from (ClientAddress: " <<
-                ip <<
-            ")";
-        //stc.releaseTransactionLog();
-#ifdef _DEBUG
-        throw;
-#else
-        ex.raise();
-#endif
-    }
-
-    log->debug("session ok");
+    g_MemoryListener->sessionGet(app,cookie,ip,pentry);
 }
 
 void ADFSListener::sessionEnd(
@@ -527,21 +447,10 @@ void ADFSListener::sessionEnd(
     const char* cookie
     ) const
 {
-#ifdef _DEBUG
-    saml::NDC ndc("sessionEnd");
-#endif
-
-    log->debug("removing session: %s", cookie);
-
-    ShibTargetConfig& stc=ShibTargetConfig::getConfig();
-    stc.getINI()->getSessionCache()->remove(cookie);
-  
-    // Transaction Logging
-    Category::getInstance(SHIBTRAN_LOGCAT).infoStream() << "Destroyed session (ID: " << cookie << ")";
-    //stc.releaseTransactionLog();
+    g_MemoryListener->sessionEnd(application,cookie);
 }
 
 void ADFSListener::ping(int& i) const
 {
-    i++;
+    g_MemoryListener->ping(i);
 }