properly support multiple attribute statements
[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 /*
333  * gss_eap_saml_attr_provider is for retrieving the underlying attributes.
334  */
335 bool
336 gss_eap_saml_attr_provider::getAssertion(int *authenticated,
337                                          const saml2::Assertion **pAssertion) const
338 {
339     const gss_eap_saml_assertion_provider *saml;
340
341     if (authenticated != NULL)
342         *authenticated = false;
343     if (pAssertion != NULL)
344         *pAssertion = NULL;
345
346     saml = static_cast<const gss_eap_saml_assertion_provider *>
347         (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION));
348     if (saml == NULL)
349         return false;
350
351     if (authenticated != NULL)
352         *authenticated = saml->authenticated();
353     if (pAssertion != NULL)
354         *pAssertion = saml->getAssertion();
355
356     return (saml->getAssertion() != NULL);
357 }
358
359 bool
360 gss_eap_saml_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
361                                               void *data) const
362 {
363     const saml2::Assertion *assertion;
364     int authenticated;
365
366     if (!getAssertion(&authenticated, &assertion))
367         return true;
368
369     /*
370      * Note: the first prefix is added by the attribute provider manager
371      *
372      * From draft-hartman-gss-eap-naming-00:
373      *
374      *   Each attribute carried in the assertion SHOULD also be a GSS name
375      *   attribute.  The name of this attribute has three parts, all separated
376      *   by an ASCII space character.  The first part is
377      *   urn:ietf:params:gss-eap:saml-attr.  The second part is the URI for
378      *   the SAML attribute name format.  The final part is the name of the
379      *   SAML attribute.  If the mechanism performs an additional attribute
380      *   query, the retrieved attributes SHOULD be GSS-API name attributes
381      *   using the same name syntax.
382      */
383     /* For each attribute statement, look for an attribute match */
384     const vector <saml2::AttributeStatement *>&statements =
385         assertion->getAttributeStatements();
386
387     for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
388         s != statements.end();
389         ++s) {
390         const vector<saml2::Attribute*>& attrs =
391             const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
392
393         for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a != attrs.end(); ++a) {
394             const XMLCh *attributeName = (*a)->getName();
395             const XMLCh *attributeNameFormat = (*a)->getNameFormat();
396             XMLCh *qualifiedName;
397             XMLCh space[2] = { ' ', 0 };
398             gss_buffer_desc utf8;
399             bool ret;
400
401             qualifiedName = new XMLCh[XMLString::stringLen(attributeNameFormat) + 1 +
402                                       XMLString::stringLen(attributeName) + 1];
403             XMLString::copyString(qualifiedName, attributeNameFormat);
404             XMLString::catString(qualifiedName, space);
405             XMLString::catString(qualifiedName, attributeName);
406
407             utf8.value = (void *)toUTF8(qualifiedName);
408             utf8.length = strlen((char *)utf8.value);
409
410             ret = addAttribute(this, &utf8, data);
411
412             delete qualifiedName;
413
414             if (!ret)
415                 return ret;
416         }
417     }
418
419     return true;
420 }
421
422 ssize_t
423 gss_eap_saml_attr_provider::getAttributeIndex(const gss_buffer_t attr) const
424 {
425     return -1;
426 }
427
428 bool
429 gss_eap_saml_attr_provider::setAttribute(int complete,
430                                          const gss_buffer_t attr,
431                                          const gss_buffer_t value)
432 {
433     return false;
434 }
435
436 bool
437 gss_eap_saml_attr_provider::deleteAttribute(const gss_buffer_t value)
438 {
439     return false;
440 }
441
442 static BaseRefVectorOf<XMLCh> *
443 decomposeAttributeName(const gss_buffer_t attr)
444 {
445     XMLCh *qualifiedAttr = new XMLCh[attr->length + 1];
446     XMLString::transcode((const char *)attr->value, qualifiedAttr, attr->length);
447
448     BaseRefVectorOf<XMLCh> *components = XMLString::tokenizeString(qualifiedAttr);
449
450     delete qualifiedAttr;
451
452     return components;
453 }
454
455 bool
456 gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
457                                          int *authenticated,
458                                          int *complete,
459                                          const saml2::Attribute **pAttribute) const
460 {
461     const saml2::Assertion *assertion;
462
463     if (authenticated != NULL)
464         *authenticated = false;
465     if (complete != NULL)
466         *complete = true;
467     *pAttribute = NULL;
468
469     if (!getAssertion(authenticated, &assertion) ||
470         assertion->getAttributeStatements().size() == 0)
471         return false;
472
473     /* Check the attribute name consists of name format | whsp | name */
474     BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
475     if (components == NULL || components->size() != 2) {
476         delete components;
477         return false;
478     }
479
480     /* For each attribute statement, look for an attribute match */
481     const vector <saml2::AttributeStatement *>&statements =
482         assertion->getAttributeStatements();
483     const saml2::Attribute *ret = NULL;
484
485     for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
486         s != statements.end();
487         ++s) {
488         const vector<saml2::Attribute*>& attrs =
489             const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
490
491         for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a != attrs.end(); ++a) {
492             if (XMLString::equals((*a)->getNameFormat(), components->elementAt(0)) &&
493                 XMLString::equals((*a)->getName(), components->elementAt(1))) {
494                 ret = *a;
495                 break;
496             }
497         }
498
499         if (ret != NULL)
500             break;
501     }
502
503     delete components;
504
505     *pAttribute = ret;
506
507     return (ret != NULL);
508 }
509
510 bool
511 gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
512                                          int *authenticated,
513                                          int *complete,
514                                          gss_buffer_t value,
515                                          gss_buffer_t display_value,
516                                          int *more) const
517 {
518     const saml2::Attribute *a;
519     const saml2::AttributeValue *av;
520     int nvalues, i = *more;
521
522     *more = 0;
523
524     if (!getAttribute(attr, authenticated, complete, &a))
525         return false;
526
527     nvalues = a->getAttributeValues().size();
528
529     if (i == -1)
530         i = 0;
531     else if (i >= nvalues)
532         return false;
533 #ifdef __APPLE__
534     av = (const saml2::AttributeValue *)((void *)(a->getAttributeValues().at(i)));
535 #else
536     av = dynamic_cast<const saml2::AttributeValue *>(a->getAttributeValues().at(i));
537 #endif
538     if (av != NULL) {
539         if (value != NULL) {
540             value->value = toUTF8(av->getTextContent(), true);
541             value->length = strlen((char *)value->value);
542         }
543         if (display_value != NULL) {
544             display_value->value = toUTF8(av->getTextContent(), true);
545             display_value->length = strlen((char *)value->value);
546         }
547     }
548
549     if (nvalues > ++i)
550         *more = i;
551
552     return true;
553 }
554
555 gss_any_t
556 gss_eap_saml_attr_provider::mapToAny(int authenticated,
557                                      gss_buffer_t type_id) const
558 {
559     return (gss_any_t)NULL;
560 }
561
562 void
563 gss_eap_saml_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
564                                                   gss_any_t input) const
565 {
566 }
567
568 void
569 gss_eap_saml_attr_provider::exportToBuffer(gss_buffer_t buffer) const
570 {
571     buffer->length = 0;
572     buffer->value = NULL;
573 }
574
575 bool
576 gss_eap_saml_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
577                                            const gss_buffer_t buffer)
578 {
579     return gss_eap_attr_provider::initFromBuffer(ctx, buffer);
580 }
581
582 bool
583 gss_eap_saml_attr_provider::init(void)
584 {
585     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML,
586                                        "urn:ietf:params:gss-eap:saml-attr",
587                                        gss_eap_saml_attr_provider::createAttrContext);
588     return true;
589 }
590
591 void
592 gss_eap_saml_attr_provider::finalize(void)
593 {
594     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML);
595 }
596
597 gss_eap_attr_provider *
598 gss_eap_saml_attr_provider::createAttrContext(void)
599 {
600     return new gss_eap_saml_attr_provider;
601 }
602
603 OM_uint32
604 gssEapSamlAttrProvidersInit(OM_uint32 *minor)
605 {
606     if (!gss_eap_saml_assertion_provider::init() ||
607         !gss_eap_saml_attr_provider::init()) {
608         *minor = GSSEAP_SAML_INIT_FAILURE;
609         return GSS_S_FAILURE;
610     }
611
612     return GSS_S_COMPLETE;
613 }
614
615 OM_uint32
616 gssEapSamlAttrProvidersFinalize(OM_uint32 *minor)
617 {
618     gss_eap_saml_attr_provider::finalize();
619     gss_eap_saml_assertion_provider::finalize();
620     return GSS_S_COMPLETE;
621 }