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