add set/delete attribute to SAML provider
[mech_eap.git] / util_saml.cpp
1 /*
2  * Copyright (c) 2010, JANET(UK)
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
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.
15  *
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.
19  *
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
30  * SUCH DAMAGE.
31  */
32
33 #include "gssapiP_eap.h"
34
35 #include <sstream>
36
37 #include <xercesc/util/XMLUniDefs.hpp>
38 #include <xmltooling/unicode.h>
39 #include <xmltooling/XMLToolingConfig.h>
40 #include <xmltooling/util/XMLHelper.h>
41 #include <xmltooling/util/ParserPool.h>
42 #include <xmltooling/util/DateTime.h>
43
44 #include <saml/saml1/core/Assertions.h>
45 #include <saml/saml2/core/Assertions.h>
46 #include <saml/saml2/metadata/Metadata.h>
47
48 using namespace xmltooling;
49 using namespace opensaml::saml2md;
50 using namespace opensaml;
51 using namespace xercesc;
52 using namespace std;
53
54 /*
55  * gss_eap_saml_assertion_provider is for retrieving the underlying
56  * assertion.
57  */
58 gss_eap_saml_assertion_provider::gss_eap_saml_assertion_provider(void)
59 {
60     m_assertion = NULL;
61     m_authenticated = false;
62 }
63
64 gss_eap_saml_assertion_provider::~gss_eap_saml_assertion_provider(void)
65 {
66     delete m_assertion;
67 }
68
69 bool
70 gss_eap_saml_assertion_provider::initFromExistingContext(const gss_eap_attr_ctx *manager,
71                                                          const gss_eap_attr_provider *ctx)
72 {
73     /* Then we may be creating from an existing attribute context */
74     const gss_eap_saml_assertion_provider *saml;
75
76     assert(m_assertion == NULL);
77
78     if (!gss_eap_attr_provider::initFromExistingContext(manager, ctx))
79         return false;
80
81     saml = static_cast<const gss_eap_saml_assertion_provider *>(ctx);
82     setAssertion(saml->getAssertion(), saml->authenticated());
83
84     return true;
85 }
86
87 bool
88 gss_eap_saml_assertion_provider::initFromGssContext(const gss_eap_attr_ctx *manager,
89                                                     const gss_cred_id_t gssCred,
90                                                     const gss_ctx_id_t gssCtx)
91 {
92     const gss_eap_radius_attr_provider *radius;
93     gss_buffer_desc value = GSS_C_EMPTY_BUFFER;
94     int authenticated, complete;
95     OM_uint32 minor;
96
97     assert(m_assertion == NULL);
98
99     if (!gss_eap_attr_provider::initFromGssContext(manager, gssCred, gssCtx))
100         return false;
101
102     /*
103      * XXX TODO we need to support draft-howlett-radius-saml-attr-00
104      */
105     radius = static_cast<const gss_eap_radius_attr_provider *>
106         (m_manager->getProvider(ATTR_TYPE_RADIUS));
107     if (radius != NULL &&
108         radius->getFragmentedAttribute(PW_SAML_AAA_ASSERTION,
109                                        VENDORPEC_UKERNA,
110                                        &authenticated, &complete, &value)) {
111         setAssertion(&value, authenticated);
112         gss_release_buffer(&minor, &value);
113     } else {
114         m_assertion = NULL;
115     }
116
117     return true;
118 }
119
120 void
121 gss_eap_saml_assertion_provider::setAssertion(const saml2::Assertion *assertion,
122                                               bool authenticated)
123 {
124
125     delete m_assertion;
126
127     if (assertion != NULL) {
128 #ifdef __APPLE__
129         m_assertion = (saml2::Assertion *)((void *)assertion->clone());
130 #else
131         m_assertion = dynamic_cast<saml2::Assertion *>(assertion->clone());
132 #endif
133         m_authenticated = authenticated;
134     } else {
135         m_assertion = NULL;
136         m_authenticated = false;
137     }
138 }
139
140 void
141 gss_eap_saml_assertion_provider::setAssertion(const gss_buffer_t buffer,
142                                               bool authenticated)
143 {
144     delete m_assertion;
145
146     m_assertion = parseAssertion(buffer);
147     m_authenticated = (m_assertion != NULL && authenticated);
148 }
149
150 saml2::Assertion *
151 gss_eap_saml_assertion_provider::parseAssertion(const gss_buffer_t buffer)
152 {
153     string str((char *)buffer->value, buffer->length);
154     istringstream istream(str);
155     DOMDocument *doc;
156     const XMLObjectBuilder *b;
157
158     doc = XMLToolingConfig::getConfig().getParser().parse(istream);
159     if (doc == NULL)
160         return NULL;
161
162     b = XMLObjectBuilder::getBuilder(doc->getDocumentElement());
163
164 #ifdef __APPLE__
165     return (saml2::Assertion *)((void *)b->buildFromDocument(doc));
166 #else
167     return dynamic_cast<saml2::Assertion *>(b->buildFromDocument(doc));
168 #endif
169 }
170
171 bool
172 gss_eap_saml_assertion_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
173                                                    void *data) const
174 {
175     bool ret;
176
177     /* just add the prefix */
178     if (m_assertion != NULL)
179         ret = addAttribute(this, GSS_C_NO_BUFFER, data);
180     else
181         ret = true;
182
183     return ret;
184 }
185
186 bool
187 gss_eap_saml_assertion_provider::setAttribute(int complete,
188                                               const gss_buffer_t attr,
189                                               const gss_buffer_t value)
190 {
191     if (attr == GSS_C_NO_BUFFER || attr->length == 0) {
192         setAssertion(value);
193         return true;
194     }
195
196     return false;
197 }
198
199 bool
200 gss_eap_saml_assertion_provider::deleteAttribute(const gss_buffer_t value)
201 {
202     delete m_assertion;
203     m_assertion = NULL;
204     m_authenticated = false;
205
206     return true;
207 }
208
209 time_t
210 gss_eap_saml_assertion_provider::getExpiryTime(void) const
211 {
212     saml2::Conditions *conditions;
213     time_t expiryTime = 0;
214
215     if (m_assertion == NULL)
216         return 0;
217
218     conditions = m_assertion->getConditions();
219
220     if (conditions != NULL && conditions->getNotOnOrAfter() != NULL)
221         expiryTime = conditions->getNotOnOrAfter()->getEpoch();
222
223     return expiryTime;
224 }
225
226 bool
227 gss_eap_saml_assertion_provider::getAttribute(const gss_buffer_t attr,
228                                               int *authenticated,
229                                               int *complete,
230                                               gss_buffer_t value,
231                                               gss_buffer_t display_value,
232                                               int *more) const
233 {
234     string str;
235
236     if (attr != GSS_C_NO_BUFFER && attr->length != 0)
237         return false;
238
239     if (m_assertion == NULL)
240         return false;
241
242     if (*more != -1)
243         return false;
244
245     if (authenticated != NULL)
246         *authenticated = m_authenticated;
247     if (complete != NULL)
248         *complete = true;
249
250     XMLHelper::serialize(m_assertion->marshall((DOMDocument *)NULL), str);
251
252     duplicateBuffer(str, value);
253     *more = 0;
254
255     return true;
256 }
257
258 gss_any_t
259 gss_eap_saml_assertion_provider::mapToAny(int authenticated,
260                                           gss_buffer_t type_id) const
261 {
262     if (authenticated && !m_authenticated)
263         return (gss_any_t)NULL;
264
265     return (gss_any_t)m_assertion;
266 }
267
268 void
269 gss_eap_saml_assertion_provider::releaseAnyNameMapping(gss_buffer_t type_id,
270                                                        gss_any_t input) const
271 {
272     delete ((saml2::Assertion *)input);
273 }
274
275 void
276 gss_eap_saml_assertion_provider::exportToBuffer(gss_buffer_t buffer) const
277 {
278     ostringstream sink;
279     string str;
280
281     buffer->length = 0;
282     buffer->value = NULL;
283
284     if (m_assertion == NULL)
285         return;
286
287     sink << *m_assertion;
288     str = sink.str();
289
290     duplicateBuffer(str, buffer);
291 }
292
293 bool
294 gss_eap_saml_assertion_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
295                                                 const gss_buffer_t buffer)
296 {
297     if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer))
298         return false;
299
300     if (buffer->length == 0)
301         return true;
302
303     assert(m_assertion == NULL);
304
305     setAssertion(buffer);
306     /* TODO XXX how to propagate authenticated flag? */
307
308     return true;
309 }
310
311 bool
312 gss_eap_saml_assertion_provider::init(void)
313 {
314     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML_ASSERTION,
315                                        "urn:ietf:params:gss-eap:saml-aaa-assertion",
316                                        gss_eap_saml_assertion_provider::createAttrContext);
317     return true;
318 }
319
320 void
321 gss_eap_saml_assertion_provider::finalize(void)
322 {
323     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML_ASSERTION);
324 }
325
326 gss_eap_attr_provider *
327 gss_eap_saml_assertion_provider::createAttrContext(void)
328 {
329     return new gss_eap_saml_assertion_provider;
330 }
331
332 saml2::Assertion *
333 gss_eap_saml_assertion_provider::initAssertion(void)
334 {
335     delete m_assertion;
336     m_assertion = saml2::AssertionBuilder::buildAssertion();
337     m_authenticated = false;
338
339     return m_assertion;
340 }
341
342 /*
343  * gss_eap_saml_attr_provider is for retrieving the underlying attributes.
344  */
345 bool
346 gss_eap_saml_attr_provider::getAssertion(int *authenticated,
347                                          saml2::Assertion **pAssertion,
348                                          bool createIfAbsent) const
349 {
350     gss_eap_saml_assertion_provider *saml;
351
352     if (authenticated != NULL)
353         *authenticated = false;
354     if (pAssertion != NULL)
355         *pAssertion = NULL;
356
357     saml = static_cast<const gss_eap_saml_assertion_provider *>
358         (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION));
359     if (saml == NULL)
360         return false;
361
362     if (authenticated != NULL)
363         *authenticated = saml->authenticated();
364     if (pAssertion != NULL)
365         *pAssertion = saml->getAssertion();
366
367     if (saml->getAssertion() == NULL) {
368         if (createIfAbsent) {
369             if (authenticated != NULL)
370                 *authenticated = false;
371             if (pAssertion != NULL)
372                 *pAssertion = saml->initAssertion();
373         } else
374             return false;
375     }
376
377     return true;
378 }
379
380 bool
381 gss_eap_saml_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
382                                               void *data) const
383 {
384     saml2::Assertion *assertion;
385     int authenticated;
386
387     if (!getAssertion(&authenticated, &assertion))
388         return true;
389
390     /*
391      * Note: the first prefix is added by the attribute provider manager
392      *
393      * From draft-hartman-gss-eap-naming-00:
394      *
395      *   Each attribute carried in the assertion SHOULD also be a GSS name
396      *   attribute.  The name of this attribute has three parts, all separated
397      *   by an ASCII space character.  The first part is
398      *   urn:ietf:params:gss-eap:saml-attr.  The second part is the URI for
399      *   the SAML attribute name format.  The final part is the name of the
400      *   SAML attribute.  If the mechanism performs an additional attribute
401      *   query, the retrieved attributes SHOULD be GSS-API name attributes
402      *   using the same name syntax.
403      */
404     /* For each attribute statement, look for an attribute match */
405     const vector <saml2::AttributeStatement *> &statements =
406         const_cast<const saml2::Assertion *>(assertion)->getAttributeStatements();
407
408     for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
409         s != statements.end();
410         ++s) {
411         const vector<saml2::Attribute*> &attrs =
412             const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
413
414         for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a != attrs.end(); ++a) {
415             const XMLCh *attributeName = (*a)->getName();
416             const XMLCh *attributeNameFormat = (*a)->getNameFormat();
417             XMLCh *qualifiedName;
418             XMLCh space[2] = { ' ', 0 };
419             gss_buffer_desc utf8;
420             bool ret;
421
422             qualifiedName = new XMLCh[XMLString::stringLen(attributeNameFormat) + 1 +
423                                       XMLString::stringLen(attributeName) + 1];
424             XMLString::copyString(qualifiedName, attributeNameFormat);
425             XMLString::catString(qualifiedName, space);
426             XMLString::catString(qualifiedName, attributeName);
427
428             utf8.value = (void *)toUTF8(qualifiedName);
429             utf8.length = strlen((char *)utf8.value);
430
431             ret = addAttribute(this, &utf8, data);
432
433             delete qualifiedName;
434
435             if (!ret)
436                 return ret;
437         }
438     }
439
440     return true;
441 }
442
443 static BaseRefVectorOf<XMLCh> *
444 decomposeAttributeName(const gss_buffer_t attr)
445 {
446     XMLCh *qualifiedAttr = new XMLCh[attr->length + 1];
447     XMLString::transcode((const char *)attr->value, qualifiedAttr, attr->length);
448
449     BaseRefVectorOf<XMLCh> *components = XMLString::tokenizeString(qualifiedAttr);
450
451     delete qualifiedAttr;
452
453     if (components->size() != 2) {
454         delete components;
455         components = NULL;
456     }
457
458     return components;
459 }
460
461 bool
462 gss_eap_saml_attr_provider::setAttribute(int complete,
463                                          const gss_buffer_t attr,
464                                          const gss_buffer_t value)
465 {
466     saml2::Assertion *assertion;
467     saml2::Attribute *attribute;
468     saml2::AttributeValue *attributeValue;
469     saml2::AttributeStatement *attributeStatement;
470
471     if (!getAssertion(NULL, &assertion, true))
472         return false;
473
474     if (assertion->getAttributeStatements().size() != 0) {
475         attributeStatement = assertion->getAttributeStatements().front();
476     } else {
477         attributeStatement = saml2::AttributeStatementBuilder::buildAttributeStatement();
478         assertion->getAttributeStatements().push_back(attributeStatement);
479     }
480
481     /* Check the attribute name consists of name format | whsp | name */
482     BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
483     if (components == NULL)
484         return false;
485
486     attribute = saml2::AttributeBuilder::buildAttribute();
487     attribute->setNameFormat(components->elementAt(0));
488     attribute->setName(components->elementAt(1));
489
490     XMLCh *xmlValue = new XMLCh[value->length + 1];
491     XMLString::transcode((const char *)value->value, xmlValue, attr->length);
492
493     attributeValue = saml2::AttributeValueBuilder::buildAttributeValue();
494     attributeValue->setTextContent(xmlValue);
495
496     attribute->getAttributeValues().push_back(attributeValue);
497
498     assert(attributeStatement != NULL);
499     attributeStatement->getAttributes().push_back(attribute);
500
501     delete components;
502     delete xmlValue;
503
504     return true;
505 }
506
507 bool
508 gss_eap_saml_attr_provider::deleteAttribute(const gss_buffer_t attr)
509 {
510     saml2::Assertion *assertion;
511     bool ret = false;
512
513     if (!getAssertion(NULL, &assertion) ||
514         assertion->getAttributeStatements().size() == 0)
515         return false;
516
517     /* Check the attribute name consists of name format | whsp | name */
518     BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
519     if (components == NULL)
520         return false;
521
522     /* For each attribute statement, look for an attribute match */
523     const vector<saml2::AttributeStatement *> &statements =
524         const_cast<const saml2::Assertion *>(assertion)->getAttributeStatements();
525
526     for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
527         s != statements.end();
528         ++s) {
529         const vector<saml2::Attribute *> &attrs =
530             const_cast<const saml2::AttributeStatement *>(*s)->getAttributes();
531         ssize_t index = -1, i = 0;
532
533         /* There's got to be an easier way to do this */
534         for (vector<saml2::Attribute *>::const_iterator a = attrs.begin();
535              a != attrs.end();
536              ++a) {
537             if (XMLString::equals((*a)->getNameFormat(), components->elementAt(0)) &&
538                 XMLString::equals((*a)->getName(), components->elementAt(1))) {
539                 index = i;
540                 break;
541             }
542             ++i;
543         }
544         if (index != -1) {
545             (*s)->getAttributes().erase((*s)->getAttributes().begin() + index);
546             ret = true;
547         }
548     }
549
550     delete components;
551
552     return ret;
553 }
554
555 bool
556 gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
557                                          int *authenticated,
558                                          int *complete,
559                                          const saml2::Attribute **pAttribute) const
560 {
561     saml2::Assertion *assertion;
562
563     if (authenticated != NULL)
564         *authenticated = false;
565     if (complete != NULL)
566         *complete = true;
567     *pAttribute = NULL;
568
569     if (!getAssertion(authenticated, &assertion) ||
570         assertion->getAttributeStatements().size() == 0)
571         return false;
572
573     /* Check the attribute name consists of name format | whsp | name */
574     BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
575     if (components == NULL)
576         return false;
577
578     /* For each attribute statement, look for an attribute match */
579     const vector <saml2::AttributeStatement *> &statements =
580         const_cast<const saml2::Assertion *>(assertion)->getAttributeStatements();
581     const saml2::Attribute *ret = NULL;
582
583     for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
584         s != statements.end();
585         ++s) {
586         const vector<saml2::Attribute *> &attrs =
587             const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
588
589         for (vector<saml2::Attribute *>::const_iterator a = attrs.begin(); a != attrs.end(); ++a) {
590             if (XMLString::equals((*a)->getNameFormat(), components->elementAt(0)) &&
591                 XMLString::equals((*a)->getName(), components->elementAt(1))) {
592                 ret = *a;
593                 break;
594             }
595         }
596
597         if (ret != NULL)
598             break;
599     }
600
601     delete components;
602
603     *pAttribute = ret;
604
605     return (ret != NULL);
606 }
607
608 bool
609 gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
610                                          int *authenticated,
611                                          int *complete,
612                                          gss_buffer_t value,
613                                          gss_buffer_t display_value,
614                                          int *more) const
615 {
616     const saml2::Attribute *a;
617     const saml2::AttributeValue *av;
618     int nvalues, i = *more;
619
620     *more = 0;
621
622     if (!getAttribute(attr, authenticated, complete, &a))
623         return false;
624
625     nvalues = a->getAttributeValues().size();
626
627     if (i == -1)
628         i = 0;
629     else if (i >= nvalues)
630         return false;
631 #ifdef __APPLE__
632     av = (const saml2::AttributeValue *)((void *)(a->getAttributeValues().at(i)));
633 #else
634     av = dynamic_cast<const saml2::AttributeValue *>(a->getAttributeValues().at(i));
635 #endif
636     if (av != NULL) {
637         if (value != NULL) {
638             value->value = toUTF8(av->getTextContent(), true);
639             value->length = strlen((char *)value->value);
640         }
641         if (display_value != NULL) {
642             display_value->value = toUTF8(av->getTextContent(), true);
643             display_value->length = strlen((char *)value->value);
644         }
645     }
646
647     if (nvalues > ++i)
648         *more = i;
649
650     return true;
651 }
652
653 gss_any_t
654 gss_eap_saml_attr_provider::mapToAny(int authenticated,
655                                      gss_buffer_t type_id) const
656 {
657     return (gss_any_t)NULL;
658 }
659
660 void
661 gss_eap_saml_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
662                                                   gss_any_t input) const
663 {
664 }
665
666 void
667 gss_eap_saml_attr_provider::exportToBuffer(gss_buffer_t buffer) const
668 {
669     buffer->length = 0;
670     buffer->value = NULL;
671 }
672
673 bool
674 gss_eap_saml_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
675                                            const gss_buffer_t buffer)
676 {
677     return gss_eap_attr_provider::initFromBuffer(ctx, buffer);
678 }
679
680 bool
681 gss_eap_saml_attr_provider::init(void)
682 {
683     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML,
684                                        "urn:ietf:params:gss-eap:saml-attr",
685                                        gss_eap_saml_attr_provider::createAttrContext);
686     return true;
687 }
688
689 void
690 gss_eap_saml_attr_provider::finalize(void)
691 {
692     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML);
693 }
694
695 gss_eap_attr_provider *
696 gss_eap_saml_attr_provider::createAttrContext(void)
697 {
698     return new gss_eap_saml_attr_provider;
699 }
700
701 OM_uint32
702 gssEapSamlAttrProvidersInit(OM_uint32 *minor)
703 {
704     if (!gss_eap_saml_assertion_provider::init() ||
705         !gss_eap_saml_attr_provider::init()) {
706         *minor = GSSEAP_SAML_INIT_FAILURE;
707         return GSS_S_FAILURE;
708     }
709
710     return GSS_S_COMPLETE;
711 }
712
713 OM_uint32
714 gssEapSamlAttrProvidersFinalize(OM_uint32 *minor)
715 {
716     gss_eap_saml_attr_provider::finalize();
717     gss_eap_saml_assertion_provider::finalize();
718     return GSS_S_COMPLETE;
719 }