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)
181 string str((char *)buffer->value, buffer->length);
182 istringstream istream(str);
184 const XMLObjectBuilder *b;
186 doc = XMLToolingConfig::getConfig().getParser().parse(istream);
187 b =XMLObjectBuilder::getBuilder(doc->getDocumentElement());
189 return dynamic_cast<saml2::Assertion *>(b->buildFromDocument(doc));
193 duplicateBuffer(gss_buffer_desc &src, gss_buffer_t dst)
197 if (GSS_ERROR(duplicateBuffer(&minor, &src, dst)))
198 throw new bad_alloc();
202 duplicateBuffer(string &str, gss_buffer_t buffer)
206 tmp.length = str.length();
207 tmp.value = (char *)str.c_str();
209 duplicateBuffer(tmp, buffer);
213 * Marshall SAML attribute context into a form suitable for
217 gss_eap_saml_attr_ctx::marshall() const
223 obj.addmember("version").integer(1);
225 attrs = obj.addmember("attributes").list();
226 for (vector<Attribute*>::const_iterator a = m_attributes.begin();
227 a != m_attributes.end(); ++a) {
228 DDF attr = (*a)->marshall();
233 sink << *m_assertion;
234 assertion = obj.addmember("assertion").string(sink.str().c_str());
240 * Unmarshall SAML attribute context from a form suitable for
243 gss_eap_saml_attr_ctx *
244 gss_eap_saml_attr_ctx::unmarshall(DDF &obj)
246 gss_eap_saml_attr_ctx *ctx = new gss_eap_saml_attr_ctx();
248 DDF version = obj["version"];
249 if (version.integer() != 1)
252 DDF assertion = obj["assertion"];
253 gss_buffer_desc buffer;
255 if (!assertion.isnull()) {
256 buffer.length = assertion.strlen();
257 buffer.value = (void *)assertion.string();
262 if (buffer.length != 0)
263 ctx->parseAssertion(&buffer);
265 DDF attrs = obj["attributes"];
266 DDF attr = attrs.first();
267 while (!attr.isnull()) {
268 Attribute *attribute = Attribute::unmarshall(attr);
269 ctx->addAttribute(attribute, false);
277 gss_eap_saml_attr_ctx::marshall(gss_buffer_t buffer)
279 DDF obj = marshall();
282 string str = sink.str();
284 duplicateBuffer(str, buffer);
289 gss_eap_saml_attr_ctx *
290 gss_eap_saml_attr_ctx::unmarshall(const gss_buffer_t buffer)
292 gss_eap_saml_attr_ctx *ctx;
294 string str((const char *)buffer->value, buffer->length);
295 istringstream source(str);
299 ctx = unmarshall(obj);
307 * Return the serialised assertion.
310 gss_eap_saml_attr_ctx::getAssertion(gss_buffer_t buffer)
314 if (m_assertion == NULL)
317 buffer->value = NULL;
320 XMLHelper::serialize(m_assertion->marshall((DOMDocument *)NULL), str);
322 duplicateBuffer(str, buffer);
328 duplicateAttribute(const Attribute *src)
330 Attribute *attribute;
332 DDF obj = src->marshall();
333 attribute = Attribute::unmarshall(obj);
339 static vector <Attribute *>
340 duplicateAttributes(const vector <Attribute *>src)
342 vector <Attribute *> dst;
344 for (vector<Attribute *>::const_iterator a = src.begin();
347 dst.push_back(duplicateAttribute(*a));
353 gss_eap_saml_attr_ctx::addAttribute(Attribute *attribute, bool copy)
357 a = copy ? duplicateAttribute(attribute) : attribute;
359 m_attributes.push_back(a);
363 gss_eap_saml_attr_ctx::setAttributes(const vector<Attribute*> attributes)
365 for_each(m_attributes.begin(), m_attributes.end(), xmltooling::cleanup<Attribute>());
366 m_attributes = attributes;
370 gss_eap_saml_attr_ctx::getAttributeIndex(const gss_buffer_t attr) const
374 for (vector<Attribute *>::const_iterator a = getAttributes().begin();
375 a != getAttributes().end();
378 for (vector<string>::const_iterator s = (*a)->getAliases().begin();
379 s != (*a)->getAliases().end();
381 if (attr->length == (*s).length() &&
382 memcmp((*s).c_str(), attr->value, attr->length) == 0) {
392 gss_eap_saml_attr_ctx::getAttribute(const gss_buffer_t attr) const
394 const Attribute *ret = NULL;
396 for (vector<Attribute *>::const_iterator a = getAttributes().begin();
397 a != getAttributes().end();
400 for (vector<string>::const_iterator s = (*a)->getAliases().begin();
401 s != (*a)->getAliases().end();
403 if (attr->length == (*s).length() &&
404 memcmp((*s).c_str(), attr->value, attr->length) == 0) {
417 gss_eap_saml_attr_ctx::getAttribute(const gss_buffer_t attr,
421 gss_buffer_t display_value,
424 const Attribute *shibAttr = NULL;
427 shibAttr = getAttribute(attr);
428 if (shibAttr == NULL)
433 } else if (*more >= (int)shibAttr->valueCount()) {
438 buf.value = (void *)shibAttr->getString(*more);
439 buf.length = strlen((char *)buf.value);
441 duplicateBuffer(buf, value);
443 *authenticated = TRUE;
450 samlAttributeFromGssBuffers(const gss_buffer_t attr,
451 const gss_buffer_t value)
453 string attrStr((char *)attr->value, attr->length);
454 vector <string> ids(1);
457 ids.push_back(attrStr);
459 a = new SimpleAttribute(ids);
461 if (value->length != 0) {
462 string valStr((char *)value->value, value->length);
464 a->getValues().push_back(valStr);
471 gss_eap_saml_attr_ctx::setAttribute(int complete,
472 const gss_buffer_t attr,
473 const gss_buffer_t value)
475 Attribute *a = samlAttributeFromGssBuffers(attr, value);
477 addAttribute(a, false);
481 gss_eap_saml_attr_ctx::deleteAttribute(const gss_buffer_t attr)
485 i = getAttributeIndex(attr);
487 m_attributes.erase(m_attributes.begin() + i);
491 samlReleaseAttrContext(OM_uint32 *minor, gss_name_t name)
494 delete name->samlCtx;
495 name->samlCtx = NULL;
496 } catch (exception &e) {
497 return mapException(minor, e);
500 return GSS_S_COMPLETE;
503 static gss_buffer_desc
504 gssEapRadiusAssertionAttr = { 3, (void *)"128" }; /* TODO */
506 class gss_eap_saml_attr_args {
508 vector <Attribute *> attrs;
509 ShibbolethResolver *resolver;
513 * Callback to add a RADIUS attribute as input to the resolver.
516 samlAddRadiusAttribute(OM_uint32 *minor,
522 gss_eap_saml_attr_args *args = (gss_eap_saml_attr_args *)data;
524 int authenticated, complete, more = -1;
525 gss_buffer_desc value;
527 /* Put attributes to skip here (or in a table somewhere) */
528 if (bufferEqual(attr, &gssEapRadiusAssertionAttr)) {
529 return GSS_S_COMPLETE;
532 major = radiusGetAttribute(minor, name, attr,
533 &authenticated, &complete,
534 &value, GSS_C_NO_BUFFER, &more);
535 if (major == GSS_S_COMPLETE) {
536 /* XXX TODO prefix */
537 a = samlAttributeFromGssBuffers(attr, &value);
538 args->attrs.push_back(a);
539 args->resolver->addAttribute(a);
542 return GSS_S_COMPLETE;
546 * Add attributes retrieved via RADIUS.
549 samlAddRadiusAttributes(OM_uint32 *minor,
551 gss_eap_saml_attr_args *args)
553 return radiusGetAttributeTypes(minor,
555 samlAddRadiusAttribute,
560 * Add assertion retrieved via RADIUS.
563 samlAddRadiusAssertion(OM_uint32 *minor,
565 gss_eap_saml_attr_ctx *ctx)
568 int authenticated, complete, more = -1;
569 gss_buffer_desc value;
574 major = radiusGetAttribute(minor, name, &gssEapRadiusAssertionAttr,
575 &authenticated, &complete,
576 &value, GSS_C_NO_BUFFER, &more);
577 if (GSS_ERROR(major) && major != GSS_S_UNAVAILABLE)
580 ctx->setAssertion(&value);
582 gss_release_buffer(minor, &value);
584 return GSS_S_COMPLETE;
588 * Initialise SAML attribute context in initiator name. RADIUS context
589 * must have been previously initialised.
592 samlCreateAttrContext(OM_uint32 *minor,
593 gss_cred_id_t acceptorCred,
594 gss_name_t initiatorName,
597 OM_uint32 major, tmpMinor;
598 gss_buffer_desc nameBuf;
599 gss_eap_saml_attr_ctx *ctx = NULL;
600 ShibbolethResolver *resolver = NULL;
601 gss_eap_saml_attr_args args;
603 assert(initiatorName != GSS_C_NO_NAME);
605 if (initiatorName->radiusCtx == NULL)
606 return GSS_S_UNAVAILABLE;
609 nameBuf.value = NULL;
611 resolver = ShibbolethResolver::create();
612 if (resolver == NULL)
613 return GSS_S_FAILURE;
615 args.resolver = resolver;
617 if (acceptorCred != GSS_C_NO_CREDENTIAL) {
618 major = gss_display_name(minor, acceptorCred->name, &nameBuf, NULL);
619 if (GSS_ERROR(major))
624 const saml2::Assertion *assertion;
625 vector <Attribute *> attrs;
627 ctx = new gss_eap_saml_attr_ctx();
629 major = samlAddRadiusAssertion(minor, initiatorName, ctx);
630 if (GSS_ERROR(major))
633 assertion = ctx->getAssertion();
635 if (assertion != NULL) {
636 if (assertion->getConditions()) {
638 assertion->getConditions()->getNotOnOrAfter()->getEpoch();
641 resolver->addToken(assertion);
644 resolver->setApplicationID((const char *)nameBuf.value);
645 if (initiatorName->radiusCtx != NULL)
646 samlAddRadiusAttributes(minor, initiatorName, &args);
647 resolver->resolveAttributes(attrs);
648 ctx->setAttributes(attrs);
649 } catch (exception &ex) {
650 major = mapException(minor, ex);
655 major = GSS_S_COMPLETE;
657 initiatorName->samlCtx = ctx;
660 for_each(args.attrs.begin(), args.attrs.end(), xmltooling::cleanup<Attribute>());
661 gss_release_buffer(&tmpMinor, &nameBuf);
662 if (GSS_ERROR(major))
670 samlGetAttributeTypes(OM_uint32 *minor,
672 enum gss_eap_attribute_type type,
673 gss_eap_add_attr_cb addAttribute,
676 OM_uint32 major = GSS_S_COMPLETE;
677 gss_eap_saml_attr_ctx *ctx = name->samlCtx;
680 return GSS_S_COMPLETE;
682 if (type != ATTR_TYPE_NONE)
683 return GSS_S_UNAVAILABLE;
685 for (vector<Attribute*>::const_iterator a = ctx->getAttributes().begin();
686 a != ctx->getAttributes().end();
689 gss_buffer_desc attribute;
691 attribute.value = (void *)((*a)->getId());
692 attribute.length = strlen((char *)attribute.value);
694 major = addAttribute(minor, name, &attribute, data);
695 if (GSS_ERROR(major))
703 * SAML implementation of gss_get_name_attribute
706 samlGetAttribute(OM_uint32 *minor,
707 enum gss_eap_attribute_type type,
713 gss_buffer_t display_value,
716 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
720 return GSS_S_UNAVAILABLE;
724 ret = ctx->getAttribute(attr, authenticated, complete,
725 value, display_value, more);
732 return ret ? GSS_S_COMPLETE : GSS_S_UNAVAILABLE;
736 samlSetAttribute(OM_uint32 *minor,
742 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
745 return GSS_S_UNAVAILABLE;
748 ctx->setAttribute(complete, attr, value);
749 } catch (exception &e) {
750 return mapException(minor, e);
753 return GSS_S_COMPLETE;
757 samlDeleteAttribute(OM_uint32 *minor,
761 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
764 return GSS_S_UNAVAILABLE;
767 ctx->deleteAttribute(attr);
768 } catch (exception &e) {
769 return mapException(minor, e);
772 return GSS_S_COMPLETE;
776 * In order to implement gss_export_name and gss_export_sec_context,
777 * we need to serialise a resolved attribute context to a buffer.
780 samlExportAttrContext(OM_uint32 *minor,
784 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
787 ctx->marshall(buffer);
788 } catch (exception &e) {
789 return mapException(minor, e);
792 return GSS_S_COMPLETE;
796 * In order to implement gss_import_name and gss_import_sec_context,
797 * we need to deserialise a resolved attribute context from a buffer.
800 samlImportAttrContext(OM_uint32 *minor,
805 assert(name->samlCtx == NULL);
806 name->samlCtx = gss_eap_saml_attr_ctx::unmarshall(buffer);
807 } catch (exception &e) {
808 return mapException(minor, e);
811 return GSS_S_COMPLETE;
815 samlGetAssertion(OM_uint32 *minor,
817 gss_buffer_t assertion)
819 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
822 return GSS_S_UNAVAILABLE;
825 ctx->getAssertion(assertion);
826 } catch (exception &e) {
827 return mapException(minor, e);
830 return GSS_S_COMPLETE;
834 samlDuplicateAttrContext(OM_uint32 *minor,
839 if (in->samlCtx != NULL)
840 out->samlCtx = new gss_eap_saml_attr_ctx(*(in->samlCtx));
843 } catch (exception &e) {
844 return mapException(minor, e);
847 return GSS_S_COMPLETE;
851 samlMapNameToAny(OM_uint32 *minor,
854 gss_buffer_t type_id,
857 struct gss_eap_saml_attr_ctx *ctx = name->samlCtx;
859 if (bufferEqualString(type_id, "shibsp::Attribute")) {
860 vector <Attribute *>v = duplicateAttributes(ctx->getAttributes());
862 *output = (gss_any_t)new vector <Attribute *>(v);
863 } else if (bufferEqualString(type_id, "opensaml::Assertion")) {
864 *output = (gss_any_t)ctx->getAssertion()->clone();
866 *output = (gss_any_t)NULL;
867 return GSS_S_UNAVAILABLE;
870 return GSS_S_COMPLETE;
874 samlReleaseAnyNameMapping(OM_uint32 *minor,
876 gss_buffer_t type_id,
879 if (bufferEqualString(type_id, "vector<shibsp::Attribute>")) {
880 vector <Attribute *> *v = ((vector <Attribute *> *)*input);
882 } else if (bufferEqualString(type_id, "opensaml::Assertion")) {
883 delete (Assertion *)*input;
885 return GSS_S_UNAVAILABLE;
888 *input = (gss_any_t)NULL;
889 return GSS_S_COMPLETE;
893 samlInit(OM_uint32 *minor)
897 return ShibbolethResolver::init() ? GSS_S_COMPLETE : GSS_S_FAILURE;
901 samlFinalize(OM_uint32 *minor)
905 ShibbolethResolver::term();
906 return GSS_S_COMPLETE;