e20dffd53d75ba2ed0c84e818c59dbec64140778
[moonshot.git] / moonshot / 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.4.1.5322.22.2.1  */
64     10, "\x2B\x06\x01\x04\x01\xA9\x4A\x16\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 = errno;
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
116 #ifdef GSSEAP_ENABLE_ACCEPTOR
117     gssEapReleaseAttrContext(&tmpMinor, name);
118 #endif
119
120     GSSEAP_MUTEX_DESTROY(&name->mutex);
121     GSSEAP_FREE(name);
122     *pName = NULL;
123
124     return GSS_S_COMPLETE;
125 }
126
127 static OM_uint32
128 krbPrincipalToName(OM_uint32 *minor,
129                    krb5_principal *principal,
130                    gss_name_t *pName)
131 {
132     OM_uint32 major;
133     gss_name_t name;
134
135     major = gssEapAllocName(minor, &name);
136     if (GSS_ERROR(major))
137         return major;
138
139     name->krbPrincipal = *principal;
140     *principal = NULL;
141
142     if (KRB_PRINC_LENGTH(name->krbPrincipal) > 1) {
143         name->flags |= NAME_FLAG_SERVICE;
144     } else {
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         GSSEAP_FREE(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) = GSSEAP_CALLOC(1, sizeof(char));
270             if (KRB_PRINC_REALM(krbPrinc) == NULL)
271                 code = ENOMEM;
272         }
273 #endif
274
275         if (defaultRealm != NULL)
276             GSSEAP_FREE(defaultRealm);
277     }
278
279     if (nameBuffer != GSS_C_NO_BUFFER)
280         GSSEAP_FREE(nameString);
281
282     if (code != 0) {
283         *minor = code;
284         return GSS_S_FAILURE;
285     }
286
287     assert(krbPrinc != NULL);
288
289     major = krbPrincipalToName(minor, &krbPrinc, pName);
290     if (GSS_ERROR(major))
291         krb5_free_principal(krbContext, krbPrinc);
292
293     return major;
294 }
295
296 static OM_uint32
297 importEapName(OM_uint32 *minor,
298               const gss_buffer_t nameBuffer,
299               gss_name_t *pName)
300 {
301     return importEapNameFlags(minor, nameBuffer, 0, pName);
302 }
303
304 static OM_uint32
305 importUserName(OM_uint32 *minor,
306                const gss_buffer_t nameBuffer,
307                gss_name_t *pName)
308 {
309     return importEapNameFlags(minor, nameBuffer, IMPORT_FLAG_DEFAULT_REALM, pName);
310 }
311
312 static OM_uint32
313 importAnonymousName(OM_uint32 *minor,
314                     const gss_buffer_t nameBuffer GSSEAP_UNUSED,
315                     gss_name_t *pName)
316 {
317     return importEapNameFlags(minor, GSS_C_NO_BUFFER, 0, pName);
318 }
319
320 #define UPDATE_REMAIN(n)    do {            \
321         p += (n);                           \
322         remain -= (n);                      \
323     } while (0)
324
325 #define CHECK_REMAIN(n)     do {        \
326         if (remain < (n)) {             \
327             major = GSS_S_BAD_NAME;     \
328             *minor = GSSEAP_TOK_TRUNC;  \
329             goto cleanup;               \
330         }                               \
331     } while (0)
332
333 OM_uint32
334 gssEapImportNameInternal(OM_uint32 *minor,
335                          const gss_buffer_t nameBuffer,
336                          gss_name_t *pName,
337                          OM_uint32 flags)
338 {
339     OM_uint32 major, tmpMinor;
340     krb5_context krbContext;
341     unsigned char *p;
342     size_t len, remain;
343     gss_buffer_desc buf;
344     gss_name_t name = GSS_C_NO_NAME;
345     gss_OID mechanismUsed = GSS_C_NO_OID;
346
347     GSSEAP_KRB_INIT(&krbContext);
348
349     p = (unsigned char *)nameBuffer->value;
350     remain = nameBuffer->length;
351
352     if (flags & EXPORT_NAME_FLAG_OID) {
353         gss_OID_desc mech;
354         enum gss_eap_token_type tokType;
355         uint16_t wireTokType;
356
357         /* TOK_ID || MECH_OID_LEN || MECH_OID */
358         if (remain < 6) {
359             *minor = GSSEAP_BAD_NAME_TOKEN;
360             return GSS_S_BAD_NAME;
361         }
362
363         if (flags & EXPORT_NAME_FLAG_COMPOSITE)
364             tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
365         else
366             tokType = TOK_TYPE_EXPORT_NAME;
367
368         /* TOK_ID */
369         wireTokType = load_uint16_be(p);
370
371         if ((flags & EXPORT_NAME_FLAG_ALLOW_COMPOSITE) &&
372             wireTokType == TOK_TYPE_EXPORT_NAME_COMPOSITE) {
373             tokType = TOK_TYPE_EXPORT_NAME_COMPOSITE;
374             flags |= EXPORT_NAME_FLAG_COMPOSITE;
375         }
376
377         if (wireTokType != tokType) {
378             *minor = GSSEAP_WRONG_TOK_ID;
379             return GSS_S_BAD_NAME;
380         }
381         UPDATE_REMAIN(2);
382
383         /* MECH_OID_LEN */
384         len = load_uint16_be(p);
385         if (len < 2) {
386             *minor = GSSEAP_BAD_NAME_TOKEN;
387             return GSS_S_BAD_NAME;
388         }
389         UPDATE_REMAIN(2);
390
391         /* MECH_OID */
392         if (p[0] != 0x06) {
393             *minor = GSSEAP_BAD_NAME_TOKEN;
394             return GSS_S_BAD_NAME;
395         }
396
397         mech.length = p[1];
398         mech.elements = &p[2];
399
400         CHECK_REMAIN(mech.length);
401
402         major = gssEapCanonicalizeOid(minor,
403                                       &mech,
404                                       OID_FLAG_FAMILY_MECH_VALID |
405                                         OID_FLAG_MAP_FAMILY_MECH_TO_NULL,
406                                       &mechanismUsed);
407         if (GSS_ERROR(major))
408             goto cleanup;
409
410         UPDATE_REMAIN(2 + mech.length);
411     }
412
413     /* NAME_LEN */
414     CHECK_REMAIN(4);
415     len = load_uint32_be(p);
416     UPDATE_REMAIN(4);
417
418     /* NAME */
419     CHECK_REMAIN(len);
420     buf.length = len;
421     buf.value = p;
422     UPDATE_REMAIN(len);
423
424     major = importEapNameFlags(minor, &buf, 0, &name);
425     if (GSS_ERROR(major))
426         goto cleanup;
427
428     name->mechanismUsed = mechanismUsed;
429     mechanismUsed = GSS_C_NO_OID;
430
431 #ifdef GSSEAP_ENABLE_ACCEPTOR
432     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
433         gss_buffer_desc buf;
434
435         buf.length = remain;
436         buf.value = p;
437
438         major = gssEapImportAttrContext(minor, &buf, name);
439         if (GSS_ERROR(major))
440             goto cleanup;
441     }
442 #endif
443
444     major = GSS_S_COMPLETE;
445     *minor = 0;
446
447 cleanup:
448     if (GSS_ERROR(major)) {
449         gssEapReleaseOid(&tmpMinor, &mechanismUsed);
450         gssEapReleaseName(&tmpMinor, &name);
451     } else {
452         *pName = name;
453     }
454
455     return major;
456 }
457
458 static OM_uint32
459 importExportName(OM_uint32 *minor,
460                  const gss_buffer_t nameBuffer,
461                  gss_name_t *name)
462 {
463     return gssEapImportNameInternal(minor, nameBuffer, name,
464                                     EXPORT_NAME_FLAG_OID |
465                                     EXPORT_NAME_FLAG_ALLOW_COMPOSITE);
466 }
467
468 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
469 static OM_uint32
470 importCompositeExportName(OM_uint32 *minor,
471                           const gss_buffer_t nameBuffer,
472                           gss_name_t *name)
473 {
474     return gssEapImportNameInternal(minor, nameBuffer, name,
475                                     EXPORT_NAME_FLAG_OID |
476                                     EXPORT_NAME_FLAG_COMPOSITE);
477 }
478 #endif
479
480 struct gss_eap_name_import_provider {
481     gss_const_OID oid;
482     OM_uint32 (*import)(OM_uint32 *, const gss_buffer_t, gss_name_t *);
483 };
484
485 OM_uint32
486 gssEapImportName(OM_uint32 *minor,
487                  const gss_buffer_t nameBuffer,
488                  const gss_OID nameType,
489                  const gss_OID mechType,
490                  gss_name_t *pName)
491 {
492     struct gss_eap_name_import_provider nameTypes[] = {
493         { GSS_EAP_NT_EAP_NAME,              importEapName               },
494         { GSS_C_NT_USER_NAME,               importUserName              },
495         { GSS_C_NT_HOSTBASED_SERVICE,       importServiceName           },
496         { GSS_C_NT_HOSTBASED_SERVICE_X,     importServiceName           },
497         { GSS_C_NT_ANONYMOUS,               importAnonymousName         },
498         { GSS_C_NT_EXPORT_NAME,             importExportName            },
499         { GSS_KRB5_NT_PRINCIPAL_NAME,       importUserName              },
500 #ifdef HAVE_GSS_C_NT_COMPOSITE_EXPORT
501         { GSS_C_NT_COMPOSITE_EXPORT,        importCompositeExportName   },
502 #endif
503     };
504     size_t i;
505     OM_uint32 major = GSS_S_BAD_NAMETYPE;
506     OM_uint32 tmpMinor;
507     gss_name_t name = GSS_C_NO_NAME;
508
509     for (i = 0; i < sizeof(nameTypes) / sizeof(nameTypes[0]); i++) {
510         if (oidEqual(nameTypes[i].oid,
511                      nameType == GSS_C_NO_OID ? GSS_EAP_NT_EAP_NAME : nameType)) {
512             major = nameTypes[i].import(minor, nameBuffer, &name);
513             break;
514         }
515     }
516
517     if (major == GSS_S_COMPLETE &&
518         mechType != GSS_C_NO_OID) {
519         assert(gssEapIsConcreteMechanismOid(mechType));
520         assert(name->mechanismUsed == GSS_C_NO_OID);
521
522         major = gssEapCanonicalizeOid(minor, mechType, 0, &name->mechanismUsed);
523     }
524
525     if (GSS_ERROR(major))
526         gssEapReleaseName(&tmpMinor, &name);
527     else
528         *pName = name;
529
530     return major;
531 }
532
533 OM_uint32
534 gssEapExportName(OM_uint32 *minor,
535                  const gss_name_t name,
536                  gss_buffer_t exportedName)
537 {
538     return gssEapExportNameInternal(minor, name, exportedName,
539                                     EXPORT_NAME_FLAG_OID);
540 }
541
542 OM_uint32
543 gssEapExportNameInternal(OM_uint32 *minor,
544                          const gss_name_t name,
545                          gss_buffer_t exportedName,
546                          OM_uint32 flags)
547 {
548     OM_uint32 major = GSS_S_FAILURE, tmpMinor;
549     gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
550     size_t exportedNameLen;
551     unsigned char *p;
552     gss_buffer_desc attrs = GSS_C_EMPTY_BUFFER;
553     gss_OID mech;
554
555     exportedName->length = 0;
556     exportedName->value = NULL;
557
558     if (name->mechanismUsed != GSS_C_NO_OID)
559         mech = name->mechanismUsed;
560     else
561         mech = GSS_EAP_MECHANISM;
562
563     major = gssEapDisplayName(minor, name, &nameBuf, NULL);
564     if (GSS_ERROR(major))
565         goto cleanup;
566
567     exportedNameLen = 0;
568     if (flags & EXPORT_NAME_FLAG_OID) {
569         exportedNameLen += 6 + mech->length;
570     }
571     exportedNameLen += 4 + nameBuf.length;
572 #ifdef GSSEAP_ENABLE_ACCEPTOR
573     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
574         major = gssEapExportAttrContext(minor, name, &attrs);
575         if (GSS_ERROR(major))
576             goto cleanup;
577         exportedNameLen += attrs.length;
578     }
579 #endif
580
581     exportedName->value = GSSEAP_MALLOC(exportedNameLen);
582     if (exportedName->value == NULL) {
583         major = GSS_S_FAILURE;
584         *minor = ENOMEM;
585         goto cleanup;
586     }
587     exportedName->length = exportedNameLen;
588
589     p = (unsigned char *)exportedName->value;
590
591     if (flags & EXPORT_NAME_FLAG_OID) {
592         /* TOK | MECH_OID_LEN */
593         store_uint16_be((flags & EXPORT_NAME_FLAG_COMPOSITE)
594                         ? TOK_TYPE_EXPORT_NAME_COMPOSITE
595                         : TOK_TYPE_EXPORT_NAME,
596                         p);
597         p += 2;
598         store_uint16_be(mech->length + 2, p);
599         p += 2;
600
601         /* MECH_OID */
602         *p++ = 0x06;
603         *p++ = mech->length & 0xff;
604         memcpy(p, mech->elements, mech->length);
605         p += mech->length;
606     }
607
608     /* NAME_LEN */
609     store_uint32_be(nameBuf.length, p);
610     p += 4;
611
612     /* NAME */
613     memcpy(p, nameBuf.value, nameBuf.length);
614     p += nameBuf.length;
615
616     if (flags & EXPORT_NAME_FLAG_COMPOSITE) {
617         memcpy(p, attrs.value, attrs.length);
618         p += attrs.length;
619     }
620
621     assert(p == (unsigned char *)exportedName->value + exportedNameLen);
622
623     major = GSS_S_COMPLETE;
624     *minor = 0;
625
626 cleanup:
627     gss_release_buffer(&tmpMinor, &attrs);
628     gss_release_buffer(&tmpMinor, &nameBuf);
629     if (GSS_ERROR(major))
630         gss_release_buffer(&tmpMinor, exportedName);
631
632     return major;
633 }
634
635 OM_uint32
636 gssEapCanonicalizeName(OM_uint32 *minor,
637                        const gss_name_t input_name,
638                        const gss_OID mech_type,
639                        gss_name_t *dest_name)
640 {
641     OM_uint32 major, tmpMinor;
642     krb5_context krbContext;
643     gss_name_t name;
644     gss_OID mech_used;
645
646     if (input_name == GSS_C_NO_NAME) {
647         *minor = EINVAL;
648         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
649     }
650
651     GSSEAP_KRB_INIT(&krbContext);
652
653     major = gssEapAllocName(minor, &name);
654     if (GSS_ERROR(major)) {
655         return major;
656     }
657
658     if (mech_type != GSS_C_NO_OID)
659         mech_used = mech_type;
660     else
661         mech_used = input_name->mechanismUsed;
662
663     major = gssEapCanonicalizeOid(minor,
664                                   mech_used,
665                                   OID_FLAG_NULL_VALID,
666                                   &name->mechanismUsed);
667     if (GSS_ERROR(major))
668         goto cleanup;
669
670     name->flags = input_name->flags;
671
672     *minor = krb5_copy_principal(krbContext, input_name->krbPrincipal,
673                                  &name->krbPrincipal);
674     if (*minor != 0) {
675         major = GSS_S_FAILURE;
676         goto cleanup;
677     }
678
679 #ifdef GSSEAP_ENABLE_ACCEPTOR
680     if (input_name->attrCtx != NULL) {
681         major = gssEapDuplicateAttrContext(minor, input_name, name);
682         if (GSS_ERROR(major))
683             goto cleanup;
684     }
685 #endif
686
687     *dest_name = name;
688
689 cleanup:
690     if (GSS_ERROR(major)) {
691         gssEapReleaseName(&tmpMinor, &name);
692     }
693
694     return major;
695 }
696
697 OM_uint32
698 gssEapDuplicateName(OM_uint32 *minor,
699                     const gss_name_t input_name,
700                     gss_name_t *dest_name)
701 {
702     return gssEapCanonicalizeName(minor, input_name,
703                                   GSS_C_NO_OID, dest_name);
704 }
705
706 OM_uint32
707 gssEapDisplayName(OM_uint32 *minor,
708                   gss_name_t name,
709                   gss_buffer_t output_name_buffer,
710                   gss_OID *output_name_type)
711 {
712     OM_uint32 major;
713     krb5_context krbContext;
714     char *krbName;
715     gss_OID name_type;
716     int flags = 0;
717
718     GSSEAP_KRB_INIT(&krbContext);
719
720     output_name_buffer->length = 0;
721     output_name_buffer->value = NULL;
722
723     if (name == GSS_C_NO_NAME) {
724         *minor = EINVAL;
725         return GSS_S_CALL_INACCESSIBLE_READ | GSS_S_BAD_NAME;
726     }
727
728     /*
729      * According to draft-ietf-abfab-gss-eap-01, when the realm is
730      * absent the trailing '@' is not included.
731      */
732 #ifdef HAVE_HEIMDAL_VERSION
733     if (KRB_PRINC_REALM(name->krbPrincipal) == NULL ||
734         KRB_PRINC_REALM(name->krbPrincipal)[0] == '\0')
735 #else
736     if (KRB_PRINC_REALM(name->krbPrincipal)->length == 0)
737 #endif
738         flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
739
740     *minor = krb5_unparse_name_flags(krbContext, name->krbPrincipal,
741                                      flags, &krbName);
742     if (*minor != 0) {
743         return GSS_S_FAILURE;
744     }
745
746     major = makeStringBuffer(minor, krbName, output_name_buffer);
747     if (GSS_ERROR(major)) {
748         krb5_free_unparsed_name(krbContext, krbName);
749         return major;
750     }
751
752     krb5_free_unparsed_name(krbContext, krbName);
753
754     if (output_name_buffer->length == 0) {
755         name_type = GSS_C_NT_ANONYMOUS;
756     } else if (name->flags & NAME_FLAG_NAI) {
757         name_type = GSS_C_NT_USER_NAME;
758     } else {
759         name_type = GSS_EAP_NT_EAP_NAME;
760     }
761
762     if (output_name_type != NULL)
763         *output_name_type = name_type;
764
765     return GSS_S_COMPLETE;
766 }
767
768 OM_uint32
769 gssEapCompareName(OM_uint32 *minor,
770                   gss_name_t name1,
771                   gss_name_t name2,
772                   int *name_equal)
773 {
774     krb5_context krbContext;
775
776     *minor = 0;
777
778     if (name1 == GSS_C_NO_NAME && name2 == GSS_C_NO_NAME) {
779         *name_equal = 1;
780     } else if (name1 != GSS_C_NO_NAME && name2 != GSS_C_NO_NAME) {
781         GSSEAP_KRB_INIT(&krbContext);
782
783         /* krbPrincipal is immutable, so lock not required */
784         *name_equal = krb5_principal_compare(krbContext,
785                                              name1->krbPrincipal,
786                                              name2->krbPrincipal);
787     }
788
789     return GSS_S_COMPLETE;
790 }