Fixes for Heimdal (macOS) builds from Stefan.
[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 #ifdef HAVE_HEIMDAL_VERSION
211         krb5_xfree(realm);
212 #else
213         krb5_free_default_realm(krbContext, realm);
214 #endif
215     }
216     GSSEAP_FREE(service);
217
218     return major;
219 }
220
221 #define IMPORT_FLAG_DEFAULT_REALM           0x1
222
223 /*
224  * Import an EAP name, possibly appending the default GSS EAP realm,
225  */
226 static OM_uint32
227 importEapNameFlags(OM_uint32 *minor,
228                    const gss_buffer_t nameBuffer,
229                    OM_uint32 importFlags,
230                    gss_name_t *pName)
231 {
232     OM_uint32 major;
233     krb5_context krbContext;
234     krb5_principal krbPrinc = NULL;
235     krb5_error_code code;
236     char *nameString;
237
238     GSSEAP_KRB_INIT(&krbContext);
239
240     if (nameBuffer == GSS_C_NO_BUFFER) {
241         nameString = "";
242         code = KRB5_PARSE_MALFORMED;
243     } else {
244         major = bufferToString(minor, nameBuffer, &nameString);
245         if (GSS_ERROR(major))
246             return major;
247
248         /*
249          * First, attempt to parse the name on the assumption that it includes
250          * a qualifying realm. This allows us to avoid accidentally appending
251          * the default Kerberos realm to an unqualified name. (A bug in MIT
252          * Kerberos prevents the default realm being set to an empty value.)
253          */
254         code = krb5_parse_name_flags(krbContext, nameString,
255                                      KRB5_PRINCIPAL_PARSE_REQUIRE_REALM, &krbPrinc);
256     }
257
258     if (code == KRB5_PARSE_MALFORMED) {
259         char *defaultRealm = NULL;
260         int parseFlags = 0;
261
262         /* Possibly append the default EAP realm if required */
263         if (importFlags & IMPORT_FLAG_DEFAULT_REALM)
264             defaultRealm = gssEapGetDefaultRealm(krbContext);
265
266         /* If no default realm, leave the realm empty in the parsed name */
267         if (defaultRealm == NULL || defaultRealm[0] == '\0')
268             parseFlags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
269
270         code = krb5_parse_name_flags(krbContext, nameString, parseFlags, &krbPrinc);
271
272 #ifdef HAVE_HEIMDAL_VERSION
273         if (code == 0 && KRB_PRINC_REALM(krbPrinc) == NULL) {
274             KRB_PRINC_REALM(krbPrinc) = KRB_CALLOC(1, sizeof(char));
275             if (KRB_PRINC_REALM(krbPrinc) == NULL)
276                 code = ENOMEM;
277         }
278         krb5_xfree(defaultRealm);
279 #else
280         if (defaultRealm != NULL)
281             krb5_free_default_realm(krbContext, defaultRealm);
282 #endif
283     }
284
285     if (nameBuffer != GSS_C_NO_BUFFER)
286         GSSEAP_FREE(nameString);
287
288     if (code != 0) {
289         *minor = code;
290         return GSS_S_FAILURE;
291     }
292
293     GSSEAP_ASSERT(krbPrinc != NULL);
294
295     major = krbPrincipalToName(minor, &krbPrinc, pName);
296     if (GSS_ERROR(major))
297         krb5_free_principal(krbContext, krbPrinc);
298
299     return major;
300 }
301
302 static OM_uint32
303 importEapName(OM_uint32 *minor,
304               const gss_buffer_t nameBuffer,
305               gss_name_t *pName)
306 {
307     return importEapNameFlags(minor, nameBuffer, 0, pName);
308 }
309
310 static OM_uint32
311 importUserName(OM_uint32 *minor,
312                const gss_buffer_t nameBuffer,
313                gss_name_t *pName)
314 {
315     return importEapNameFlags(minor, nameBuffer, IMPORT_FLAG_DEFAULT_REALM, pName);
316 }
317
318 static OM_uint32
319 importAnonymousName(OM_uint32 *minor,
320                     const gss_buffer_t nameBuffer GSSEAP_UNUSED,
321                     gss_name_t *pName)
322 {
323     return importEapNameFlags(minor, GSS_C_NO_BUFFER, 0, pName);
324 }
325
326 #define UPDATE_REMAIN(n)    do {            \
327         p += (n);                           \
328         remain -= (n);                      \
329     } while (0)
330
331 #define CHECK_REMAIN(n)     do {        \
332         if (remain < (n)) {             \
333             major = GSS_S_BAD_NAME;     \
334             *minor = GSSEAP_TOK_TRUNC;  \
335             goto cleanup;               \
336         }                               \
337     } while (0)
338
339 OM_uint32
340 gssEapImportNameInternal(OM_uint32 *minor,
341                          const gss_buffer_t nameBuffer,
342                          gss_name_t *pName,
343                          OM_uint32 flags)
344 {
345     OM_uint32 major, tmpMinor;
346     krb5_context krbContext;
347     unsigned char *p;
348     size_t len, remain;
349     gss_buffer_desc buf;
350     gss_name_t name = GSS_C_NO_NAME;
351     gss_OID mechanismUsed = GSS_C_NO_OID;
352
353     GSSEAP_KRB_INIT(&krbContext);
354
355     p = (unsigned char *)nameBuffer->value;
356     remain = nameBuffer->length;
357
358     if (flags & EXPORT_NAME_FLAG_OID) {
359         gss_OID_desc mech;
360         enum gss_eap_token_type tokType;
361         uint16_t wireTokType;
362
363         /* TOK_ID || MECH_OID_LEN || MECH_OID */
364         if (remain < 6) {
365             *minor = GSSEAP_BAD_NAME_TOKEN;
366             return GSS_S_BAD_NAME;
367         }
368
369         if (flags & EXPORT_NAME_FLAG_COMPOSITE)
370             tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
371         else
372             tokType = TOK_TYPE_EXPORT_NAME;
373
374         /* TOK_ID */
375         wireTokType = load_uint16_be(p);
376
377         if ((flags & EXPORT_NAME_FLAG_ALLOW_COMPOSITE) &&
378             wireTokType == TOK_TYPE_EXPORT_NAME_COMPOSITE) {
379             tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
380             flags |= EXPORT_NAME_FLAG_COMPOSITE;
381         }
382
383         if (wireTokType != tokType) {
384             *minor = GSSEAP_WRONG_TOK_ID;
385             return GSS_S_BAD_NAME;
386         }
387         UPDATE_REMAIN(2);
388
389         /* MECH_OID_LEN */
390         len = load_uint16_be(p);
391         if (len < 2) {
392             *minor = GSSEAP_BAD_NAME_TOKEN;
393             return GSS_S_BAD_NAME;
394         }
395         UPDATE_REMAIN(2);
396
397         /* MECH_OID */
398         if (p[0] != 0x06) {
399             *minor = GSSEAP_BAD_NAME_TOKEN;
400             return GSS_S_BAD_NAME;
401         }
402
403         mech.length = p[1];
404         mech.elements = &p[2];
405
406         CHECK_REMAIN(mech.length);
407
408         major = gssEapCanonicalizeOid(minor,
409                                       &mech,
410                                       OID_FLAG_FAMILY_MECH_VALID |
411                                         OID_FLAG_MAP_FAMILY_MECH_TO_NULL,
412                                       &mechanismUsed);
413         if (GSS_ERROR(major))
414             goto cleanup;
415
416         UPDATE_REMAIN(2 + mech.length);
417     }
418
419     /* NAME_LEN */
420     CHECK_REMAIN(4);
421     len = load_uint32_be(p);
422     UPDATE_REMAIN(4);
423
424     /* NAME */
425     CHECK_REMAIN(len);
426     buf.length = len;
427     buf.value = p;
428     UPDATE_REMAIN(len);
429
430     major = importEapNameFlags(minor, &buf, 0, &name);
431     if (GSS_ERROR(major))
432         goto cleanup;
433
434     name->mechanismUsed = mechanismUsed;
435     mechanismUsed = GSS_C_NO_OID;
436
437 #ifdef GSSEAP_ENABLE_ACCEPTOR
438     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
439         gss_buffer_desc buf;
440
441         buf.length = remain;
442         buf.value = p;
443
444         major = gssEapImportAttrContext(minor, &buf, name);
445         if (GSS_ERROR(major))
446             goto cleanup;
447     }
448 #endif
449
450     major = GSS_S_COMPLETE;
451     *minor = 0;
452
453 cleanup:
454     if (GSS_ERROR(major)) {
455         gssEapReleaseOid(&tmpMinor, &mechanismUsed);
456         gssEapReleaseName(&tmpMinor, &name);
457     } else {
458         *pName = name;
459     }
460
461     return major;
462 }
463
464 static OM_uint32
465 importExportName(OM_uint32 *minor,
466                  const gss_buffer_t nameBuffer,
467                  gss_name_t *name)
468 {
469     return gssEapImportNameInternal(minor, nameBuffer, name,
470                                     EXPORT_NAME_FLAG_OID |
471                                     EXPORT_NAME_FLAG_ALLOW_COMPOSITE);
472 }
473
474 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
475 static OM_uint32
476 importCompositeExportName(OM_uint32 *minor,
477                           const gss_buffer_t nameBuffer,
478                           gss_name_t *name)
479 {
480     return gssEapImportNameInternal(minor, nameBuffer, name,
481                                     EXPORT_NAME_FLAG_OID |
482                                     EXPORT_NAME_FLAG_COMPOSITE);
483 }
484 #endif
485
486 struct gss_eap_name_import_provider {
487     gss_const_OID oid;
488     OM_uint32 (*import)(OM_uint32 *, const gss_buffer_t, gss_name_t *);
489 };
490
491 OM_uint32
492 gssEapImportName(OM_uint32 *minor,
493                  const gss_buffer_t nameBuffer,
494                  const gss_OID nameType,
495                  const gss_OID mechType,
496                  gss_name_t *pName)
497 {
498     struct gss_eap_name_import_provider nameTypes[] = {
499         { GSS_EAP_NT_EAP_NAME,              importEapName               },
500         { GSS_C_NT_USER_NAME,               importUserName              },
501         { GSS_C_NT_HOSTBASED_SERVICE,       importServiceName           },
502         { GSS_C_NT_HOSTBASED_SERVICE_X,     importServiceName           },
503         { GSS_C_NT_ANONYMOUS,               importAnonymousName         },
504         { GSS_C_NT_EXPORT_NAME,             importExportName            },
505         { GSS_KRB5_NT_PRINCIPAL_NAME,       importUserName              },
506 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
507         { GSS_C_NT_COMPOSITE_EXPORT,        importCompositeExportName   },
508 #endif
509     };
510     size_t i;
511     OM_uint32 major = GSS_S_BAD_NAMETYPE;
512     OM_uint32 tmpMinor;
513     gss_name_t name = GSS_C_NO_NAME;
514
515     for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) {
516         if (oidEqual(nameTypes[i].oid,
517                      nameType == GSS_C_NO_OID ? GSS_EAP_NT_EAP_NAME : nameType)) {
518             major = nameTypes[i].import(minor, nameBuffer, &name);
519             break;
520         }
521     }
522
523     if (major == GSS_S_COMPLETE &&
524         mechType != GSS_C_NO_OID) {
525         GSSEAP_ASSERT(gssEapIsConcreteMechanismOid(mechType));
526         GSSEAP_ASSERT(name != GSS_C_NO_NAME);
527         GSSEAP_ASSERT(name->mechanismUsed == GSS_C_NO_OID);
528
529         major = gssEapCanonicalizeOid(minor, mechType, 0, &name->mechanismUsed);
530     }
531
532     if (GSS_ERROR(major))
533         gssEapReleaseName(&tmpMinor, &name);
534     else
535         *pName = name;
536
537     return major;
538 }
539
540 OM_uint32
541 gssEapExportName(OM_uint32 *minor,
542                  gss_const_name_t name,
543                  gss_buffer_t exportedName)
544 {
545     return gssEapExportNameInternal(minor, name, exportedName,
546                                     EXPORT_NAME_FLAG_OID);
547 }
548
549 OM_uint32
550 gssEapExportNameInternal(OM_uint32 *minor,
551                          gss_const_name_t name,
552                          gss_buffer_t exportedName,
553                          OM_uint32 flags)
554 {
555     OM_uint32 major = GSS_S_FAILURE, tmpMinor;
556     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
557     size_t exportedNameLen;
558     unsigned char *p;
559     gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER;
560     gss_OID mech;
561
562     exportedName->length = 0;
563     exportedName->value = NULL;
564
565     if (name->mechanismUsed != GSS_C_NO_OID)
566         mech = name->mechanismUsed;
567     else
568         mech = GSS_EAP_MECHANISM;
569
570     major = gssEapDisplayName(minor, name, &nameBuf, NULL);
571     if (GSS_ERROR(major))
572         goto cleanup;
573
574     exportedNameLen = 0;
575     if (flags & EXPORT_NAME_FLAG_OID) {
576         exportedNameLen += 6 + mech->length;
577     }
578     exportedNameLen += 4 + nameBuf.length;
579 #ifdef GSSEAP_ENABLE_ACCEPTOR
580     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
581         major = gssEapExportAttrContext(minor, name, &attrs);
582         if (GSS_ERROR(major))
583             goto cleanup;
584         exportedNameLen += attrs.length;
585     }
586 #endif
587
588     exportedName->value = GSSEAP_MALLOC(exportedNameLen);
589     if (exportedName->value == NULL) {
590         major = GSS_S_FAILURE;
591         *minor = ENOMEM;
592         goto cleanup;
593     }
594     exportedName->length = exportedNameLen;
595
596     p = (unsigned char *)exportedName->value;
597
598     if (flags & EXPORT_NAME_FLAG_OID) {
599         /* TOK | MECH_OID_LEN */
600         store_uint16_be((flags & EXPORT_NAME_FLAG_COMPOSITE)
601                         ? TOK_TYPE_EXPORT_NAME_COMPOSITE
602                         : TOK_TYPE_EXPORT_NAME,
603                         p);
604         p += 2;
605         store_uint16_be(mech->length + 2, p);
606         p += 2;
607
608         /* MECH_OID */
609         *p++ = 0x06;
610         *p++ = mech->length & 0xff;
611         memcpy(p, mech->elements, mech->length);
612         p += mech->length;
613     }
614
615     /* NAME_LEN */
616     store_uint32_be(nameBuf.length, p);
617     p += 4;
618
619     /* NAME */
620     memcpy(p, nameBuf.value, nameBuf.length);
621     p += nameBuf.length;
622
623     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
624         memcpy(p, attrs.value, attrs.length);
625         p += attrs.length;
626     }
627
628     GSSEAP_ASSERT(p == (unsigned char *)exportedName->value + exportedNameLen);
629
630     major = GSS_S_COMPLETE;
631     *minor = 0;
632
633 cleanup:
634     gss_release_buffer(&tmpMinor, &attrs);
635     gss_release_buffer(&tmpMinor, &nameBuf);
636     if (GSS_ERROR(major))
637         gss_release_buffer(&tmpMinor, exportedName);
638
639     return major;
640 }
641
642 OM_uint32
643 gssEapCanonicalizeName(OM_uint32 *minor,
644                        gss_const_name_t input_name,
645                        const gss_OID mech_type,
646                        gss_name_t *dest_name)
647 {
648     OM_uint32 major, tmpMinor;
649     krb5_context krbContext;
650     gss_name_t name;
651     gss_OID mech_used;
652
653     if (input_name == GSS_C_NO_NAME) {
654         *minor = EINVAL;
655         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
656     }
657
658     GSSEAP_KRB_INIT(&krbContext);
659
660     major = gssEapAllocName(minor, &name);
661     if (GSS_ERROR(major)) {
662         return major;
663     }
664
665     if (mech_type != GSS_C_NO_OID)
666         mech_used = mech_type;
667     else
668         mech_used = input_name->mechanismUsed;
669
670     major = gssEapCanonicalizeOid(minor,
671                                   mech_used,
672                                   OID_FLAG_NULL_VALID,
673                                   &name->mechanismUsed);
674     if (GSS_ERROR(major))
675         goto cleanup;
676
677     name->flags = input_name->flags;
678
679     *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal,
680                                  &name->krbPrincipal);
681     if (*minor != 0) {
682         major = GSS_S_FAILURE;
683         goto cleanup;
684     }
685
686 #ifdef GSSEAP_ENABLE_ACCEPTOR
687     if (input_name->attrCtx != NULL) {
688         major = gssEapDuplicateAttrContext(minor, input_name, name);
689         if (GSS_ERROR(major))
690             goto cleanup;
691     }
692 #endif
693
694     *dest_name = name;
695
696 cleanup:
697     if (GSS_ERROR(major)) {
698         gssEapReleaseName(&tmpMinor, &name);
699     }
700
701     return major;
702 }
703
704 OM_uint32
705 gssEapDuplicateName(OM_uint32 *minor,
706                     gss_const_name_t input_name,
707                     gss_name_t *dest_name)
708 {
709     return gssEapCanonicalizeName(minor, input_name,
710                                   GSS_C_NO_OID, dest_name);
711 }
712
713 static int
714 hasRealmP(gss_const_name_t name)
715 {
716 #ifdef HAVE_HEIMDAL_VERSION
717     if (KRB_PRINC_REALM(name->krbPrincipal) != NULL &&
718         KRB_PRINC_REALM(name->krbPrincipal)[0] != '\0')
719 #else
720     if (KRB_PRINC_REALM(name->krbPrincipal)->length != 0)
721 #endif
722         return TRUE;
723
724     return FALSE;
725 }
726
727 OM_uint32
728 gssEapDisplayName(OM_uint32 *minor,
729                   gss_const_name_t name,
730                   gss_buffer_t output_name_buffer,
731                   gss_OID *output_name_type)
732 {
733     OM_uint32 major;
734     krb5_context krbContext;
735     char *krbName;
736     gss_OID name_type;
737     int flags = 0;
738
739     GSSEAP_KRB_INIT(&krbContext);
740
741     output_name_buffer->length = 0;
742     output_name_buffer->value = NULL;
743
744     if (name == GSS_C_NO_NAME) {
745         *minor = EINVAL;
746         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
747     }
748
749     /*
750      * According to draft-ietf-abfab-gss-eap-01, when the realm is
751      * absent the trailing '@' is not included.
752      */
753     if (!hasRealmP(name))
754         flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
755
756     *minor = krb5_unparse_name_flags(krbContext, name->krbPrincipal,
757                                      flags, &krbName);
758     if (*minor != 0) {
759         return GSS_S_FAILURE;
760     }
761
762     major = makeStringBuffer(minor, krbName, output_name_buffer);
763 #ifdef HAVE_HEIMDAL_VERSION
764     krb5_xfree(krbName);
765 #else
766     krb5_free_unparsed_name(krbContext, krbName);
767 #endif
768     if (GSS_ERROR(major))
769         return major;
770
771     if (output_name_buffer->length == 0) {
772         name_type = GSS_C_NT_ANONYMOUS;
773     } else if (name->flags & NAME_FLAG_NAI) {
774         name_type = GSS_C_NT_USER_NAME;
775     } else {
776         name_type = GSS_EAP_NT_EAP_NAME;
777     }
778
779     if (output_name_type != NULL)
780         *output_name_type = name_type;
781
782     return GSS_S_COMPLETE;
783 }
784
785 OM_uint32
786 gssEapCompareName(OM_uint32 *minor,
787                   gss_const_name_t name1,
788                   gss_const_name_t name2,
789                   OM_uint32 flags,
790                   int *name_equal)
791 {
792     krb5_context krbContext;
793
794     *minor = 0;
795
796     if (name1 == GSS_C_NO_NAME && name2 == GSS_C_NO_NAME) {
797         *name_equal = 1;
798     } else if (name1 != GSS_C_NO_NAME && name2 != GSS_C_NO_NAME) {
799         GSSEAP_KRB_INIT(&krbContext);
800
801         /* krbPrincipal is immutable, so lock not required */
802         if ((flags & COMPARE_NAME_FLAG_IGNORE_EMPTY_REALMS) &&
803             (hasRealmP(name1) == FALSE || hasRealmP(name2) == FALSE)) {
804             *name_equal = krb5_principal_compare_any_realm(krbContext,
805                                                            name1->krbPrincipal,
806                                                            name2->krbPrincipal);
807         } else {
808             *name_equal = krb5_principal_compare(krbContext,
809                                                  name1->krbPrincipal,
810                                                  name2->krbPrincipal);
811         }
812     } else {
813         *name_equal = 0;
814     }
815
816     return GSS_S_COMPLETE;
817 }