7a2e60b936b4e3aa753c834761c23b991e3bdb43
[mech_eap.git] / mech_eap / util_name.c
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  * Portions Copyright 2009 by the Massachusetts Institute of Technology.
34  * All Rights Reserved.
35  *
36  * Export of this software from the United States of America may
37  *   require a specific license from the United States Government.
38  *   It is the responsibility of any person or organization contemplating
39  *   export to obtain such a license before exporting.
40  *
41  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
42  * distribute this software and its documentation for any purpose and
43  * without fee is hereby granted, provided that the above copyright
44  * notice appear in all copies and that both that copyright notice and
45  * this permission notice appear in supporting documentation, and that
46  * the name of M.I.T. not be used in advertising or publicity pertaining
47  * to distribution of the software without specific, written prior
48  * permission.  Furthermore if you modify this software you must label
49  * your software as modified software and not distribute it in such a
50  * fashion that it might be confused with the original M.I.T. software.
51  * M.I.T. makes no representations about the suitability of
52  * this software for any purpose.  It is provided "as is" without express
53  * or implied warranty.
54  */
55
56 /*
57  * Name utility routines.
58  */
59
60 #include "gssapiP_eap.h"
61
62 static gss_OID_desc gssEapNtEapName = {
63     /* 1.3.6.1.5.5.15.2.1 */
64     8, "\x2B\x06\x01\x05\x05\x0f\x02\x01"
65 };
66
67 gss_OID GSS_EAP_NT_EAP_NAME = &gssEapNtEapName;
68
69 OM_uint32
70 gssEapAllocName(OM_uint32 *minor, gss_name_t *pName)
71 {
72     OM_uint32 tmpMinor;
73     gss_name_t name;
74
75     *pName = GSS_C_NO_NAME;
76
77     name = (gss_name_t)GSSEAP_CALLOC(1, sizeof(*name));
78     if (name == NULL) {
79         *minor = ENOMEM;
80         return GSS_S_FAILURE;
81     }
82
83     if (GSSEAP_MUTEX_INIT(&name->mutex) != 0) {
84         *minor = GSSEAP_GET_LAST_ERROR();
85         gssEapReleaseName(&tmpMinor, &name);
86         return GSS_S_FAILURE;
87     }
88
89     *pName = name;
90
91     return GSS_S_COMPLETE;
92 }
93
94 OM_uint32
95 gssEapReleaseName(OM_uint32 *minor, gss_name_t *pName)
96 {
97     gss_name_t name;
98     krb5_context krbContext = NULL;
99     OM_uint32 tmpMinor;
100
101     *minor = 0;
102
103     if (pName == NULL) {
104         return GSS_S_COMPLETE;
105     }
106
107     name = *pName;
108     if (name == GSS_C_NO_NAME) {
109         return GSS_S_COMPLETE;
110     }
111
112     GSSEAP_KRB_INIT(&krbContext);
113     krb5_free_principal(krbContext, name->krbPrincipal);
114     gssEapReleaseOid(&tmpMinor, &name->mechanismUsed);
115 #ifdef GSSEAP_ENABLE_ACCEPTOR
116     gssEapReleaseAttrContext(&tmpMinor, name);
117 #endif
118
119     GSSEAP_MUTEX_DESTROY(&name->mutex);
120     GSSEAP_FREE(name);
121     *pName = NULL;
122
123     return GSS_S_COMPLETE;
124 }
125
126 static OM_uint32
127 krbPrincipalToName(OM_uint32 *minor,
128                    krb5_principal *principal,
129                    gss_name_t *pName)
130 {
131     OM_uint32 major;
132     gss_name_t name;
133
134     major = gssEapAllocName(minor, &name);
135     if (GSS_ERROR(major))
136         return major;
137
138     name->krbPrincipal = *principal;
139     *principal = NULL;
140
141     if (KRB_PRINC_LENGTH(name->krbPrincipal) >= 1) {
142         name->flags |= NAME_FLAG_SERVICE;
143     }
144     if (KRB_PRINC_LENGTH(name->krbPrincipal) == 1) {
145         name->flags |= NAME_FLAG_NAI;
146     }
147
148     *pName = name;
149     *minor = 0;
150
151     return GSS_S_COMPLETE;
152 }
153
154 static char *
155 gssEapGetDefaultRealm(krb5_context krbContext)
156 {
157     char *defaultRealm = NULL;
158
159     krb5_appdefault_string(krbContext, "eap_gss",
160                            NULL, "default_realm", "", &defaultRealm);
161
162     return defaultRealm;
163 }
164
165 static OM_uint32
166 importServiceName(OM_uint32 *minor,
167                   const gss_buffer_t nameBuffer,
168                   gss_name_t *pName)
169 {
170     OM_uint32 major;
171     krb5_error_code code;
172     krb5_context krbContext;
173     krb5_principal krbPrinc;
174     char *service, *host, *realm = NULL;
175
176     GSSEAP_KRB_INIT(&krbContext);
177
178     major = bufferToString(minor, nameBuffer, &service);
179     if (GSS_ERROR(major))
180         return major;
181
182     host = strchr(service, '@');
183     if (host != NULL) {
184         *host = '\0';
185         host++;
186     }
187
188     realm = gssEapGetDefaultRealm(krbContext);
189
190     code = krb5_build_principal(krbContext,
191                                 &krbPrinc,
192                                 realm != NULL ? strlen(realm) : 0,
193                                 realm != NULL ? realm : "",
194                                 service,
195                                 host,
196                                 NULL);
197
198     if (code == 0) {
199         KRB_PRINC_TYPE(krbPrinc) = KRB5_NT_SRV_HST;
200
201         major = krbPrincipalToName(minor, &krbPrinc, pName);
202         if (GSS_ERROR(major))
203             krb5_free_principal(krbContext, krbPrinc);
204     } else {
205         major = GSS_S_FAILURE;
206         *minor = GSSEAP_BAD_SERVICE_NAME;
207     }
208
209     if (realm != NULL)
210         krb5_free_default_realm(krbContext, realm);
211     GSSEAP_FREE(service);
212
213     return major;
214 }
215
216 #define IMPORT_FLAG_DEFAULT_REALM           0x1
217
218 /*
219  * Import an EAP name, possibly appending the default GSS EAP realm,
220  */
221 static OM_uint32
222 importEapNameFlags(OM_uint32 *minor,
223                    const gss_buffer_t nameBuffer,
224                    OM_uint32 importFlags,
225                    gss_name_t *pName)
226 {
227     OM_uint32 major;
228     krb5_context krbContext;
229     krb5_principal krbPrinc = NULL;
230     krb5_error_code code;
231     char *nameString;
232
233     GSSEAP_KRB_INIT(&krbContext);
234
235     if (nameBuffer == GSS_C_NO_BUFFER) {
236         nameString = "";
237         code = KRB5_PARSE_MALFORMED;
238     } else {
239         major = bufferToString(minor, nameBuffer, &nameString);
240         if (GSS_ERROR(major))
241             return major;
242
243         /*
244          * First, attempt to parse the name on the assumption that it includes
245          * a qualifying realm. This allows us to avoid accidentally appending
246          * the default Kerberos realm to an unqualified name. (A bug in MIT
247          * Kerberos prevents the default realm being set to an empty value.)
248          */
249         code = krb5_parse_name_flags(krbContext, nameString,
250                                      KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &krbPrinc);
251     }
252
253     if (code == KRB5_PARSE_MALFORMED) {
254         char *defaultRealm = NULL;
255         int parseFlags = 0;
256
257         /* Possibly append the default EAP realm if required */
258         if (importFlags & IMPORT_FLAG_DEFAULT_REALM)
259             defaultRealm = gssEapGetDefaultRealm(krbContext);
260
261         /* If no default realm, leave the realm empty in the parsed name */
262         if (defaultRealm == NULL || defaultRealm[0] == '\0')
263             parseFlags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
264
265         code = krb5_parse_name_flags(krbContext, nameString, parseFlags, &krbPrinc);
266
267 #ifdef HAVE_HEIMDAL_VERSION
268         if (code == 0 && KRB_PRINC_REALM(krbPrinc) == NULL) {
269             KRB_PRINC_REALM(krbPrinc) = KRB_CALLOC(1, sizeof(char));
270             if (KRB_PRINC_REALM(krbPrinc) == NULL)
271                 code = ENOMEM;
272         }
273         krb5_xfree(defaultRealm);
274 #else
275         if (defaultRealm != NULL)
276             krb5_free_default_realm(krbContext, defaultRealm);
277 #endif
278     }
279
280     if (nameBuffer != GSS_C_NO_BUFFER)
281         GSSEAP_FREE(nameString);
282
283     if (code != 0) {
284         *minor = code;
285         return GSS_S_FAILURE;
286     }
287
288     GSSEAP_ASSERT(krbPrinc != NULL);
289
290     major = krbPrincipalToName(minor, &krbPrinc, pName);
291     if (GSS_ERROR(major))
292         krb5_free_principal(krbContext, krbPrinc);
293
294     return major;
295 }
296
297 static OM_uint32
298 importEapName(OM_uint32 *minor,
299               const gss_buffer_t nameBuffer,
300               gss_name_t *pName)
301 {
302     return importEapNameFlags(minor, nameBuffer, 0, pName);
303 }
304
305 static OM_uint32
306 importUserName(OM_uint32 *minor,
307                const gss_buffer_t nameBuffer,
308                gss_name_t *pName)
309 {
310     return importEapNameFlags(minor, nameBuffer, IMPORT_FLAG_DEFAULT_REALM, pName);
311 }
312
313 static OM_uint32
314 importAnonymousName(OM_uint32 *minor,
315                     const gss_buffer_t nameBuffer GSSEAP_UNUSED,
316                     gss_name_t *pName)
317 {
318     return importEapNameFlags(minor, GSS_C_NO_BUFFER, 0, pName);
319 }
320
321 #define UPDATE_REMAIN(n)    do {            \
322         p += (n);                           \
323         remain -= (n);                      \
324     } while (0)
325
326 #define CHECK_REMAIN(n)     do {        \
327         if (remain < (n)) {             \
328             major = GSS_S_BAD_NAME;     \
329             *minor = GSSEAP_TOK_TRUNC;  \
330             goto cleanup;               \
331         }                               \
332     } while (0)
333
334 OM_uint32
335 gssEapImportNameInternal(OM_uint32 *minor,
336                          const gss_buffer_t nameBuffer,
337                          gss_name_t *pName,
338                          OM_uint32 flags)
339 {
340     OM_uint32 major, tmpMinor;
341     krb5_context krbContext;
342     unsigned char *p;
343     size_t len, remain;
344     gss_buffer_desc buf;
345     gss_name_t name = GSS_C_NO_NAME;
346     gss_OID mechanismUsed = GSS_C_NO_OID;
347
348     GSSEAP_KRB_INIT(&krbContext);
349
350     p = (unsigned char *)nameBuffer->value;
351     remain = nameBuffer->length;
352
353     if (flags & EXPORT_NAME_FLAG_OID) {
354         gss_OID_desc mech;
355         enum gss_eap_token_type tokType;
356         uint16_t wireTokType;
357
358         /* TOK_ID || MECH_OID_LEN || MECH_OID */
359         if (remain < 6) {
360             *minor = GSSEAP_BAD_NAME_TOKEN;
361             return GSS_S_BAD_NAME;
362         }
363
364         if (flags & EXPORT_NAME_FLAG_COMPOSITE)
365             tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
366         else
367             tokType = TOK_TYPE_EXPORT_NAME;
368
369         /* TOK_ID */
370         wireTokType = load_uint16_be(p);
371
372         if ((flags & EXPORT_NAME_FLAG_ALLOW_COMPOSITE) &&
373             wireTokType == TOK_TYPE_EXPORT_NAME_COMPOSITE) {
374             tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
375             flags |= EXPORT_NAME_FLAG_COMPOSITE;
376         }
377
378         if (wireTokType != tokType) {
379             *minor = GSSEAP_WRONG_TOK_ID;
380             return GSS_S_BAD_NAME;
381         }
382         UPDATE_REMAIN(2);
383
384         /* MECH_OID_LEN */
385         len = load_uint16_be(p);
386         if (len < 2) {
387             *minor = GSSEAP_BAD_NAME_TOKEN;
388             return GSS_S_BAD_NAME;
389         }
390         UPDATE_REMAIN(2);
391
392         /* MECH_OID */
393         if (p[0] != 0x06) {
394             *minor = GSSEAP_BAD_NAME_TOKEN;
395             return GSS_S_BAD_NAME;
396         }
397
398         mech.length = p[1];
399         mech.elements = &p[2];
400
401         CHECK_REMAIN(mech.length);
402
403         major = gssEapCanonicalizeOid(minor,
404                                       &mech,
405                                       OID_FLAG_FAMILY_MECH_VALID |
406                                         OID_FLAG_MAP_FAMILY_MECH_TO_NULL,
407                                       &mechanismUsed);
408         if (GSS_ERROR(major))
409             goto cleanup;
410
411         UPDATE_REMAIN(2 + mech.length);
412     }
413
414     /* NAME_LEN */
415     CHECK_REMAIN(4);
416     len = load_uint32_be(p);
417     UPDATE_REMAIN(4);
418
419     /* NAME */
420     CHECK_REMAIN(len);
421     buf.length = len;
422     buf.value = p;
423     UPDATE_REMAIN(len);
424
425     major = importEapNameFlags(minor, &buf, 0, &name);
426     if (GSS_ERROR(major))
427         goto cleanup;
428
429     name->mechanismUsed = mechanismUsed;
430     mechanismUsed = GSS_C_NO_OID;
431
432 #ifdef GSSEAP_ENABLE_ACCEPTOR
433     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
434         gss_buffer_desc buf;
435
436         buf.length = remain;
437         buf.value = p;
438
439         major = gssEapImportAttrContext(minor, &buf, name);
440         if (GSS_ERROR(major))
441             goto cleanup;
442     }
443 #endif
444
445     major = GSS_S_COMPLETE;
446     *minor = 0;
447
448 cleanup:
449     if (GSS_ERROR(major)) {
450         gssEapReleaseOid(&tmpMinor, &mechanismUsed);
451         gssEapReleaseName(&tmpMinor, &name);
452     } else {
453         *pName = name;
454     }
455
456     return major;
457 }
458
459 static OM_uint32
460 importExportName(OM_uint32 *minor,
461                  const gss_buffer_t nameBuffer,
462                  gss_name_t *name)
463 {
464     return gssEapImportNameInternal(minor, nameBuffer, name,
465                                     EXPORT_NAME_FLAG_OID |
466                                     EXPORT_NAME_FLAG_ALLOW_COMPOSITE);
467 }
468
469 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
470 static OM_uint32
471 importCompositeExportName(OM_uint32 *minor,
472                           const gss_buffer_t nameBuffer,
473                           gss_name_t *name)
474 {
475     return gssEapImportNameInternal(minor, nameBuffer, name,
476                                     EXPORT_NAME_FLAG_OID |
477                                     EXPORT_NAME_FLAG_COMPOSITE);
478 }
479 #endif
480
481 struct gss_eap_name_import_provider {
482     gss_const_OID oid;
483     OM_uint32 (*import)(OM_uint32 *, const gss_buffer_t, gss_name_t *);
484 };
485
486 OM_uint32
487 gssEapImportName(OM_uint32 *minor,
488                  const gss_buffer_t nameBuffer,
489                  const gss_OID nameType,
490                  const gss_OID mechType,
491                  gss_name_t *pName)
492 {
493     struct gss_eap_name_import_provider nameTypes[] = {
494         { GSS_EAP_NT_EAP_NAME,              importEapName               },
495         { GSS_C_NT_USER_NAME,               importUserName              },
496         { GSS_C_NT_HOSTBASED_SERVICE,       importServiceName           },
497         { GSS_C_NT_HOSTBASED_SERVICE_X,     importServiceName           },
498         { GSS_C_NT_ANONYMOUS,               importAnonymousName         },
499         { GSS_C_NT_EXPORT_NAME,             importExportName            },
500         { GSS_KRB5_NT_PRINCIPAL_NAME,       importUserName              },
501 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
502         { GSS_C_NT_COMPOSITE_EXPORT,        importCompositeExportName   },
503 #endif
504     };
505     size_t i;
506     OM_uint32 major = GSS_S_BAD_NAMETYPE;
507     OM_uint32 tmpMinor;
508     gss_name_t name = GSS_C_NO_NAME;
509
510     for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) {
511         if (oidEqual(nameTypes[i].oid,
512                      nameType == GSS_C_NO_OID ? GSS_EAP_NT_EAP_NAME : nameType)) {
513             major = nameTypes[i].import(minor, nameBuffer, &name);
514             break;
515         }
516     }
517
518     if (major == GSS_S_COMPLETE &&
519         mechType != GSS_C_NO_OID) {
520         GSSEAP_ASSERT(gssEapIsConcreteMechanismOid(mechType));
521         GSSEAP_ASSERT(name != GSS_C_NO_NAME);
522         GSSEAP_ASSERT(name->mechanismUsed == GSS_C_NO_OID);
523
524         major = gssEapCanonicalizeOid(minor, mechType, 0, &name->mechanismUsed);
525     }
526
527     if (GSS_ERROR(major))
528         gssEapReleaseName(&tmpMinor, &name);
529     else
530         *pName = name;
531
532     return major;
533 }
534
535 OM_uint32
536 gssEapExportName(OM_uint32 *minor,
537                  gss_const_name_t name,
538                  gss_buffer_t exportedName)
539 {
540     return gssEapExportNameInternal(minor, name, exportedName,
541                                     EXPORT_NAME_FLAG_OID);
542 }
543
544 OM_uint32
545 gssEapExportNameInternal(OM_uint32 *minor,
546                          gss_const_name_t name,
547                          gss_buffer_t exportedName,
548                          OM_uint32 flags)
549 {
550     OM_uint32 major = GSS_S_FAILURE, tmpMinor;
551     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
552     size_t exportedNameLen;
553     unsigned char *p;
554     gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER;
555     gss_OID mech;
556
557     exportedName->length = 0;
558     exportedName->value = NULL;
559
560     if (name->mechanismUsed != GSS_C_NO_OID)
561         mech = name->mechanismUsed;
562     else
563         mech = GSS_EAP_MECHANISM;
564
565     major = gssEapDisplayName(minor, name, &nameBuf, NULL);
566     if (GSS_ERROR(major))
567         goto cleanup;
568
569     exportedNameLen = 0;
570     if (flags & EXPORT_NAME_FLAG_OID) {
571         exportedNameLen += 6 + mech->length;
572     }
573     exportedNameLen += 4 + nameBuf.length;
574 #ifdef GSSEAP_ENABLE_ACCEPTOR
575     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
576         major = gssEapExportAttrContext(minor, name, &attrs);
577         if (GSS_ERROR(major))
578             goto cleanup;
579         exportedNameLen += attrs.length;
580     }
581 #endif
582
583     exportedName->value = GSSEAP_MALLOC(exportedNameLen);
584     if (exportedName->value == NULL) {
585         major = GSS_S_FAILURE;
586         *minor = ENOMEM;
587         goto cleanup;
588     }
589     exportedName->length = exportedNameLen;
590
591     p = (unsigned char *)exportedName->value;
592
593     if (flags & EXPORT_NAME_FLAG_OID) {
594         /* TOK | MECH_OID_LEN */
595         store_uint16_be((flags & EXPORT_NAME_FLAG_COMPOSITE)
596                         ? TOK_TYPE_EXPORT_NAME_COMPOSITE
597                         : TOK_TYPE_EXPORT_NAME,
598                         p);
599         p += 2;
600         store_uint16_be(mech->length + 2, p);
601         p += 2;
602
603         /* MECH_OID */
604         *p++ = 0x06;
605         *p++ = mech->length & 0xff;
606         memcpy(p, mech->elements, mech->length);
607         p += mech->length;
608     }
609
610     /* NAME_LEN */
611     store_uint32_be(nameBuf.length, p);
612     p += 4;
613
614     /* NAME */
615     memcpy(p, nameBuf.value, nameBuf.length);
616     p += nameBuf.length;
617
618     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
619         memcpy(p, attrs.value, attrs.length);
620         p += attrs.length;
621     }
622
623     GSSEAP_ASSERT(p == (unsigned char *)exportedName->value + exportedNameLen);
624
625     major = GSS_S_COMPLETE;
626     *minor = 0;
627
628 cleanup:
629     gss_release_buffer(&tmpMinor, &attrs);
630     gss_release_buffer(&tmpMinor, &nameBuf);
631     if (GSS_ERROR(major))
632         gss_release_buffer(&tmpMinor, exportedName);
633
634     return major;
635 }
636
637 OM_uint32
638 gssEapCanonicalizeName(OM_uint32 *minor,
639                        gss_const_name_t input_name,
640                        const gss_OID mech_type,
641                        gss_name_t *dest_name)
642 {
643     OM_uint32 major, tmpMinor;
644     krb5_context krbContext;
645     gss_name_t name;
646     gss_OID mech_used;
647
648     if (input_name == GSS_C_NO_NAME) {
649         *minor = EINVAL;
650         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
651     }
652
653     GSSEAP_KRB_INIT(&krbContext);
654
655     major = gssEapAllocName(minor, &name);
656     if (GSS_ERROR(major)) {
657         return major;
658     }
659
660     if (mech_type != GSS_C_NO_OID)
661         mech_used = mech_type;
662     else
663         mech_used = input_name->mechanismUsed;
664
665     major = gssEapCanonicalizeOid(minor,
666                                   mech_used,
667                                   OID_FLAG_NULL_VALID,
668                                   &name->mechanismUsed);
669     if (GSS_ERROR(major))
670         goto cleanup;
671
672     name->flags = input_name->flags;
673
674     *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal,
675                                  &name->krbPrincipal);
676     if (*minor != 0) {
677         major = GSS_S_FAILURE;
678         goto cleanup;
679     }
680
681 #ifdef GSSEAP_ENABLE_ACCEPTOR
682     if (input_name->attrCtx != NULL) {
683         major = gssEapDuplicateAttrContext(minor, input_name, name);
684         if (GSS_ERROR(major))
685             goto cleanup;
686     }
687 #endif
688
689     *dest_name = name;
690
691 cleanup:
692     if (GSS_ERROR(major)) {
693         gssEapReleaseName(&tmpMinor, &name);
694     }
695
696     return major;
697 }
698
699 OM_uint32
700 gssEapDuplicateName(OM_uint32 *minor,
701                     gss_const_name_t input_name,
702                     gss_name_t *dest_name)
703 {
704     return gssEapCanonicalizeName(minor, input_name,
705                                   GSS_C_NO_OID, dest_name);
706 }
707
708 static int
709 hasRealmP(gss_const_name_t name)
710 {
711 #ifdef HAVE_HEIMDAL_VERSION
712     if (KRB_PRINC_REALM(name->krbPrincipal) != NULL &&
713         KRB_PRINC_REALM(name->krbPrincipal)[0] != '\0')
714 #else
715     if (KRB_PRINC_REALM(name->krbPrincipal)->length != 0)
716 #endif
717         return TRUE;
718
719     return FALSE;
720 }
721
722 OM_uint32
723 gssEapDisplayName(OM_uint32 *minor,
724                   gss_const_name_t name,
725                   gss_buffer_t output_name_buffer,
726                   gss_OID *output_name_type)
727 {
728     OM_uint32 major;
729     krb5_context krbContext;
730     char *krbName;
731     gss_OID name_type;
732     int flags = 0;
733
734     GSSEAP_KRB_INIT(&krbContext);
735
736     output_name_buffer->length = 0;
737     output_name_buffer->value = NULL;
738
739     if (name == GSS_C_NO_NAME) {
740         *minor = EINVAL;
741         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
742     }
743
744     /*
745      * According to draft-ietf-abfab-gss-eap-01, when the realm is
746      * absent the trailing '@' is not included.
747      */
748     if (!hasRealmP(name))
749         flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
750
751     *minor = krb5_unparse_name_flags(krbContext, name->krbPrincipal,
752                                      flags, &krbName);
753     if (*minor != 0) {
754         return GSS_S_FAILURE;
755     }
756
757     major = makeStringBuffer(minor, krbName, output_name_buffer);
758 #ifdef HAVE_HEIMDAL_VERSION
759     krb5_xfree(krbName);
760 #else
761     krb5_free_unparsed_name(krbContext, krbName);
762 #endif
763     if (GSS_ERROR(major))
764         return major;
765
766     if (output_name_buffer->length == 0) {
767         name_type = GSS_C_NT_ANONYMOUS;
768     } else if (name->flags & NAME_FLAG_NAI) {
769         name_type = GSS_C_NT_USER_NAME;
770     } else {
771         name_type = GSS_EAP_NT_EAP_NAME;
772     }
773
774     if (output_name_type != NULL)
775         *output_name_type = name_type;
776
777     return GSS_S_COMPLETE;
778 }
779
780 OM_uint32
781 gssEapCompareName(OM_uint32 *minor,
782                   gss_const_name_t name1,
783                   gss_const_name_t name2,
784                   OM_uint32 flags,
785                   int *name_equal)
786 {
787     krb5_context krbContext;
788
789     *minor = 0;
790
791     if (name1 == GSS_C_NO_NAME && name2 == GSS_C_NO_NAME) {
792         *name_equal = 1;
793     } else if (name1 != GSS_C_NO_NAME && name2 != GSS_C_NO_NAME) {
794         GSSEAP_KRB_INIT(&krbContext);
795
796         /* krbPrincipal is immutable, so lock not required */
797         if ((flags & COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS) &&
798             (hasRealmP(name1) == FALSE || hasRealmP(name2) == FALSE)) {
799             *name_equal = krb5_principal_compare_any_realm(krbContext,
800                                                            name1->krbPrincipal,
801                                                            name2->krbPrincipal);
802         } else {
803             *name_equal = krb5_principal_compare(krbContext,
804                                                  name1->krbPrincipal,
805                                                  name2->krbPrincipal);
806         }
807     } else {
808         *name_equal = 0;
809     }
810
811     return GSS_S_COMPLETE;
812 }