Fixes for Heimdal (macOS) builds from Stefan.
[mech_eap.git] / mech_eap / util_shib.cpp
index 15a8b44..4ba70e0 100644 (file)
  * Local attribute provider implementation.
  */
 
+#include "gssapiP_eap.h"
+
 #include <xmltooling/XMLObject.h>
+#ifndef HAVE_OPENSAML
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/ParserPool.h>
+#endif
 
 #include <saml/saml2/core/Assertions.h>
 
 #include <shibsp/exceptions.h>
 #include <shibsp/attribute/SimpleAttribute.h>
+#include <shibsp/attribute/BinaryAttribute.h>
+#include <shibsp/attribute/ScopedAttribute.h>
 #include <shibresolver/resolver.h>
 
 #include <sstream>
 
-#include "gssapiP_eap.h"
-
 using namespace shibsp;
 using namespace shibresolver;
-using namespace opensaml::saml2md;
-using namespace opensaml;
 using namespace xmltooling;
 using namespace std;
+#ifdef HAVE_OPENSAML
+using namespace opensaml::saml2md;
+using namespace opensaml;
+#else
+using namespace xercesc;
+#endif
+
+
+namespace {
+
+
+    class ShibFinalizer {
+    public:
+
+        static bool isShibInitialized() {return shibInitialized;}
+        static void createSingleton();
+
+    private:
+        ShibFinalizer(): is_extra(false) {
+            if (shibInitialized) {
+                // This should never, ever happen. Initialization is (supposed to be)
+                // funneled through a single thread, so there should be no race
+                // conditions here. And only this class sets this flag, and there's
+                // only a single instance of this class.
+                wpa_printf(MSG_ERROR, "### ShibFinalizer::ShibFinalizer(): Attempt to construct an extraneous instance!");
+                is_extra = true;
+            }
+            else {
+                wpa_printf(MSG_INFO, "### ShibFinalizer::ShibFinalizer(): Constructing");
+                shibInitialized = true;
+            }
+        }
+
+        ~ShibFinalizer() {
+            if (!is_extra) {
+                wpa_printf(MSG_INFO, "### ShibFinalizer::~ShibFinalizer(): Destructing");
+                gss_eap_shib_attr_provider::finalize();
+                shibInitialized = false;
+            }
+            else {
+                wpa_printf(MSG_INFO, "### ShibFinalizer::~ShibFinalizer(): This was an extraneous instance; not destructing anything.");
+            }
+        }
+
+        bool is_extra;
+        static bool shibInitialized;
+    };
+}
+
+
+bool ShibFinalizer::shibInitialized = false;
+
+void ShibFinalizer::createSingleton() {
+    // This object's constructor is invoked on the first call to this method.
+    // At exit, its destructor will terminate Shibboleth.
+    static ShibFinalizer finalizer;
+}
+
 
 gss_eap_shib_attr_provider::gss_eap_shib_attr_provider(void)
 {
@@ -141,12 +203,32 @@ gss_eap_shib_attr_provider::initWithGssContext(const gss_eap_attr_ctx *manager,
         gss_release_buffer(&minor, &mechName);
     }
 
+#ifdef HAVE_OPENSAML
     const gss_eap_saml_assertion_provider *saml;
     saml = static_cast<const gss_eap_saml_assertion_provider *>
         (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION));
     if (saml != NULL && saml->getAssertion() != NULL) {
         resolver->addToken(saml->getAssertion());
     }
