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