f9a74ee469923f0fdd5f87bc1e7a04a42bc368e9
[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 void
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     }
194 }
195
196 void
197 gss_eap_saml_assertion_provider::deleteAttribute(const gss_buffer_t value)
198 {
199     delete m_assertion;
200     m_assertion = NULL;
201     m_authenticated = false;
202 }
203
204 time_t
205 gss_eap_saml_assertion_provider::getExpiryTime(void) const
206 {
207     saml2::Conditions *conditions;
208     time_t expiryTime = 0;
209
210     if (m_assertion == NULL)
211         return 0;
212
213     conditions = m_assertion->getConditions();
214
215     if (conditions != NULL && conditions->getNotOnOrAfter() != NULL)
216         expiryTime = conditions->getNotOnOrAfter()->getEpoch();
217
218     return expiryTime;
219 }
220
221 bool
222 gss_eap_saml_assertion_provider::getAttribute(const gss_buffer_t attr,
223                                               int *authenticated,
224                                               int *complete,
225                                               gss_buffer_t value,
226                                               gss_buffer_t display_value,
227                                               int *more) const
228 {
229     string str;
230
231     if (attr != GSS_C_NO_BUFFER && attr->length != 0)
232         return false;
233
234     if (m_assertion == NULL)
235         return false;
236
237     if (*more != -1)
238         return false;
239
240     if (authenticated != NULL)
241         *authenticated = m_authenticated;
242     if (complete != NULL)
243         *complete = true;
244
245     XMLHelper::serialize(m_assertion->marshall((DOMDocument *)NULL), str);
246
247     duplicateBuffer(str, value);
248     *more = 0;
249
250     return true;
251 }
252
253 gss_any_t
254 gss_eap_saml_assertion_provider::mapToAny(int authenticated,
255                                           gss_buffer_t type_id) const
256 {
257     if (authenticated && !m_authenticated)
258         return (gss_any_t)NULL;
259
260     return (gss_any_t)m_assertion;
261 }
262
263 void
264 gss_eap_saml_assertion_provider::releaseAnyNameMapping(gss_buffer_t type_id,
265                                                        gss_any_t input) const
266 {
267     delete ((saml2::Assertion *)input);
268 }
269
270 void
271 gss_eap_saml_assertion_provider::exportToBuffer(gss_buffer_t buffer) const
272 {
273     ostringstream sink;
274     string str;
275
276     buffer->length = 0;
277     buffer->value = NULL;
278
279     if (m_assertion == NULL)
280         return;
281
282     sink << *m_assertion;
283     str = sink.str();
284
285     duplicateBuffer(str, buffer);
286 }
287
288 bool
289 gss_eap_saml_assertion_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
290                                                 const gss_buffer_t buffer)
291 {
292     if (!gss_eap_attr_provider::initFromBuffer(ctx, buffer))
293         return false;
294
295     if (buffer->length == 0)
296         return true;
297
298     assert(m_assertion == NULL);
299
300     setAssertion(buffer);
301     /* TODO XXX how to propagate authenticated flag? */
302
303     return true;
304 }
305
306 bool
307 gss_eap_saml_assertion_provider::init(void)
308 {
309     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML_ASSERTION,
310                                        "urn:ietf:params:gss-eap:saml-aaa-assertion",
311                                        gss_eap_saml_assertion_provider::createAttrContext);
312     return true;
313 }
314
315 void
316 gss_eap_saml_assertion_provider::finalize(void)
317 {
318     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML_ASSERTION);
319 }
320
321 gss_eap_attr_provider *
322 gss_eap_saml_assertion_provider::createAttrContext(void)
323 {
324     return new gss_eap_saml_assertion_provider;
325 }
326
327 /*
328  * gss_eap_saml_attr_provider is for retrieving the underlying attributes.
329  */
330 bool
331 gss_eap_saml_attr_provider::getAssertion(int *authenticated,
332                                          const saml2::Assertion **pAssertion) const
333 {
334     const gss_eap_saml_assertion_provider *saml;
335
336     if (authenticated != NULL)
337         *authenticated = false;
338     if (pAssertion != NULL)
339         *pAssertion = NULL;
340
341     saml = static_cast<const gss_eap_saml_assertion_provider *>
342         (m_manager->getProvider(ATTR_TYPE_SAML_ASSERTION));
343     if (saml == NULL)
344         return false;
345
346     if (authenticated != NULL)
347         *authenticated = saml->authenticated();
348     if (pAssertion != NULL)
349         *pAssertion = saml->getAssertion();
350
351     return (saml->getAssertion() != NULL);
352 }
353
354 bool
355 gss_eap_saml_attr_provider::getAttributeTypes(gss_eap_attr_enumeration_cb addAttribute,
356                                               void *data) const
357 {
358     const saml2::Assertion *assertion;
359     bool ret = true;
360     int authenticated;
361
362     if (!getAssertion(&authenticated, &assertion))
363         return true;
364
365     /*
366      * Note: the first prefix is added by the attribute provider manager
367      *
368      * From draft-hartman-gss-eap-naming-00:
369      *
370      *   Each attribute carried in the assertion SHOULD also be a GSS name
371      *   attribute.  The name of this attribute has three parts, all separated
372      *   by an ASCII space character.  The first part is
373      *   urn:ietf:params:gss-eap:saml-attr.  The second part is the URI for
374      *   the SAML attribute name format.  The final part is the name of the
375      *   SAML attribute.  If the mechanism performs an additional attribute
376      *   query, the retrieved attributes SHOULD be GSS-API name attributes
377      *   using the same name syntax.
378      */
379     const vector<saml2::Attribute*>& attrs2 =
380         const_cast<const saml2::AttributeStatement*>(assertion->getAttributeStatements().front())->getAttributes();
381     for (vector<saml2::Attribute*>::const_iterator a = attrs2.begin();
382         a != attrs2.end();
383         ++a)
384     {
385         const XMLCh *attributeName = (*a)->getName();
386         const XMLCh *attributeNameFormat = (*a)->getNameFormat();
387         XMLCh *qualifiedName;
388         XMLCh space[2] = { ' ', 0 };
389         gss_buffer_desc utf8;
390
391         qualifiedName = new XMLCh[XMLString::stringLen(attributeNameFormat) + 1 +
392                                   XMLString::stringLen(attributeName) + 1];
393         XMLString::copyString(qualifiedName, attributeNameFormat);
394         XMLString::catString(qualifiedName, space);
395         XMLString::catString(qualifiedName, attributeName);
396
397         utf8.value = (void *)toUTF8(qualifiedName);
398         utf8.length = strlen((char *)utf8.value);
399
400         ret = addAttribute(this, &utf8, data);
401
402         delete qualifiedName;
403
404         if (!ret)
405             break;
406     }
407
408     return ret;
409 }
410
411 void
412 gss_eap_saml_attr_provider::setAttribute(int complete,
413                                          const gss_buffer_t attr,
414                                          const gss_buffer_t value)
415 {
416 }
417
418 void
419 gss_eap_saml_attr_provider::deleteAttribute(const gss_buffer_t value)
420 {
421 }
422
423 static BaseRefVectorOf<XMLCh> *
424 decomposeAttributeName(const gss_buffer_t attr)
425 {
426     XMLCh *qualifiedAttr = new XMLCh[attr->length + 1];
427     XMLString::transcode((const char *)attr->value, qualifiedAttr, attr->length);
428
429     BaseRefVectorOf<XMLCh> *components = XMLString::tokenizeString(qualifiedAttr);
430
431     delete qualifiedAttr;
432
433     return components;
434 }
435
436 bool
437 gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
438                                          int *authenticated,
439                                          int *complete,
440                                          const saml2::Attribute **pAttribute) const
441 {
442     const saml2::Assertion *assertion;
443
444     if (authenticated != NULL)
445         *authenticated = false;
446     if (complete != NULL)
447         *complete = true;
448     *pAttribute = NULL;
449
450     if (!getAssertion(authenticated, &assertion) ||
451         assertion->getAttributeStatements().size() == 0)
452         return false;
453
454     /* Check the attribute name consists of name format | whsp | name */
455     BaseRefVectorOf<XMLCh> *components = decomposeAttributeName(attr);
456     if (components == NULL || components->size() != 2) {
457         delete components;
458         return false;
459     }
460
461     /* For each attribute statement, look for an attribute match */
462     const vector <saml2::AttributeStatement *>&statements =
463         assertion->getAttributeStatements();
464     const saml2::Attribute *ret = NULL;
465
466     for (vector<saml2::AttributeStatement *>::const_iterator s = statements.begin();
467         s != statements.end();
468         ++s) {
469         const vector<saml2::Attribute*>& attrs =
470             const_cast<const saml2::AttributeStatement*>(*s)->getAttributes();
471
472         for (vector<saml2::Attribute*>::const_iterator a = attrs.begin(); a != attrs.end(); ++a) {
473             if (XMLString::equals((*a)->getNameFormat(), components->elementAt(0)) &&
474                 XMLString::equals((*a)->getName(), components->elementAt(1))) {
475                 ret = *a;
476                 break;
477             }
478         }
479
480         if (ret != NULL)
481             break;
482     }
483
484     delete components;
485
486     *pAttribute = ret;
487
488     return (ret != NULL);
489 }
490
491 bool
492 gss_eap_saml_attr_provider::getAttribute(const gss_buffer_t attr,
493                                          int *authenticated,
494                                          int *complete,
495                                          gss_buffer_t value,
496                                          gss_buffer_t display_value,
497                                          int *more) const
498 {
499     const saml2::Attribute *a;
500     const saml2::AttributeValue *av;
501     int nvalues, i = *more;
502
503     *more = 0;
504
505     if (!getAttribute(attr, authenticated, complete, &a))
506         return false;
507
508     nvalues = a->getAttributeValues().size();
509
510     if (i == -1)
511         i = 0;
512     else if (i >= nvalues)
513         return false;
514 #ifdef __APPLE__
515     av = (const saml2::AttributeValue *)((void *)(a->getAttributeValues().at(i)));
516 #else
517     av = dynamic_cast<const saml2::AttributeValue *>(a->getAttributeValues().at(i));
518 #endif
519     if (av != NULL) {
520         if (value != NULL) {
521             value->value = toUTF8(av->getTextContent(), true);
522             value->length = strlen((char *)value->value);
523         }
524         if (display_value != NULL) {
525             display_value->value = toUTF8(av->getTextContent(), true);
526             display_value->length = strlen((char *)value->value);
527         }
528     }
529
530     if (nvalues > ++i)
531         *more = i;
532
533     return true;
534 }
535
536 gss_any_t
537 gss_eap_saml_attr_provider::mapToAny(int authenticated,
538                                      gss_buffer_t type_id) const
539 {
540     return (gss_any_t)NULL;
541 }
542
543 void
544 gss_eap_saml_attr_provider::releaseAnyNameMapping(gss_buffer_t type_id,
545                                                   gss_any_t input) const
546 {
547 }
548
549 void
550 gss_eap_saml_attr_provider::exportToBuffer(gss_buffer_t buffer) const
551 {
552     buffer->length = 0;
553     buffer->value = NULL;
554 }
555
556 bool
557 gss_eap_saml_attr_provider::initFromBuffer(const gss_eap_attr_ctx *ctx,
558                                            const gss_buffer_t buffer)
559 {
560     return gss_eap_attr_provider::initFromBuffer(ctx, buffer);
561 }
562
563 bool
564 gss_eap_saml_attr_provider::init(void)
565 {
566     gss_eap_attr_ctx::registerProvider(ATTR_TYPE_SAML,
567                                        "urn:ietf:params:gss-eap:saml-attr",
568                                        gss_eap_saml_attr_provider::createAttrContext);
569     return true;
570 }
571
572 void
573 gss_eap_saml_attr_provider::finalize(void)
574 {
575     gss_eap_attr_ctx::unregisterProvider(ATTR_TYPE_SAML);
576 }
577
578 gss_eap_attr_provider *
579 gss_eap_saml_attr_provider::createAttrContext(void)
580 {
581     return new gss_eap_saml_attr_provider;
582 }
583
584 OM_uint32
585 gssEapSamlAttrProvidersInit(OM_uint32 *minor)
586 {
587     if (gss_eap_saml_assertion_provider::init() &&
588         gss_eap_saml_attr_provider::init())
589         return GSS_S_COMPLETE;
590
591     return GSS_S_FAILURE;
592 }
593
594 OM_uint32
595 gssEapSamlAttrProvidersFinalize(OM_uint32 *minor)
596 {
597     gss_eap_saml_attr_provider::finalize();
598     gss_eap_saml_assertion_provider::finalize();
599     return GSS_S_COMPLETE;
600 }