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