d3c99d311963d3d167974ab3e83410861beeea58
[moonshot.git] / moonshot / mech_eap / util_attr.cpp
1 /*
2  * Copyright (c) 2011, 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 /*
34  * Attribute provider mechanism.
35  */
36
37 #include "gssapiP_eap.h"
38
39 #include <typeinfo>
40 #include <string>
41 #include <sstream>
42 #include <exception>
43 #include <new>
44
45 /* lazy initialisation */
46 #ifdef WIN32
47 static volatile OM_uint32 gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE;
48 #else
49 static GSSEAP_THREAD_ONCE gssEapAttrProvidersInitOnce = GSSEAP_ONCE_INITIALIZER;
50 static OM_uint32 gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE;
51 #endif
52
53 GSSEAP_ONCE_CALLBACK(gssEapAttrProvidersInitInternal)
54 {
55     OM_uint32 major, minor;
56
57     GSSEAP_ASSERT(gssEapAttrProvidersInitStatus == GSS_S_UNAVAILABLE);
58
59     json_set_alloc_funcs(GSSEAP_MALLOC, GSSEAP_FREE);
60
61     major = gssEapRadiusAttrProviderInit(&minor);
62     if (GSS_ERROR(major))
63         goto cleanup;
64
65 #ifdef HAVE_OPENSAML
66     major = gssEapSamlAttrProvidersInit(&minor);
67     if (GSS_ERROR(major))
68         goto cleanup;
69 #endif
70
71 #ifdef HAVE_SHIBRESOLVER
72     /* Allow Shibboleth initialization failure to be non-fatal */
73     gssEapLocalAttrProviderInit(&minor);
74 #endif
75
76 cleanup:
77 #ifdef GSSEAP_DEBUG
78     GSSEAP_ASSERT(major == GSS_S_COMPLETE);
79 #endif
80
81 #ifdef WIN32
82     InterlockedCompareExchangeRelease(&gssEapAttrProvidersInitStatus,
83                                       major, GSS_S_UNAVAILABLE);
84 #else
85     gssEapAttrProvidersInitStatus = major;
86 #endif
87
88     GSSEAP_ONCE_LEAVE;
89 }
90
91 static OM_uint32
92 gssEapAttrProvidersInit(OM_uint32 *minor)
93 {
94 #ifdef WIN32
95     if (gssEapAttrProvidersInitStatus == GSS_S_UNAVAILABLE)
96         gssEapAttrProvidersInitInternal();
97 #else
98     GSSEAP_ONCE(&gssEapAttrProvidersInitOnce, gssEapAttrProvidersInitInternal);
99 #endif
100
101     if (GSS_ERROR(gssEapAttrProvidersInitStatus))
102         *minor = GSSEAP_NO_ATTR_PROVIDERS;
103
104     return gssEapAttrProvidersInitStatus;
105 }
106
107 OM_uint32
108 gssEapAttrProvidersFinalize(OM_uint32 *minor)
109 {
110     if (gssEapAttrProvidersInitStatus == GSS_S_COMPLETE) {
111 #ifdef HAVE_SHIBRESOLVER
112         gssEapLocalAttrProviderFinalize(minor);
113 #endif
114 #ifdef HAVE_OPENSAML
115         gssEapSamlAttrProvidersFinalize(minor);
116 #endif
117         gssEapRadiusAttrProviderFinalize(minor);
118
119         gssEapAttrProvidersInitStatus = GSS_S_UNAVAILABLE;
120     }
121
122     return GSS_S_COMPLETE;
123 }
124
125 static gss_eap_attr_create_provider gssEapAttrFactories[ATTR_TYPE_MAX + 1];
126
127 /*
128  * Register a provider for a particular type and prefix
129  */
130 void
131 gss_eap_attr_ctx::registerProvider(unsigned int type,
132                                    gss_eap_attr_create_provider factory)
133 {
134     GSSEAP_ASSERT(type <= ATTR_TYPE_MAX);
135
136     GSSEAP_ASSERT(gssEapAttrFactories[type] == NULL);
137
138     gssEapAttrFactories[type] = factory;
139 }
140
141 /*
142  * Unregister a provider
143  */
144 void
145 gss_eap_attr_ctx::unregisterProvider(unsigned int type)
146 {
147     GSSEAP_ASSERT(type <= ATTR_TYPE_MAX);
148
149     gssEapAttrFactories[type] = NULL;
150 }
151
152 /*
153  * Create an attribute context, that manages instances of providers
154  */
155 gss_eap_attr_ctx::gss_eap_attr_ctx(void)
156 {
157     m_flags = 0;
158
159     for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
160         gss_eap_attr_provider *provider;
161
162         if (gssEapAttrFactories[i] != NULL) {
163             provider = (gssEapAttrFactories[i])();
164         } else {
165             provider = NULL;
166         }
167
168         m_providers[i] = provider;
169     }
170 }
171
172 /*
173  * Convert an attribute prefix to a type
174  */
175 unsigned int
176 gss_eap_attr_ctx::attributePrefixToType(const gss_buffer_t prefix) const
177 {
178     unsigned int i;
179
180     for (i = ATTR_TYPE_MIN; i < ATTR_TYPE_MAX; i++) {
181         const char *pprefix;
182
183         if (!providerEnabled(i))
184             continue;
185
186         pprefix = m_providers[i]->prefix();
187         if (pprefix == NULL)
188             continue;
189
190         if (strlen(pprefix) == prefix->length &&
191             memcmp(pprefix, prefix->value, prefix->length) == 0)
192             return i;
193     }
194
195     return ATTR_TYPE_LOCAL;
196 }
197
198 /*
199  * Convert a type to an attribute prefix
200  */
201 gss_buffer_desc
202 gss_eap_attr_ctx::attributeTypeToPrefix(unsigned int type) const
203 {
204     gss_buffer_desc prefix = GSS_C_EMPTY_BUFFER;
205
206     if (type < ATTR_TYPE_MIN || type >= ATTR_TYPE_MAX)
207         return prefix;
208
209     if (!providerEnabled(type))
210         return prefix;
211
212     prefix.value = (void *)m_providers[type]->prefix();
213     if (prefix.value != NULL)
214         prefix.length = strlen((char *)prefix.value);
215
216     return prefix;
217 }
218
219 bool
220 gss_eap_attr_ctx::providerEnabled(unsigned int type) const
221 {
222     if (type == ATTR_TYPE_LOCAL &&
223         (m_flags & ATTR_FLAG_DISABLE_LOCAL))
224         return false;
225
226     if (m_providers[type] == NULL)
227         return false;
228
229     return true;
230 }
231
232 void
233 gss_eap_attr_ctx::releaseProvider(unsigned int type)
234 {
235     delete m_providers[type];
236     m_providers[type] = NULL;
237 }
238
239 /*
240  * Initialize a context from an existing context.
241  */
242 bool
243 gss_eap_attr_ctx::initWithExistingContext(const gss_eap_attr_ctx *manager)
244 {
245     bool ret = true;
246
247     m_flags = manager->m_flags;
248
249     for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
250         gss_eap_attr_provider *provider;
251
252         if (!providerEnabled(i)) {
253             releaseProvider(i);
254             continue;
255         }
256
257         provider = m_providers[i];
258
259         ret = provider->initWithExistingContext(this,
260                                                 manager->m_providers[i]);
261         if (ret == false) {
262             releaseProvider(i);
263             break;
264         }
265     }
266
267     return ret;
268 }
269
270 /*
271  * Initialize a context from a GSS credential and context.
272  */
273 bool
274 gss_eap_attr_ctx::initWithGssContext(const gss_cred_id_t cred,
275                                      const gss_ctx_id_t ctx)
276 {
277     bool ret = true;
278
279     if (cred != GSS_C_NO_CREDENTIAL &&
280         (cred->flags & GSS_EAP_DISABLE_LOCAL_ATTRS_FLAG)) {
281         m_flags |= ATTR_FLAG_DISABLE_LOCAL;
282     }
283
284     for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
285         gss_eap_attr_provider *provider;
286
287         if (!providerEnabled(i)) {
288             releaseProvider(i);
289             continue;
290         }
291
292         provider = m_providers[i];
293
294         ret = provider->initWithGssContext(this, cred, ctx);
295         if (ret == false) {
296             releaseProvider(i);
297             break;
298         }
299     }
300
301     return ret;
302 }
303
304 bool
305 gss_eap_attr_ctx::initWithJsonObject(JSONObject &obj)
306 {
307     bool ret = false;
308     bool foundSource[ATTR_TYPE_MAX + 1];
309     unsigned int type;
310
311     for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++)
312         foundSource[type] = false;
313
314     if (obj["version"].integer() != 1)
315         return false;
316
317     m_flags = obj["flags"].integer();
318
319     JSONObject sources = obj["sources"];
320
321     /* Initialize providers from serialized state */
322     for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++) {
323         gss_eap_attr_provider *provider;
324         const char *key;
325
326         if (!providerEnabled(type)) {
327             releaseProvider(type);
328             continue;
329         }
330
331         provider = m_providers[type];
332         key = provider->name();
333         if (key == NULL)
334             continue;
335
336         JSONObject source = sources.get(key);
337         if (!source.isNull() &&
338             !provider->initWithJsonObject(this, source)) {
339             releaseProvider(type);
340             return false;
341         }
342
343         foundSource[type] = true;
344     }
345
346     /* Initialize remaining providers from initialized providers */
347     for (type = ATTR_TYPE_MIN; type <= ATTR_TYPE_MAX; type++) {
348         gss_eap_attr_provider *provider;
349
350         if (foundSource[type] || !providerEnabled(type))
351             continue;
352
353         provider = m_providers[type];
354
355         ret = provider->initWithGssContext(this,
356                                            GSS_C_NO_CREDENTIAL,
357                                            GSS_C_NO_CONTEXT);
358         if (ret == false) {
359             releaseProvider(type);
360             return false;
361         }
362     }
363
364     return true;
365 }
366
367 JSONObject
368 gss_eap_attr_ctx::jsonRepresentation(void) const
369 {
370     JSONObject obj, sources;
371     unsigned int i;
372
373     obj.set("version", 1);
374     obj.set("flags", m_flags);
375
376     for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
377         gss_eap_attr_provider *provider;
378         const char *key;
379
380         provider = m_providers[i];
381         if (provider == NULL)
382             continue; /* provider not initialised */
383
384         key = provider->name();
385         if (key == NULL)
386             continue; /* provider does not have state */
387
388         JSONObject source = provider->jsonRepresentation();
389         sources.set(key, source);
390     }
391
392     obj.set("sources", sources);
393
394     return obj;
395 }
396
397 /*
398  * Initialize a context from an exported context or name token
399  */
400 bool
401 gss_eap_attr_ctx::initWithBuffer(const gss_buffer_t buffer)
402 {
403     OM_uint32 major, minor;
404     bool ret;
405     char *s;
406     json_error_t error;
407
408     major = bufferToString(&minor, buffer, &s);
409     if (GSS_ERROR(major))
410         return false;
411
412     JSONObject obj = JSONObject::load(s, 0, &error);
413     if (!obj.isNull()) {
414         ret = initWithJsonObject(obj);
415     } else
416         ret = false;
417
418     GSSEAP_FREE(s);
419
420     return ret;
421 }
422
423 gss_eap_attr_ctx::~gss_eap_attr_ctx(void)
424 {
425     for (unsigned int i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++)
426         delete m_providers[i];
427 }
428
429 /*
430  * Locate provider for a given type
431  */
432 gss_eap_attr_provider *
433 gss_eap_attr_ctx::getProvider(unsigned int type) const
434 {
435     GSSEAP_ASSERT(type >= ATTR_TYPE_MIN && type <= ATTR_TYPE_MAX);
436     return m_providers[type];
437 }
438
439 /*
440  * Get primary provider. Only the primary provider is serialised when
441  * gss_export_sec_context() or gss_export_name_composite() is called.
442  */
443 gss_eap_attr_provider *
444 gss_eap_attr_ctx::getPrimaryProvider(void) const
445 {
446     return m_providers[ATTR_TYPE_MIN];
447 }
448
449 /*
450  * Set an attribute
451  */
452 bool
453 gss_eap_attr_ctx::setAttribute(int complete,
454                                const gss_buffer_t attr,
455                                const gss_buffer_t value)
456 {
457     gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
458     unsigned int type;
459     gss_eap_attr_provider *provider;
460     bool ret = false;
461
462     decomposeAttributeName(attr, &type, &suffix);
463
464     provider = m_providers[type];
465     if (provider != NULL) {
466         ret = provider->setAttribute(complete,
467                                      (type == ATTR_TYPE_LOCAL) ? attr : &suffix,
468                                      value);
469     }
470
471     return ret;
472 }
473
474 /*
475  * Delete an attrbiute
476  */
477 bool
478 gss_eap_attr_ctx::deleteAttribute(const gss_buffer_t attr)
479 {
480     gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
481     unsigned int type;
482     gss_eap_attr_provider *provider;
483     bool ret = false;
484
485     decomposeAttributeName(attr, &type, &suffix);
486
487     provider = m_providers[type];
488     if (provider != NULL) {
489         ret = provider->deleteAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix);
490     }
491
492     return ret;
493 }
494
495 /*
496  * Enumerate attribute types with callback
497  */
498 bool
499 gss_eap_attr_ctx::getAttributeTypes(gss_eap_attr_enumeration_cb cb, void *data) const
500 {
501     bool ret = false;
502     size_t i;
503
504     for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
505         gss_eap_attr_provider *provider = m_providers[i];
506
507         if (provider == NULL)
508             continue;
509
510         ret = provider->getAttributeTypes(cb, data);
511         if (ret == false)
512             break;
513     }
514
515     return ret;
516 }
517
518 struct eap_gss_get_attr_types_args {
519     unsigned int type;
520     gss_buffer_set_t attrs;
521 };
522
523 static bool
524 addAttribute(const gss_eap_attr_ctx *manager,
525              const gss_eap_attr_provider *provider GSSEAP_UNUSED,
526              const gss_buffer_t attribute,
527              void *data)
528 {
529     eap_gss_get_attr_types_args *args = (eap_gss_get_attr_types_args *)data;
530     gss_buffer_desc qualified;
531     OM_uint32 major, minor;
532
533     if (args->type != ATTR_TYPE_LOCAL) {
534         manager->composeAttributeName(args->type, attribute, &qualified);
535         major = gss_add_buffer_set_member(&minor, &qualified, &args->attrs);
536         gss_release_buffer(&minor, &qualified);
537     } else {
538         major = gss_add_buffer_set_member(&minor, attribute, &args->attrs);
539     }
540
541     return GSS_ERROR(major) == false;
542 }
543
544 /*
545  * Enumerate attribute types, output is buffer set
546  */
547 bool
548 gss_eap_attr_ctx::getAttributeTypes(gss_buffer_set_t *attrs)
549 {
550     eap_gss_get_attr_types_args args;
551     OM_uint32 major, minor;
552     bool ret = false;
553     unsigned int i;
554
555     major = gss_create_empty_buffer_set(&minor, attrs);
556     if (GSS_ERROR(major))
557         throw std::bad_alloc();
558
559     args.attrs = *attrs;
560
561     for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
562         gss_eap_attr_provider *provider = m_providers[i];
563
564         args.type = i;
565
566         if (provider == NULL)
567             continue;
568
569         ret = provider->getAttributeTypes(addAttribute, (void *)&args);
570         if (ret == false)
571             break;
572     }
573
574     if (ret == false)
575         gss_release_buffer_set(&minor, attrs);
576
577     return ret;
578 }
579
580 /*
581  * Get attribute with given name
582  */
583 bool
584 gss_eap_attr_ctx::getAttribute(const gss_buffer_t attr,
585                                int *authenticated,
586                                int *complete,
587                                gss_buffer_t value,
588                                gss_buffer_t display_value,
589                                int *more) const
590 {
591     gss_buffer_desc suffix = GSS_C_EMPTY_BUFFER;
592     unsigned int type;
593     gss_eap_attr_provider *provider;
594     bool ret;
595
596     decomposeAttributeName(attr, &type, &suffix);
597
598     provider = m_providers[type];
599     if (provider == NULL)
600         return false;
601
602     ret = provider->getAttribute(type == ATTR_TYPE_LOCAL ? attr : &suffix,
603                                  authenticated, complete,
604                                  value, display_value, more);
605
606     return ret;
607 }
608
609 /*
610  * Map attribute context to C++ object
611  */
612 gss_any_t
613 gss_eap_attr_ctx::mapToAny(int authenticated,
614                            gss_buffer_t type_id) const
615 {
616     unsigned int type;
617     gss_eap_attr_provider *provider;
618     gss_buffer_desc suffix;
619
620     decomposeAttributeName(type_id, &type, &suffix);
621
622     provider = m_providers[type];
623     if (provider == NULL)
624         return (gss_any_t)NULL;
625
626     return provider->mapToAny(authenticated, &suffix);
627 }
628
629 /*
630  * Release mapped context
631  */
632 void
633 gss_eap_attr_ctx::releaseAnyNameMapping(gss_buffer_t type_id,
634                                         gss_any_t input) const
635 {
636     unsigned int type;
637     gss_eap_attr_provider *provider;
638     gss_buffer_desc suffix;
639
640     decomposeAttributeName(type_id, &type, &suffix);
641
642     provider = m_providers[type];
643     if (provider != NULL)
644         provider->releaseAnyNameMapping(&suffix, input);
645 }
646
647 /*
648  * Export attribute context to buffer
649  */
650 void
651 gss_eap_attr_ctx::exportToBuffer(gss_buffer_t buffer) const
652 {
653     OM_uint32 minor;
654     char *s;
655
656     JSONObject obj = jsonRepresentation();
657
658 #if 0
659     obj.dump(stdout);
660 #endif
661
662     s = obj.dump(JSON_COMPACT);
663
664     if (GSS_ERROR(makeStringBuffer(&minor, s, buffer)))
665         throw std::bad_alloc();
666 }
667
668 /*
669  * Return soonest expiry time of providers
670  */
671 time_t
672 gss_eap_attr_ctx::getExpiryTime(void) const
673 {
674     unsigned int i;
675     time_t expiryTime = 0;
676
677     for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
678         gss_eap_attr_provider *provider = m_providers[i];
679         time_t providerExpiryTime;
680
681         if (provider == NULL)
682             continue;
683
684         providerExpiryTime = provider->getExpiryTime();
685         if (providerExpiryTime == 0)
686             continue;
687
688         if (expiryTime == 0 || providerExpiryTime < expiryTime)
689             expiryTime = providerExpiryTime;
690     }
691
692     return expiryTime;
693 }
694
695 OM_uint32
696 gss_eap_attr_ctx::mapException(OM_uint32 *minor, std::exception &e) const
697 {
698     unsigned int i;
699     OM_uint32 major;
700
701     /* Errors we handle ourselves */
702     if (typeid(e) == typeid(std::bad_alloc)) {
703         major = GSS_S_FAILURE;
704         *minor = ENOMEM;
705         goto cleanup;
706     } else if (typeid(e) == typeid(JSONException)) {
707         major = GSS_S_BAD_NAME;
708         *minor = GSSEAP_BAD_ATTR_TOKEN;
709         gssEapSaveStatusInfo(*minor, "%s", e.what());
710         goto cleanup;
711     }
712
713     /* Errors we delegate to providers */
714     major = GSS_S_CONTINUE_NEEDED;
715
716     for (i = ATTR_TYPE_MIN; i <= ATTR_TYPE_MAX; i++) {
717         gss_eap_attr_provider *provider = m_providers[i];
718
719         if (provider == NULL)
720             continue;
721
722         major = provider->mapException(minor, e);
723         if (major != GSS_S_CONTINUE_NEEDED)
724             break;
725     }
726
727     if (major == GSS_S_CONTINUE_NEEDED) {
728         *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
729         major = GSS_S_FAILURE;
730     }
731
732 cleanup:
733     GSSEAP_ASSERT(GSS_ERROR(major));
734
735     return major;
736 }
737
738 /*
739  * Decompose attribute name into prefix and suffix
740  */
741 void
742 gss_eap_attr_ctx::decomposeAttributeName(const gss_buffer_t attribute,
743                                          gss_buffer_t prefix,
744                                          gss_buffer_t suffix)
745 {
746     char *p = NULL;
747     size_t i;
748
749     for (i = 0; i < attribute->length; i++) {
750         if (((char *)attribute->value)[i] == ' ') {
751             p = (char *)attribute->value + i + 1;
752             break;
753         }
754     }
755
756     prefix->value = attribute->value;
757     prefix->length = i;
758
759     if (p != NULL && *p != '\0')  {
760         suffix->length = attribute->length - 1 - prefix->length;
761         suffix->value = p;
762     } else {
763         suffix->length = 0;
764         suffix->value = NULL;
765     }
766 }
767
768 /*
769  * Decompose attribute name into type and suffix
770  */
771 void
772 gss_eap_attr_ctx::decomposeAttributeName(const gss_buffer_t attribute,
773                                          unsigned int *type,
774                                          gss_buffer_t suffix) const
775 {
776     gss_buffer_desc prefix = GSS_C_EMPTY_BUFFER;
777
778     decomposeAttributeName(attribute, &prefix, suffix);
779     *type = attributePrefixToType(&prefix);
780 }
781
782 /*
783  * Compose attribute name from prefix, suffix; returns C++ string
784  */
785 std::string
786 gss_eap_attr_ctx::composeAttributeName(const gss_buffer_t prefix,
787                                        const gss_buffer_t suffix)
788 {
789     std::string str;
790
791     if (prefix == GSS_C_NO_BUFFER || prefix->length == 0)
792         return str;
793
794     str.append((const char *)prefix->value, prefix->length);
795
796     if (suffix != GSS_C_NO_BUFFER) {
797         str.append(" ");
798         str.append((const char *)suffix->value, suffix->length);
799     }
800
801     return str;
802 }
803
804 /*
805  * Compose attribute name from type, suffix; returns C++ string
806  */
807 std::string
808 gss_eap_attr_ctx::composeAttributeName(unsigned int type,
809                                        const gss_buffer_t suffix)
810 {
811     gss_buffer_desc prefix = attributeTypeToPrefix(type);
812
813     return composeAttributeName(&prefix, suffix);
814 }
815
816 /*
817  * Compose attribute name from prefix, suffix; returns GSS buffer
818  */
819 void
820 gss_eap_attr_ctx::composeAttributeName(const gss_buffer_t prefix,
821                                        const gss_buffer_t suffix,
822                                        gss_buffer_t attribute)
823 {
824     std::string str = composeAttributeName(prefix, suffix);
825
826     if (str.length() != 0) {
827         return duplicateBuffer(str, attribute);
828     } else {
829         attribute->length = 0;
830         attribute->value = NULL;
831     }
832 }
833
834 /*
835  * Compose attribute name from type, suffix; returns GSS buffer
836  */
837 void
838 gss_eap_attr_ctx::composeAttributeName(unsigned int type,
839                                        const gss_buffer_t suffix,
840                                        gss_buffer_t attribute) const
841 {
842     gss_buffer_desc prefix = attributeTypeToPrefix(type);
843
844     return composeAttributeName(&prefix, suffix, attribute);
845 }
846
847 /*
848  * C wrappers
849  */
850 OM_uint32
851 gssEapInquireName(OM_uint32 *minor,
852                   gss_name_t name,
853                   int *name_is_MN,
854                   gss_OID *MN_mech,
855                   gss_buffer_set_t *attrs)
856 {
857     OM_uint32 major;
858
859     if (name_is_MN != NULL)
860         *name_is_MN = (name->mechanismUsed != GSS_C_NULL_OID);
861
862     if (MN_mech != NULL) {
863         major = gssEapCanonicalizeOid(minor, name->mechanismUsed,
864                                       OID_FLAG_NULL_VALID, MN_mech);
865         if (GSS_ERROR(major))
866             return major;
867     }
868
869     if (name->attrCtx == NULL) {
870         *minor = GSSEAP_NO_ATTR_CONTEXT;
871         return GSS_S_UNAVAILABLE;
872     }
873
874     if (GSS_ERROR(gssEapAttrProvidersInit(minor))) {
875         return GSS_S_UNAVAILABLE;
876     }
877
878     try {
879         if (!name->attrCtx->getAttributeTypes(attrs)) {
880             *minor = GSSEAP_NO_ATTR_CONTEXT;
881             return GSS_S_UNAVAILABLE;
882         }
883     } catch (std::exception &e) {
884         return name->attrCtx->mapException(minor, e);
885     }
886
887     return GSS_S_COMPLETE;
888 }
889
890 OM_uint32
891 gssEapGetNameAttribute(OM_uint32 *minor,
892                        gss_name_t name,
893                        gss_buffer_t attr,
894                        int *authenticated,
895                        int *complete,
896                        gss_buffer_t value,
897                        gss_buffer_t display_value,
898                        int *more)
899 {
900     if (authenticated != NULL)
901         *authenticated = 0;
902     if (complete != NULL)
903         *complete = 0;
904
905     if (value != NULL) {
906         value->length = 0;
907         value->value = NULL;
908     }
909
910     if (display_value != NULL) {
911         display_value->length = 0;
912         display_value->value = NULL;
913     }
914
915     if (name->attrCtx == NULL) {
916         *minor = GSSEAP_NO_ATTR_CONTEXT;
917         return GSS_S_UNAVAILABLE;
918     }
919
920     if (GSS_ERROR(gssEapAttrProvidersInit(minor))) {
921         return GSS_S_UNAVAILABLE;
922     }
923
924     try {
925         if (!name->attrCtx->getAttribute(attr, authenticated, complete,
926                                          value, display_value, more)) {
927             *minor = GSSEAP_NO_SUCH_ATTR;
928             gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
929                                  (int)attr->length, (char *)attr->value);
930             return GSS_S_UNAVAILABLE;
931         }
932     } catch (std::exception &e) {
933         return name->attrCtx->mapException(minor, e);
934     }
935
936     return GSS_S_COMPLETE;
937 }
938
939 OM_uint32
940 gssEapDeleteNameAttribute(OM_uint32 *minor,
941                           gss_name_t name,
942                           gss_buffer_t attr)
943 {
944     if (name->attrCtx == NULL) {
945         *minor = GSSEAP_NO_ATTR_CONTEXT;
946         return GSS_S_UNAVAILABLE;
947     }
948
949     if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
950         return GSS_S_UNAVAILABLE;
951
952     try {
953         if (!name->attrCtx->deleteAttribute(attr)) {
954             *minor = GSSEAP_NO_SUCH_ATTR;
955             gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
956                                  (int)attr->length, (char *)attr->value);
957             return GSS_S_UNAVAILABLE;
958         }
959     } catch (std::exception &e) {
960         return name->attrCtx->mapException(minor, e);
961     }
962
963     return GSS_S_COMPLETE;
964 }
965
966 OM_uint32
967 gssEapSetNameAttribute(OM_uint32 *minor,
968                        gss_name_t name,
969                        int complete,
970                        gss_buffer_t attr,
971                        gss_buffer_t value)
972 {
973     if (name->attrCtx == NULL) {
974         *minor = GSSEAP_NO_ATTR_CONTEXT;
975         return GSS_S_UNAVAILABLE;
976     }
977
978     if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
979         return GSS_S_UNAVAILABLE;
980
981     try {
982         if (!name->attrCtx->setAttribute(complete, attr, value)) {
983              *minor = GSSEAP_NO_SUCH_ATTR;
984             gssEapSaveStatusInfo(*minor, "Unknown naming attribute %.*s",
985                                  (int)attr->length, (char *)attr->value);
986             return GSS_S_UNAVAILABLE;
987         }
988     } catch (std::exception &e) {
989         return name->attrCtx->mapException(minor, e);
990     }
991
992     return GSS_S_COMPLETE;
993 }
994
995 OM_uint32
996 gssEapExportAttrContext(OM_uint32 *minor,
997                         gss_name_t name,
998                         gss_buffer_t buffer)
999 {
1000     if (name->attrCtx == NULL) {
1001         buffer->length = 0;
1002         buffer->value = NULL;
1003
1004         return GSS_S_COMPLETE;
1005     }
1006
1007     if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
1008         return GSS_S_UNAVAILABLE;
1009
1010     try {
1011         name->attrCtx->exportToBuffer(buffer);
1012     } catch (std::exception &e) {
1013         return name->attrCtx->mapException(minor, e);
1014     }
1015
1016     return GSS_S_COMPLETE;
1017 }
1018
1019 OM_uint32
1020 gssEapImportAttrContext(OM_uint32 *minor,
1021                         gss_buffer_t buffer,
1022                         gss_name_t name)
1023 {
1024     gss_eap_attr_ctx *ctx = NULL;
1025     OM_uint32 major = GSS_S_FAILURE;
1026
1027     GSSEAP_ASSERT(name->attrCtx == NULL);
1028
1029     if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
1030         return GSS_S_UNAVAILABLE;
1031
1032     if (buffer->length == 0)
1033         return GSS_S_COMPLETE;
1034
1035     try {
1036         ctx = new gss_eap_attr_ctx();
1037
1038         if (ctx->initWithBuffer(buffer)) {
1039             name->attrCtx = ctx;
1040             major = GSS_S_COMPLETE;
1041             *minor = 0;
1042         } else {
1043             major = GSS_S_BAD_NAME;
1044             *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
1045         }
1046     } catch (std::exception &e) {
1047         if (ctx != NULL)
1048             major = ctx->mapException(minor, e);
1049     }
1050
1051     GSSEAP_ASSERT(major == GSS_S_COMPLETE || name->attrCtx == NULL);
1052
1053     if (GSS_ERROR(major))
1054         delete ctx;
1055
1056     return major;
1057 }
1058
1059 OM_uint32
1060 gssEapDuplicateAttrContext(OM_uint32 *minor,
1061                            gss_name_t in,
1062                            gss_name_t out)
1063 {
1064     gss_eap_attr_ctx *ctx = NULL;
1065     OM_uint32 major = GSS_S_FAILURE;
1066
1067     GSSEAP_ASSERT(out->attrCtx == NULL);
1068
1069     if (in->attrCtx == NULL) {
1070         *minor = 0;
1071         return GSS_S_COMPLETE;
1072     }
1073
1074     if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
1075         return GSS_S_UNAVAILABLE;
1076
1077     try {
1078         ctx = new gss_eap_attr_ctx();
1079
1080         if (ctx->initWithExistingContext(in->attrCtx)) {
1081             out->attrCtx = ctx;
1082             major = GSS_S_COMPLETE;
1083             *minor = 0;
1084         } else {
1085             major = GSS_S_FAILURE;
1086             *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
1087         }
1088     } catch (std::exception &e) {
1089         major = in->attrCtx->mapException(minor, e);
1090     }
1091
1092     GSSEAP_ASSERT(major == GSS_S_COMPLETE || out->attrCtx == NULL);
1093
1094     if (GSS_ERROR(major))
1095         delete ctx;
1096
1097     return GSS_S_COMPLETE;
1098 }
1099
1100 OM_uint32
1101 gssEapMapNameToAny(OM_uint32 *minor,
1102                    gss_name_t name,
1103                    int authenticated,
1104                    gss_buffer_t type_id,
1105                    gss_any_t *output)
1106 {
1107     if (name->attrCtx == NULL) {
1108         *minor = GSSEAP_NO_ATTR_CONTEXT;
1109         return GSS_S_UNAVAILABLE;
1110     }
1111
1112     if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
1113         return GSS_S_UNAVAILABLE;
1114
1115     try {
1116         *output = name->attrCtx->mapToAny(authenticated, type_id);
1117     } catch (std::exception &e) {
1118         return name->attrCtx->mapException(minor, e);
1119     }
1120
1121     return GSS_S_COMPLETE;
1122 }
1123
1124 OM_uint32
1125 gssEapReleaseAnyNameMapping(OM_uint32 *minor,
1126                             gss_name_t name,
1127                             gss_buffer_t type_id,
1128                             gss_any_t *input)
1129 {
1130     if (name->attrCtx == NULL) {
1131         *minor = GSSEAP_NO_ATTR_CONTEXT;
1132         return GSS_S_UNAVAILABLE;
1133     }
1134
1135     if (GSS_ERROR(gssEapAttrProvidersInit(minor)))
1136         return GSS_S_UNAVAILABLE;
1137
1138     try {
1139         if (*input != NULL)
1140             name->attrCtx->releaseAnyNameMapping(type_id, *input);
1141         *input = NULL;
1142     } catch (std::exception &e) {
1143         return name->attrCtx->mapException(minor, e);
1144     }
1145
1146     return GSS_S_COMPLETE;
1147 }
1148
1149 OM_uint32
1150 gssEapReleaseAttrContext(OM_uint32 *minor,
1151                          gss_name_t name)
1152 {
1153     if (name->attrCtx != NULL)
1154         delete name->attrCtx;
1155
1156     *minor = 0;
1157     return GSS_S_COMPLETE;
1158 }
1159
1160 /*
1161  * Public accessor for initialisng a context from a GSS context. Also
1162  * sets expiry time on GSS context as a side-effect.
1163  */
1164 OM_uint32
1165 gssEapCreateAttrContext(OM_uint32 *minor,
1166                         gss_cred_id_t gssCred,
1167                         gss_ctx_id_t gssCtx,
1168                         struct gss_eap_attr_ctx **pAttrContext,
1169                         time_t *pExpiryTime)
1170 {
1171     gss_eap_attr_ctx *ctx = NULL;
1172     OM_uint32 major;
1173
1174     GSSEAP_ASSERT(gssCtx != GSS_C_NO_CONTEXT);
1175
1176     *pAttrContext = NULL;
1177
1178     major = gssEapAttrProvidersInit(minor);
1179     if (GSS_ERROR(major))
1180         return major;
1181
1182     try {
1183         /* Set *pAttrContext here to for reentrancy */
1184         *pAttrContext = ctx = new gss_eap_attr_ctx();
1185
1186         if (ctx->initWithGssContext(gssCred, gssCtx)) {
1187             *pExpiryTime = ctx->getExpiryTime();
1188             major = GSS_S_COMPLETE;
1189             *minor = 0;
1190         } else {
1191             major = GSS_S_FAILURE;
1192             *minor = GSSEAP_ATTR_CONTEXT_FAILURE;
1193         }
1194     } catch (std::exception &e) {
1195         if (ctx != NULL)
1196             major = ctx->mapException(minor, e);
1197     }
1198
1199     if (GSS_ERROR(major)) {
1200         delete ctx;
1201         *pAttrContext = NULL;
1202     }
1203
1204     return major;
1205 }