2 * Copyright (c) 2010, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of JANET(UK) nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * Copyright 2001-2009 Internet2
35 * Licensed under the Apache License, Version 2.0 (the "License");
36 * you may not use this file except in compliance with the License.
37 * You may obtain a copy of the License at
39 * http://www.apache.org/licenses/LICENSE-2.0
41 * Unless required by applicable law or agreed to in writing, software
42 * distributed under the License is distributed on an "AS IS" BASIS,
43 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
44 * See the License for the specific language governing permissions and
45 * limitations under the License.
48 #include "gssapiP_eap.h"
50 #include <shibsp/Application.h>
51 #include <shibsp/exceptions.h>
52 #include <shibsp/SPConfig.h>
53 #include <shibsp/ServiceProvider.h>
54 #include <shibsp/attribute/Attribute.h>
55 #include <shibsp/attribute/SimpleAttribute.h>
56 #include <shibsp/attribute/resolver/ResolutionContext.h>
57 #include <shibsp/handler/AssertionConsumerService.h>
58 #include <shibsp/metadata/MetadataProviderCriteria.h>
59 #include <shibsp/util/SPConstants.h>
61 #include <saml/saml1/core/Assertions.h>
62 #include <saml/saml2/core/Assertions.h>
63 #include <saml/saml2/metadata/Metadata.h>
64 #include <xercesc/util/XMLUniDefs.hpp>
65 #include <xmltooling/XMLToolingConfig.h>
66 #include <xmltooling/util/XMLHelper.h>
70 using namespace shibsp;
71 using namespace shibresolver;
72 using namespace opensaml::saml2md;
73 using namespace opensaml;
74 using namespace xmltooling::logging;
75 using namespace xmltooling;
76 using namespace xercesc;
79 static vector <Attribute *>
80 duplicateAttributes(const vector <Attribute *>src);
83 * Class representing the SAML compoments of a EAP GSS name.
85 struct gss_eap_saml_attr_ctx
88 gss_eap_saml_attr_ctx(void) {}
90 gss_eap_saml_attr_ctx(const vector<Attribute*>& attributes,
91 const saml2::Assertion *assertion = NULL) {
92 if (assertion != NULL)
93 setAssertion(assertion);
94 if (attributes.size())
95 setAttributes(duplicateAttributes(attributes));
98 gss_eap_saml_attr_ctx(const gss_eap_saml_attr_ctx &ctx) {
99 gss_eap_saml_attr_ctx(ctx.m_attributes, ctx.m_assertion);
102 ~gss_eap_saml_attr_ctx() {
103 for_each(m_attributes.begin(),
105 xmltooling::cleanup<Attribute>())
110 const vector <Attribute *> getAttributes(void) const {
114 void addAttribute(Attribute *attr, bool copy = true);
115 void setAttributes(const vector<Attribute*> attributes);
117 void setAttribute(int complete,
118 const gss_buffer_t attr,
119 const gss_buffer_t value);
120 void deleteAttribute(const gss_buffer_t attr);
122 int getAttributeIndex(const gss_buffer_t attr) const;
123 const Attribute *getAttribute(const gss_buffer_t attr) const;
125 bool getAttribute(const gss_buffer_t attr,
129 gss_buffer_t display_value,
132 const saml2::Assertion *getAssertion(void) const {
136 void setAssertion(const saml2::Assertion *assertion) {
138 if (assertion != NULL)
139 m_assertion = dynamic_cast<saml2::Assertion *>(assertion->clone());
144 void setAssertion(const gss_buffer_t buffer) {
146 m_assertion = parseAssertion(buffer);
149 bool getAssertion(gss_buffer_t buffer);
151 DDF marshall() const;
152 static gss_eap_saml_attr_ctx *unmarshall(DDF &in);
154 void marshall(gss_buffer_t buffer);
155 static gss_eap_saml_attr_ctx *unmarshall(const gss_buffer_t buffer);
158 mutable vector<Attribute*> m_attributes;
159 mutable saml2::Assertion *m_assertion;
161 static saml2::Assertion *parseAssertion(const gss_buffer_t buffer);
165 * Map an exception to a GSS major/mechanism status code.
169 mapException(OM_uint32 *minor, exception &e)
172 return GSS_S_FAILURE;
176 * Parse a GSS buffer into a SAML v2 assertion.
179 gss_eap_saml_attr_ctx::parseAssertion(const gss_buffer_t buffer)
182 const XMLObjectBuilder *b;
185 string str((char *)buffer->value, buffer->length);
186 istringstream istream(str);
188 doc = XMLToolingConfig::getConfig().getParser().parse(istream);
189 b = XMLObjectBuilder::getDefaultBuilder();
190 elem = doc->getDocumentElement();
191 xobj = b->buildOneFromElement(elem, true);
193 return dynamic_cast<saml2::Assertion *>(xobj);
197 duplicateBuffer(gss_buffer_desc &src, gss_buffer_t dst)
201 if (GSS_ERROR(duplicateBuffer(&minor, &src, dst)))
202 throw new bad_alloc();
206 duplicateBuffer(string &str, gss_buffer_t buffer)
210 tmp.length = str.length();
211 tmp.value = (char *)str.c_str();
213 duplicateBuffer(tmp, buffer);
217 * Marshall SAML attribute context into a form suitable for
221 gss_eap_saml_attr_ctx::marshall() const
227 obj.addmember("version").integer(1);
229 attrs = obj.addmember("attributes").list();
230 for (vector<Attribute*>::const_iterator a = m_attributes.begin();
231 a != m_attributes.end(); ++a) {
232 DDF attr = (*a)->marshall();
237 sink << *m_assertion;
238 assertion = obj.addmember("assertion").string(sink.str().c_str());
244 * Unmarshall SAML attribute context from a form suitable for
247 gss_eap_saml_attr_ctx *
248 gss_eap_saml_attr_ctx::unmarshall(DDF &obj)
250 gss_eap_saml_attr_ctx *ctx = new gss_eap_saml_attr_ctx();
252 DDF version = obj["version"];
253 if (version.integer() != 1)
256 DDF assertion = obj["assertion"];
257 gss_buffer_desc buffer;
259 if (!assertion.isnull()) {
260 buffer.length = assertion.strlen();
261 buffer.value = (void *)assertion.string();
266 if (buffer.length != 0)
267 ctx->parseAssertion(&buffer);
269 DDF attrs = obj["attributes"];
270 DDF attr = attrs.first();
271 while (!attr.isnull()) {
272 Attribute *attribute = Attribute::unmarshall(attr);
273 ctx->addAttribute(attribute, false);
281 gss_eap_saml_attr_ctx::marshall(gss_buffer_t buffer)
283 DDF obj = marshall();
286 string str = sink.str();
288 duplicateBuffer(str, buffer);
293 gss_eap_saml_attr_ctx *
294 gss_eap_saml_attr_ctx::unmarshall(const gss_buffer_t buffer)
296 gss_eap_saml_attr_ctx *ctx;
298 string str((const char *)buffer->value, buffer->length);
299 istringstream source(str);
303 ctx = unmarshall(obj);
311 * Return the serialised assertion.
314 gss_eap_saml_attr_ctx::getAssertion(gss_buffer_t buffer)
318 if (m_assertion == NULL)
321 buffer->value = NULL;
324 XMLHelper::serialize(m_assertion->marshall((DOMDocument *)NULL), str);
326 duplicateBuffer(str, buffer);
332 duplicateAttribute(const Attribute *src)
334 Attribute *attribute;
336 DDF obj = src->marshall();
337 attribute = Attribute::unmarshall(obj);
343 static vector <Attribute *>
344 duplicateAttributes(const vector <Attribute *>src)
346 vector <Attribute *> dst;
348 for (vector<Attribute *>::const_iterator a = src.begin();
351 dst.push_back(duplicateAttribute(*a));
357 gss_eap_saml_attr_ctx::addAttribute(Attribute *attribute, bool copy)
361 a = copy ? duplicateAttribute(attribute) : attribute;
363 m_attributes.push_back(a);
367 gss_eap_saml_attr_ctx::setAttributes(const vector<Attribute*> attributes)
369 for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
370 m_attributes = attributes;
374 gss_eap_saml_attr_ctx::getAttributeIndex(const gss_buffer_t attr) const
378 for (vector<Attribute *>::const_iterator a = getAttributes().begin();
379 a != getAttributes().end();
382 for (vector<string>::const_iterator s = (*a)->getAliases().begin();
383 s != (*a)->getAliases().end();
385 if (attr->length == (*s).length() &&
386 memcmp((*s).c_str(), attr->value, attr->length) == 0) {
396 gss_eap_saml_attr_ctx::getAttribute(const gss_buffer_t attr) const
398 const Attribute *ret = NULL;
400 for (vector<Attribute *>::const_iterator a = getAttributes().begin();
401 a != getAttributes().end();
404 for (vector<string>::const_iterator s = (*a)->getAliases().begin();
405 s != (*a)->getAliases().end();
407 if (attr->length == (*s).length() &&
408 memcmp((*s).c_str(), attr->value, attr->length) == 0) {
421 gss_eap_saml_attr_ctx::getAttribute(const gss_buffer_t attr,
425 gss_buffer_t display_value,
428 const Attribute *shibAttr = NULL;
431 shibAttr = getAttribute(attr);
432 if (shibAttr == NULL)
437 } else if (*more >= (int)shibAttr->valueCount()) {
442 buf.value = (void *)shibAttr->getString(*more);
443 buf.length = strlen((char *)buf.value);
445 duplicateBuffer(buf, value);
447 *authenticated = TRUE;
454 samlAttributeFromGssBuffers(const gss_buffer_t attr,
455 const gss_buffer_t value)
457 string attrStr((char *)attr->value, attr->length);
458 vector <string> ids(1);
461 ids.push_back(attrStr);
463 a = new SimpleAttribute(ids);
465 if (value->length != 0) {
466 string valStr((char *)value->value, value->length);
468 a->getValues().push_back(valStr);
475 gss_eap_saml_attr_ctx::setAttribute(int complete,
476 const gss_buffer_t attr,
477 const gss_buffer_t value)
479 Attribute *a = samlAttributeFromGssBuffers(attr, value);
481 addAttribute(a, false);
485 gss_eap_saml_attr_ctx::deleteAttribute(const gss_buffer_t attr)
489 i = getAttributeIndex(attr);
491 m_attributes.erase(m_attributes.begin() + i);
495 samlReleaseAttrContext(OM_uint32 *minor, gss_name_t name)
498 delete name->samlCtx;
499 name->samlCtx = NULL;
500 } catch (exception &e) {
501 return mapException(minor, e);
504 return GSS_S_COMPLETE;
507 static gss_buffer_desc
508 gssEapRadiusAssertionAttr = { 3, (void *)"128" }; /* TODO */
510 class gss_eap_saml_attr_args {
512 vector <Attribute *> attrs;
513 ShibbolethResolver *resolver;
517 * Callback to add a RADIUS attribute as input to the resolver.
520 samlAddRadiusAttribute(OM_uint32 *minor,
526 gss_eap_saml_attr_args *args = (gss_eap_saml_attr_args *)data;
528 int authenticated, complete, more = -1;
529 gss_buffer_desc value;
531 /* Put attributes to skip here (or in a table somewhere) */
532 if (bufferEqual(attr, &gssEapRadiusAssertionAttr)) {
533 return GSS_S_COMPLETE;
536 major = radiusGetAttribute(minor, name, attr,
537 &authenticated, &complete,
538 &value, GSS_C_NO_BUFFER, &more);
539 if (major == GSS_S_COMPLETE) {
540 /* XXX TODO prefix */
541 a = samlAttributeFromGssBuffers(attr, &value);
542 args->attrs.push_back(a);
543 args->resolver->addAttribute(a);
546 return GSS_S_COMPLETE;
550 * Add attributes retrieved via RADIUS.
553 samlAddRadiusAttributes(OM_uint32 *minor,
555 gss_eap_saml_attr_args *args)
557 return radiusGetAttributeTypes(minor,
559 samlAddRadiusAttribute,
564 * Add assertion retrieved via RADIUS.
567 samlAddRadiusAssertion(OM_uint32 *minor,
569 gss_eap_saml_attr_ctx *ctx)
572 int authenticated, complete, more = -1;
573 gss_buffer_desc value;
578 major = radiusGetAttribute(minor, name, &gssEapRadiusAssertionAttr,
579 &authenticated, &complete,
580 &value, GSS_C_NO_BUFFER, &more);
581 if (GSS_ERROR(major) && major != GSS_S_UNAVAILABLE)
584 ctx->setAssertion(&value);
586 gss_release_buffer(minor, &value);
588 return GSS_S_COMPLETE;
592 * Initialise SAML attribute context in initiator name. RADIUS context
593 * must have been previously initialised.
596 samlCreateAttrContext(OM_uint32 *minor,
597 gss_cred_id_t acceptorCred,
598 gss_name_t initiatorName,
601 OM_uint32 major, tmpMinor;
602 gss_buffer_desc nameBuf;
603 gss_eap_saml_attr_ctx *ctx = NULL;
604 ShibbolethResolver *resolver = NULL;
605 gss_eap_saml_attr_args args;
607 assert(initiatorName != GSS_C_NO_NAME);
609 if (initiatorName->radiusCtx == NULL)
610 return GSS_S_UNAVAILABLE;
613 nameBuf.value = NULL;
615 resolver = ShibbolethResolver::create();
616 if (resolver == NULL)
617 return GSS_S_FAILURE;
619 args.resolver = resolver;
621 if (acceptorCred != GSS_C_NO_CREDENTIAL) {
622 major = gss_display_name(minor, acceptorCred->name, &nameBuf, NULL);
623 if (GSS_ERROR(major))
628 const saml2::Assertion *assertion;
629 vector <Attribute *> attrs;
631 ctx = new gss_eap_saml_attr_ctx();
633 major = samlAddRadiusAssertion(minor, initiatorName, ctx);
634 if (GSS_ERROR(major))
637 assertion = ctx->getAssertion();
639 if (assertion != NULL) {
640 if (assertion->getConditions()) {
642 assertion->getConditions()->getNotOnOrAfter()->getEpoch();
645 resolver->addToken(assertion);
648 resolver->setApplicationID((const char *)nameBuf.value);
649 if (initiatorName->radiusCtx != NULL)
650 samlAddRadiusAttributes(minor, initiatorName, &args);
651 resolver->resolveAttributes(attrs);
652 ctx->setAttributes(attrs);
653 } catch (exception &ex) {
654 major = mapException(minor, ex);
659 major = GSS_S_COMPLETE;
661 initiatorName->samlCtx = ctx;
664 for_each(args.attrs.begin(), args.attrs.end(), xmltooling::cleanup<Attribute>());
665 gss_release_buffer(&tmpMinor, &nameBuf);
666 if (GSS_ERROR(major))
674 samlGetAttributeTypes(OM_uint32 *minor,
676 enum gss_eap_attribute_type type,
677 gss_eap_add_attr_cb addAttribute,
680 OM_uint32 major = GSS_S_COMPLETE;
681 gss_eap_saml_attr_ctx *ctx = name->samlCtx;
684 return GSS_S_COMPLETE;
686 if (type != ATTR_TYPE_NONE)
687 return GSS_S_UNAVAILABLE;
689 for (vector<Attribute*>::const_iterator a = ctx->getAttributes().begin();
690 a != ctx->getAttributes().end();
693 gss_buffer_desc attribute;
695 attribute.value = (void *)((*a)->getId());
696 attribute.length = strlen((char *)attribute.value);
698 major = addAttribute(minor, name, &attribute, data);
699 if (GSS_ERROR(major))
707 * SAML implementation of gss_get_name_attribute
710 samlGetAttribute(OM_uint32 *minor,
711 enum gss_eap_attribute_type type,
717 gss_buffer_t display_value,
720 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
724 return GSS_S_UNAVAILABLE;
728 ret = ctx->getAttribute(attr, authenticated, complete,
729 value, display_value, more);
736 return ret ? GSS_S_COMPLETE : GSS_S_UNAVAILABLE;
740 samlSetAttribute(OM_uint32 *minor,
746 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
749 return GSS_S_UNAVAILABLE;
752 ctx->setAttribute(complete, attr, value);
753 } catch (exception &e) {
754 return mapException(minor, e);
757 return GSS_S_COMPLETE;
761 samlDeleteAttribute(OM_uint32 *minor,
765 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
768 return GSS_S_UNAVAILABLE;
771 ctx->deleteAttribute(attr);
772 } catch (exception &e) {
773 return mapException(minor, e);
776 return GSS_S_COMPLETE;
780 * In order to implement gss_export_name and gss_export_sec_context,
781 * we need to serialise a resolved attribute context to a buffer.
784 samlExportAttrContext(OM_uint32 *minor,
788 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
791 ctx->marshall(buffer);
792 } catch (exception &e) {
793 return mapException(minor, e);
796 return GSS_S_COMPLETE;
800 * In order to implement gss_import_name and gss_import_sec_context,
801 * we need to deserialise a resolved attribute context from a buffer.
804 samlImportAttrContext(OM_uint32 *minor,
809 assert(name->samlCtx == NULL);
810 name->samlCtx = gss_eap_saml_attr_ctx::unmarshall(buffer);
811 } catch (exception &e) {
812 return mapException(minor, e);
815 return GSS_S_COMPLETE;
819 samlGetAssertion(OM_uint32 *minor,
821 gss_buffer_t assertion)
823 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
826 return GSS_S_UNAVAILABLE;
829 ctx->getAssertion(assertion);
830 } catch (exception &e) {
831 return mapException(minor, e);
834 return GSS_S_COMPLETE;
838 samlDuplicateAttrContext(OM_uint32 *minor,
843 if (in->samlCtx != NULL)
844 out->samlCtx = new gss_eap_saml_attr_ctx(*(in->samlCtx));
847 } catch (exception &e) {
848 return mapException(minor, e);
851 return GSS_S_COMPLETE;
855 samlMapNameToAny(OM_uint32 *minor,
858 gss_buffer_t type_id,
861 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
863 if (bufferEqualString(type_id, "shibsp::Attribute")) {
864 vector <Attribute *>v = duplicateAttributes(ctx->getAttributes());
866 *output = (gss_any_t)new vector <Attribute *>(v);
867 } else if (bufferEqualString(type_id, "opensaml::Assertion")) {
868 *output = (gss_any_t)ctx->getAssertion()->clone();
870 *output = (gss_any_t)NULL;
871 return GSS_S_UNAVAILABLE;
874 return GSS_S_COMPLETE;
878 samlReleaseAnyNameMapping(OM_uint32 *minor,
880 gss_buffer_t type_id,
883 if (bufferEqualString(type_id, "vector<shibsp::Attribute>")) {
884 vector <Attribute *> *v = ((vector <Attribute *> *)*input);
886 } else if (bufferEqualString(type_id, "opensaml::Assertion")) {
887 delete (Assertion *)*input;
889 return GSS_S_UNAVAILABLE;
892 *input = (gss_any_t)NULL;
893 return GSS_S_COMPLETE;
897 samlInit(OM_uint32 *minor)
901 return ShibbolethResolver::init() ? GSS_S_COMPLETE : GSS_S_FAILURE;
905 samlFinalize(OM_uint32 *minor)
909 ShibbolethResolver::term();
910 return GSS_S_COMPLETE;