+#else
+    /* If no OpenSAML, parse the XML assertion explicitly */
+    const gss_eap_radius_attr_provider *radius;
+    int authenticated, complete;
+    gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
+    gss_eap_attrid attrid(VENDORPEC_UKERNA, PW_SAML_AAA_ASSERTION);
+
+    radius = static_cast<const gss_eap_radius_attr_provider *>
+        (m_manager->getProvider(ATTR_TYPE_RADIUS));
+    if (radius != NULL &&
+        radius->getFragmentedAttribute(attrid, &authenticated, &complete, &value)) {
+        string str((char *)value.value, value.length);
+        istringstream istream(str);
+        DOMDocument *doc = XMLToolingConfig::getConfig().getParser().parse(istream);
+        const XMLObjectBuilder *b = XMLObjectBuilder::getBuilder(doc->getDocumentElement());
+        resolver->addToken(b->buildFromDocument(doc));
+        gss_release_buffer(&minor, &value);
+    }
+#endif /* HAVE_OPENSAML */
 
     try {
         resolver->resolve();
@@ -167,7 +249,7 @@ gss_eap_shib_attr_provider::getAttributeIndex(const gss_buffer_t attr) const
 {
     int i = 0;
 
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     for (vector<Attribute *>::const_iterator a = m_attributes.begin();
          a != m_attributes.end();
@@ -193,9 +275,9 @@ gss_eap_shib_attr_provider::setAttribute(int complete GSSEAP_UNUSED,
 {
     string attrStr((char *)attr->value, attr->length);
     vector <string> ids(1, attrStr);
-    SimpleAttribute *a = new SimpleAttribute(ids);
+    BinaryAttribute *a = new BinaryAttribute(ids);
 
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     if (value->length != 0) {
         string valueStr((char *)value->value, value->length);
@@ -214,7 +296,7 @@ gss_eap_shib_attr_provider::deleteAttribute(const gss_buffer_t attr)
 {
     int i;
 
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     i = getAttributeIndex(attr);
     if (i >= 0)
@@ -229,7 +311,7 @@ bool
 gss_eap_shib_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
                                               void *data) const
 {
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     for (vector<Attribute*>::const_iterator a = m_attributes.begin();
         a != m_attributes.end();
@@ -252,7 +334,7 @@ gss_eap_shib_attr_provider::getAttribute(const gss_buffer_t attr) const
 {
     const Attribute *ret = NULL;
 
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     for (vector<Attribute *>::const_iterator a = m_attributes.begin();
          a != m_attributes.end();
@@ -283,10 +365,12 @@ gss_eap_shib_attr_provider::getAttribute(const gss_buffer_t attr,
                                          int *more) const
 {
     const Attribute *shibAttr = NULL;
-    gss_buffer_desc buf;
+    const BinaryAttribute *binaryAttr;
+    gss_buffer_desc valueBuf = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc displayValueBuf = GSS_C_EMPTY_BUFFER;
     int nvalues, i = *more;
 
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     *more = 0;
 
@@ -301,39 +385,34 @@ gss_eap_shib_attr_provider::getAttribute(const gss_buffer_t attr,
     if (i >= nvalues)
         return false;
 
-    buf.value = (void *)shibAttr->getSerializedValues()[*more].c_str();
-    buf.length = strlen((char *)buf.value);
-
-    /* XXX hack until we have proper binary attribute support */
-    if (attr->length == sizeof("urn:mspac:") - 1 &&
-        memcmp(attr->value, "urn:mspac:", attr->length) == 0) {
-        ssize_t octetLen;
+    binaryAttr = dynamic_cast<const BinaryAttribute *>(shibAttr);
+    if (binaryAttr != NULL) {
+        std::string str = binaryAttr->getValues()[*more];
 
-        value->value = GSSEAP_MALLOC(buf.length);
-        if (value->value == NULL)
-            throw std::bad_alloc();
-
-        octetLen = base64Decode((char *)buf.value, value->value);
-        if (octetLen < 0) {
-            GSSEAP_FREE(value->value);
-            value->value = NULL;
-            return false;
-        }
+        valueBuf.value = (void *)str.data();
+        valueBuf.length = str.size();
+    } else {
+        std::string str = shibAttr->getSerializedValues()[*more];
 
-        value->length = octetLen;
-    } else if (buf.length != 0) {
-        if (value != NULL)
-            duplicateBuffer(buf, value);
+        valueBuf.value = (void *)str.c_str();
+        valueBuf.length = str.length();
 
-        if (display_value != NULL)
-            duplicateBuffer(buf, display_value);
+        const SimpleAttribute *simpleAttr =
+            dynamic_cast<const SimpleAttribute *>(shibAttr);
+        const ScopedAttribute *scopedAttr =
+            dynamic_cast<const ScopedAttribute *>(shibAttr);
+        if (simpleAttr != NULL || scopedAttr != NULL)
+            displayValueBuf = valueBuf;
     }
 
     if (authenticated != NULL)
         *authenticated = m_authenticated;
     if (complete != NULL)
         *complete = true;
-
+    if (value != NULL)
+        duplicateBuffer(valueBuf, value);
+    if (display_value != NULL)
+        duplicateBuffer(displayValueBuf, display_value);
     if (nvalues > ++i)
         *more = i;
 
@@ -346,7 +425,7 @@ gss_eap_shib_attr_provider::mapToAny(int authenticated,
 {
     gss_any_t output;
 
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     if (authenticated && !m_authenticated)
         return (gss_any_t)NULL;
@@ -362,7 +441,7 @@ void
 gss_eap_shib_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id GSSEAP_UNUSED,
                                                   gss_any_t input) const
 {
-    assert(m_initialized);
+    GSSEAP_ASSERT(m_initialized);
 
     vector <Attribute *> *v = ((vector <Attribute *> *)input);
     delete v;
@@ -411,8 +490,8 @@ gss_eap_shib_attr_provider::initWithJsonObject(const gss_eap_attr_ctx *ctx,
     if (!gss_eap_attr_provider::initWithJsonObject(ctx, obj))
         return false;
 
-    assert(m_authenticated == false);
-    assert(m_attributes.size() == 0);
+    GSSEAP_ASSERT(m_authenticated == false);
+    GSSEAP_ASSERT(m_attributes.size() == 0);
 
     JSONObject jattrs = obj["attributes"];
     size_t nelems = jattrs.size();
@@ -436,14 +515,22 @@ gss_eap_shib_attr_provider::init(void)
 {
     bool ret = false;
 
+    if (ShibFinalizer::isShibInitialized()) {
+        wpa_printf(MSG_INFO, "### gss_eap_shib_attr_provider::init(): ShibResolver library is already initialized; ignoring.");
+        return true;
+    }
+
+    wpa_printf(MSG_INFO, "### gss_eap_shib_attr_provider::init(): Initializing ShibResolver library");
+
     try {
-        if (SPConfig::getConfig().getFeatures() == 0)
-            ret = ShibbolethResolver::init();
+        ret = ShibbolethResolver::init();
     } catch (exception &e) {
     }
 
-    if (ret)
+    if (ret) {
+        ShibFinalizer::createSingleton();
         gss_eap_attr_ctx::registerProvider(ATTR_TYPE_LOCAL, createAttrContext);
+    }
 
     return ret;
 }
@@ -451,6 +538,7 @@ gss_eap_shib_attr_provider::init(void)
 void
 gss_eap_shib_attr_provider::finalize(void)
 {
+    wpa_printf(MSG_INFO, "### gss_eap_shib_attr_provider::finalize(): calling ShibbolethResolver::term()");
     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_LOCAL);
     ShibbolethResolver::term();
 }
@@ -508,6 +596,7 @@ gss_eap_shib_attr_provider::duplicateAttributes(const vector <Attribute *>src)
     return dst;
 }
 
+
 OM_uint32
 gssEapLocalAttrProviderInit(OM_uint32 *minor)
 {
@@ -515,14 +604,6 @@ gssEapLocalAttrProviderInit(OM_uint32 *minor)
         *minor = GSSEAP_SHIB_INIT_FAILURE;
         return GSS_S_FAILURE;
     }
-    return GSS_S_COMPLETE;
-}
-
-OM_uint32
-gssEapLocalAttrProviderFinalize(OM_uint32 *minor)
-{
-    gss_eap_shib_attr_provider::finalize();
 
-    *minor = 0;
     return GSS_S_COMPLETE;
 }