Merge remote-tracking branch 'origin/eap-tls'
[mech_eap.git] / mech_eap / util_cred.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 /*
34  * Utility routines for credential handles.
35  */
36
37 #include "gssapiP_eap.h"
38
39 #ifdef WIN32
40 # include <shlobj.h>     /* may need to use ShFolder.h instead */
41 # include <stdio.h>
42 #else
43 # include <pwd.h>
44 #endif
45
46 OM_uint32
47 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
48 {
49     OM_uint32 tmpMinor;
50     gss_cred_id_t cred;
51
52     *pCred = GSS_C_NO_CREDENTIAL;
53
54     cred = (gss_cred_id_t)GSSEAP_CALLOC(1, sizeof(*cred));
55     if (cred == NULL) {
56         *minor = ENOMEM;
57         return GSS_S_FAILURE;
58     }
59
60     if (GSSEAP_MUTEX_INIT(&cred->mutex) != 0) {
61         *minor = GSSEAP_GET_LAST_ERROR();
62         gssEapReleaseCred(&tmpMinor, &cred);
63         return GSS_S_FAILURE;
64     }
65
66     *pCred = cred;
67
68     *minor = 0;
69     return GSS_S_COMPLETE;
70 }
71
72 static void
73 zeroAndReleasePassword(gss_buffer_t password)
74 {
75     if (password->value != NULL) {
76         memset(password->value, 0, password->length);
77         GSSEAP_FREE(password->value);
78     }
79
80     password->value = NULL;
81     password->length = 0;
82 }
83
84 OM_uint32
85 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
86 {
87     OM_uint32 tmpMinor;
88     gss_cred_id_t cred = *pCred;
89     krb5_context krbContext = NULL;
90
91     if (cred == GSS_C_NO_CREDENTIAL) {
92         return GSS_S_COMPLETE;
93     }
94
95     GSSEAP_KRB_INIT(&krbContext);
96
97     gssEapReleaseName(&tmpMinor, &cred->name);
98     gssEapReleaseName(&tmpMinor, &cred->target);
99
100     zeroAndReleasePassword(&cred->password);
101
102     gss_release_buffer(&tmpMinor, &cred->radiusConfigFile);
103     gss_release_buffer(&tmpMinor, &cred->radiusConfigStanza);
104     gss_release_buffer(&tmpMinor, &cred->caCertificate);
105     gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
106     gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
107     gss_release_buffer(&tmpMinor, &cred->clientCertificate);
108     gss_release_buffer(&tmpMinor, &cred->privateKey);
109
110 #ifdef GSSEAP_ENABLE_REAUTH
111     if (cred->krbCredCache != NULL) {
112         if (cred->flags & CRED_FLAG_DEFAULT_CCACHE)
113             krb5_cc_close(krbContext, cred->krbCredCache);
114         else
115             krb5_cc_destroy(krbContext, cred->krbCredCache);
116     }
117     if (cred->reauthCred != GSS_C_NO_CREDENTIAL)
118         gssReleaseCred(&tmpMinor, &cred->reauthCred);
119 #endif
120
121     GSSEAP_MUTEX_DESTROY(&cred->mutex);
122     memset(cred, 0, sizeof(*cred));
123     GSSEAP_FREE(cred);
124     *pCred = NULL;
125
126     *minor = 0;
127     return GSS_S_COMPLETE;
128 }
129
130 static OM_uint32
131 readStaticIdentityFile(OM_uint32 *minor,
132                        gss_buffer_t defaultIdentity,
133                        gss_buffer_t defaultPassword)
134 {
135     OM_uint32 major, tmpMinor;
136     FILE *fp = NULL;
137     char buf[BUFSIZ];
138     char *ccacheName;
139     int i = 0;
140 #ifndef WIN32
141     struct passwd *pw = NULL, pwd;
142     char pwbuf[BUFSIZ];
143 #endif
144
145     defaultIdentity->length = 0;
146     defaultIdentity->value = NULL;
147
148     if (defaultPassword != GSS_C_NO_BUFFER) {
149         defaultPassword->length = 0;
150         defaultPassword->value = NULL;
151     }
152
153     ccacheName = getenv("GSSEAP_IDENTITY");
154     if (ccacheName == NULL) {
155 #ifdef WIN32
156         TCHAR szPath[MAX_PATH];
157
158         if (!SUCCEEDED(SHGetFolderPath(NULL,
159                                        CSIDL_APPDATA, /* |CSIDL_FLAG_CREATE */
160                                        NULL, /* User access token */
161                                        0,    /* SHGFP_TYPE_CURRENT */
162                                        szPath))) {
163             major = GSS_S_CRED_UNAVAIL;
164             *minor = GSSEAP_GET_LAST_ERROR(); /* XXX */
165             goto cleanup;
166         }
167
168         snprintf(buf, sizeof(buf), "%s/.gss_eap_id", szPath);
169 #else
170         if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 ||
171             pw == NULL || pw->pw_dir == NULL) {
172             major = GSS_S_CRED_UNAVAIL;
173             *minor = GSSEAP_GET_LAST_ERROR();
174             goto cleanup;
175         }
176
177         snprintf(buf, sizeof(buf), "%s/.gss_eap_id", pw->pw_dir);
178 #endif /* WIN32 */
179         ccacheName = buf;
180     }
181
182     fp = fopen(ccacheName, "r");
183     if (fp == NULL) {
184         major = GSS_S_CRED_UNAVAIL;
185         *minor = GSSEAP_NO_DEFAULT_CRED;
186         goto cleanup;
187     }
188
189     while (fgets(buf, sizeof(buf), fp) != NULL) {
190         gss_buffer_desc src, *dst;
191
192         src.length = strlen(buf);
193         src.value = buf;
194
195         if (src.length == 0)
196             break;
197
198         if (buf[src.length - 1] == '\n') {
199             buf[src.length - 1] = '\0';
200             if (--src.length == 0)
201                 break;
202         }
203
204         if (i == 0)
205             dst = defaultIdentity;
206         else if (i == 1)
207             dst = defaultPassword;
208         else
209             break;
210
211         if (dst != GSS_C_NO_BUFFER) {
212             major = duplicateBuffer(minor, &src, dst);
213             if (GSS_ERROR(major))
214                 goto cleanup;
215         }
216
217         i++;
218     }
219
220     if (defaultIdentity->length == 0) {
221         major = GSS_S_CRED_UNAVAIL;
222         *minor = GSSEAP_NO_DEFAULT_CRED;
223         goto cleanup;
224     }
225
226     major = GSS_S_COMPLETE;
227     *minor = 0;
228
229 cleanup:
230     if (fp != NULL)
231         fclose(fp);
232
233     if (GSS_ERROR(major)) {
234         gss_release_buffer(&tmpMinor, defaultIdentity);
235         zeroAndReleasePassword(defaultPassword);
236     }
237
238     memset(buf, 0, sizeof(buf));
239
240     return major;
241 }
242
243 gss_OID
244 gssEapPrimaryMechForCred(gss_cred_id_t cred)
245 {
246     gss_OID credMech = GSS_C_NO_OID;
247
248     if (cred != GSS_C_NO_CREDENTIAL &&
249         cred->mechanisms != GSS_C_NO_OID_SET &&
250         cred->mechanisms->count == 1)
251         credMech = &cred->mechanisms->elements[0];
252
253     return credMech;
254 }
255
256 OM_uint32
257 gssEapAcquireCred(OM_uint32 *minor,
258                   const gss_name_t desiredName,
259                   OM_uint32 timeReq GSSEAP_UNUSED,
260                   const gss_OID_set desiredMechs,
261                   int credUsage,
262                   gss_cred_id_t *pCred,
263                   gss_OID_set *pActualMechs,
264                   OM_uint32 *timeRec)
265 {
266     OM_uint32 major, tmpMinor;
267     gss_cred_id_t cred;
268
269     /* XXX TODO validate with changed set_cred_option API */
270     *pCred = GSS_C_NO_CREDENTIAL;
271
272     major = gssEapAllocCred(minor, &cred);
273     if (GSS_ERROR(major))
274         goto cleanup;
275
276     switch (credUsage) {
277     case GSS_C_BOTH:
278         cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT;
279         break;
280     case GSS_C_INITIATE:
281         cred->flags |= CRED_FLAG_INITIATE;
282         break;
283     case GSS_C_ACCEPT:
284         cred->flags |= CRED_FLAG_ACCEPT;
285         break;
286     default:
287         major = GSS_S_FAILURE;
288         *minor = GSSEAP_BAD_USAGE;
289         goto cleanup;
290         break;
291     }
292
293     major = gssEapValidateMechs(minor, desiredMechs);
294     if (GSS_ERROR(major))
295         goto cleanup;
296
297     major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms);
298     if (GSS_ERROR(major))
299         goto cleanup;
300
301     if (desiredName != GSS_C_NO_NAME) {
302         GSSEAP_MUTEX_LOCK(&desiredName->mutex);
303
304         major = gssEapDuplicateName(minor, desiredName, &cred->name);
305         if (GSS_ERROR(major)) {
306             GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
307             goto cleanup;
308         }
309
310         GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
311     }
312
313 #ifdef GSSEAP_ENABLE_ACCEPTOR
314     if (cred->flags & CRED_FLAG_ACCEPT) {
315         struct rs_context *radContext;
316
317         major = gssEapCreateRadiusContext(minor, cred, &radContext);
318         if (GSS_ERROR(major))
319             goto cleanup;
320
321         rs_context_destroy(radContext);
322     }
323 #endif
324
325     if (pActualMechs != NULL) {
326         major = duplicateOidSet(minor, cred->mechanisms, pActualMechs);
327         if (GSS_ERROR(major))
328             goto cleanup;
329     }
330
331     if (timeRec != NULL)
332         *timeRec = GSS_C_INDEFINITE;
333
334     *pCred = cred;
335
336     major = GSS_S_COMPLETE;
337     *minor = 0;
338
339 cleanup:
340     if (GSS_ERROR(major))
341         gssEapReleaseCred(&tmpMinor, &cred);
342
343     return major;
344 }
345
346 /*
347  * Return TRUE if cred available for mechanism. Caller need no acquire
348  * lock because mechanisms list is immutable.
349  */
350 int
351 gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
352 {
353     OM_uint32 minor;
354     int present = 0;
355
356     GSSEAP_ASSERT(mech != GSS_C_NO_OID);
357
358     if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
359         return TRUE;
360
361     gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present);
362
363     return present;
364 }
365
366 static OM_uint32
367 staticIdentityFileResolveDefaultIdentity(OM_uint32 *minor,
368                                          const gss_cred_id_t cred,
369                                          gss_name_t *pName)
370 {
371     OM_uint32 major, tmpMinor;
372     gss_OID nameMech = gssEapPrimaryMechForCred(cred);
373     gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
374
375     *pName = GSS_C_NO_NAME;
376
377     major = readStaticIdentityFile(minor, &defaultIdentity, GSS_C_NO_BUFFER);
378     if (major == GSS_S_COMPLETE) {
379         major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
380                                  nameMech, pName);
381     }
382
383     gss_release_buffer(&tmpMinor, &defaultIdentity);
384
385     return major;
386 }
387
388 static OM_uint32
389 gssEapResolveCredIdentity(OM_uint32 *minor,
390                           gss_cred_id_t cred)
391 {
392     OM_uint32 major;
393     gss_OID nameMech = gssEapPrimaryMechForCred(cred);
394
395     if (cred->name != GSS_C_NO_NAME) {
396         *minor = 0;
397         return GSS_S_COMPLETE;
398     }
399
400     if (cred->flags & CRED_FLAG_ACCEPT) {
401         gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
402         char serviceName[5 + MAXHOSTNAMELEN];
403
404         /* default host-based service is host@localhost */
405         memcpy(serviceName, "host@", 5);
406         if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
407             *minor = GSSEAP_NO_HOSTNAME;
408             return GSS_S_FAILURE;
409         }
410
411         nameBuf.value = serviceName;
412         nameBuf.length = strlen((char *)nameBuf.value);
413
414         major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
415                                  nameMech, &cred->name);
416         if (GSS_ERROR(major))
417             return major;
418     } else if (cred->flags & CRED_FLAG_INITIATE) {
419 #ifdef HAVE_MOONSHOT_GET_IDENTITY
420         major = libMoonshotResolveDefaultIdentity(minor, cred, &cred->name);
421         if (major == GSS_S_CRED_UNAVAIL)
422 #endif
423             major = staticIdentityFileResolveDefaultIdentity(minor, cred, &cred->name);
424         if (major != GSS_S_CRED_UNAVAIL)
425             return major;
426     }
427
428     *minor = 0;
429     return GSS_S_COMPLETE;
430 }
431
432 OM_uint32
433 gssEapInquireCred(OM_uint32 *minor,
434                   gss_cred_id_t cred,
435                   gss_name_t *name,
436                   OM_uint32 *pLifetime,
437                   gss_cred_usage_t *cred_usage,
438                   gss_OID_set *mechanisms)
439 {
440     OM_uint32 major;
441     time_t now, lifetime;
442
443     if (name != NULL) {
444         major = gssEapResolveCredIdentity(minor, cred);
445         if (GSS_ERROR(major))
446             goto cleanup;
447
448         if (cred->name != GSS_C_NO_NAME) {
449             major = gssEapDuplicateName(minor, cred->name, name);
450             if (GSS_ERROR(major))
451                 goto cleanup;
452         } else
453             *name = GSS_C_NO_NAME;
454     }
455
456     if (cred_usage != NULL) {
457         OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT));
458
459         switch (flags) {
460         case CRED_FLAG_INITIATE:
461             *cred_usage = GSS_C_INITIATE;
462             break;
463         case CRED_FLAG_ACCEPT:
464             *cred_usage = GSS_C_ACCEPT;
465             break;
466         default:
467             *cred_usage = GSS_C_BOTH;
468             break;
469         }
470     }
471
472     if (mechanisms != NULL) {
473         if (cred->mechanisms != GSS_C_NO_OID_SET)
474             major = duplicateOidSet(minor, cred->mechanisms, mechanisms);
475         else
476             major = gssEapIndicateMechs(minor, mechanisms);
477         if (GSS_ERROR(major))
478             goto cleanup;
479     }
480
481     if (cred->expiryTime == 0) {
482         lifetime = GSS_C_INDEFINITE;
483     } else  {
484         now = time(NULL);
485         lifetime = now - cred->expiryTime;
486         if (lifetime < 0)
487             lifetime = 0;
488     }
489
490     if (pLifetime != NULL) {
491         *pLifetime = lifetime;
492     }
493
494     if (lifetime == 0) {
495         major = GSS_S_CREDENTIALS_EXPIRED;
496         *minor = GSSEAP_CRED_EXPIRED;
497         goto cleanup;
498     }
499
500     major = GSS_S_COMPLETE;
501     *minor = 0;
502
503 cleanup:
504     return major;
505 }
506
507 OM_uint32
508 gssEapSetCredPassword(OM_uint32 *minor,
509                       gss_cred_id_t cred,
510                       const gss_buffer_t password)
511 {
512     OM_uint32 major, tmpMinor;
513     gss_buffer_desc newPassword = GSS_C_EMPTY_BUFFER;
514
515     if (cred->flags & CRED_FLAG_RESOLVED) {
516         major = GSS_S_FAILURE;
517         *minor = GSSEAP_CRED_RESOLVED;
518         goto cleanup;
519     }
520
521     if (password != GSS_C_NO_BUFFER) {
522         major = duplicateBuffer(minor, password, &newPassword);
523         if (GSS_ERROR(major))
524             goto cleanup;
525
526         cred->flags |= CRED_FLAG_PASSWORD;
527     } else {
528         cred->flags &= ~(CRED_FLAG_PASSWORD);
529     }
530
531     gss_release_buffer(&tmpMinor, &cred->password);
532     cred->password = newPassword;
533
534     major = GSS_S_COMPLETE;
535     *minor = 0;
536
537 cleanup:
538     return major;
539 }
540
541 /*
542  * Currently only the privateKey path is exposed to the application
543  * (via gss_set_cred_option() or the third line in ~/.gss_eap_id).
544  * At some point in the future we may add support for setting the
545  * client certificate separately.
546  */
547 OM_uint32
548 gssEapSetCredClientCertificate(OM_uint32 *minor,
549                               gss_cred_id_t cred,
550                               const gss_buffer_t clientCert,
551                               const gss_buffer_t privateKey)
552 {
553     OM_uint32 major, tmpMinor;
554     gss_buffer_desc newClientCert = GSS_C_EMPTY_BUFFER;
555     gss_buffer_desc newPrivateKey = GSS_C_EMPTY_BUFFER;
556
557     if (cred->flags & CRED_FLAG_RESOLVED) {
558         major = GSS_S_FAILURE;
559         *minor = GSSEAP_CRED_RESOLVED;
560         goto cleanup;
561     }
562
563     if (clientCert == GSS_C_NO_BUFFER &&
564         privateKey == GSS_C_NO_BUFFER) {
565         cred->flags &= ~(CRED_FLAG_CERTIFICATE);
566         major = GSS_S_COMPLETE;
567         *minor = 0;
568         goto cleanup;
569     }
570
571     if (clientCert != GSS_C_NO_BUFFER) {
572         major = duplicateBuffer(minor, clientCert, &newClientCert);
573         if (GSS_ERROR(major))
574             goto cleanup;
575     }
576
577     if (privateKey != GSS_C_NO_BUFFER) {
578         major = duplicateBuffer(minor, privateKey, &newPrivateKey);
579         if (GSS_ERROR(major))
580             goto cleanup;
581     }
582
583     cred->flags |= CRED_FLAG_CERTIFICATE;
584
585     gss_release_buffer(&tmpMinor, &cred->clientCertificate);
586     cred->clientCertificate = newClientCert;
587
588     gss_release_buffer(&tmpMinor, &cred->privateKey);
589     cred->privateKey = newPrivateKey;
590
591     major = GSS_S_COMPLETE;
592     *minor = 0;
593
594 cleanup:
595     if (GSS_ERROR(major)) {
596         gss_release_buffer(&tmpMinor, &newClientCert);
597         gss_release_buffer(&tmpMinor, &newPrivateKey);
598     }
599
600     return major;
601 }
602
603 OM_uint32
604 gssEapSetCredService(OM_uint32 *minor,
605                      gss_cred_id_t cred,
606                      const gss_name_t target)
607 {
608     OM_uint32 major, tmpMinor;
609     gss_name_t newTarget = GSS_C_NO_NAME;
610
611     if (cred->flags & CRED_FLAG_RESOLVED) {
612         major = GSS_S_FAILURE;
613         *minor = GSSEAP_CRED_RESOLVED;
614         goto cleanup;
615     }
616
617     if (target != GSS_C_NO_NAME) {
618         major = gssEapDuplicateName(minor, target, &newTarget);
619         if (GSS_ERROR(major))
620             goto cleanup;
621
622         cred->flags |= CRED_FLAG_TARGET;
623     } else {
624         cred->flags &= ~(CRED_FLAG_TARGET);
625     }
626
627     gssEapReleaseName(&tmpMinor, &cred->target);
628     cred->target = newTarget;
629
630     major = GSS_S_COMPLETE;
631     *minor = 0;
632
633 cleanup:
634     return major;
635 }
636
637 static OM_uint32
638 gssEapDuplicateCred(OM_uint32 *minor,
639                     const gss_cred_id_t src,
640                     gss_cred_id_t *pDst)
641 {
642     OM_uint32 major, tmpMinor;
643     gss_cred_id_t dst = GSS_C_NO_CREDENTIAL;
644
645     *pDst = GSS_C_NO_CREDENTIAL;
646
647     major = gssEapAllocCred(minor, &dst);
648     if (GSS_ERROR(major))
649         goto cleanup;
650
651     dst->flags = src->flags;
652
653     if (src->name != GSS_C_NO_NAME) {
654         major = gssEapDuplicateName(minor, src->name, &dst->name);
655         if (GSS_ERROR(major))
656             goto cleanup;
657     }
658
659     if (src->target != GSS_C_NO_NAME) {
660         major = gssEapDuplicateName(minor, src->target, &dst->target);
661         if (GSS_ERROR(major))
662             goto cleanup;
663     }
664
665     if (src->password.value != NULL) {
666         major = duplicateBuffer(minor, &src->password, &dst->password);
667         if (GSS_ERROR(major))
668             goto cleanup;
669     }
670
671     major = duplicateOidSet(minor, src->mechanisms, &dst->mechanisms);
672     if (GSS_ERROR(major))
673         goto cleanup;
674
675     dst->expiryTime = src->expiryTime;
676
677     if (src->radiusConfigFile.value != NULL)
678         duplicateBufferOrCleanup(&src->radiusConfigFile, &dst->radiusConfigFile);
679     if (src->radiusConfigStanza.value != NULL)
680         duplicateBufferOrCleanup(&src->radiusConfigStanza, &dst->radiusConfigStanza);
681     if (src->caCertificate.value != NULL)
682         duplicateBufferOrCleanup(&src->caCertificate, &dst->caCertificate);
683     if (src->subjectNameConstraint.value != NULL)
684         duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint);
685     if (src->subjectAltNameConstraint.value != NULL)
686         duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint);
687     if (src->clientCertificate.value != NULL)
688         duplicateBufferOrCleanup(&src->clientCertificate, &dst->clientCertificate);
689     if (src->privateKey.value != NULL)
690         duplicateBufferOrCleanup(&src->privateKey, &dst->privateKey);
691
692 #ifdef GSSEAP_ENABLE_REAUTH
693     /* XXX krbCredCache, reauthCred */
694 #endif
695
696     *pDst = dst;
697     dst = GSS_C_NO_CREDENTIAL;
698
699     major = GSS_S_COMPLETE;
700     *minor = 0;
701
702 cleanup:
703     gssEapReleaseCred(&tmpMinor, &dst);
704
705     return major;
706 }
707
708 static OM_uint32
709 staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
710 {
711     OM_uint32 major, tmpMinor;
712     gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
713     gss_name_t defaultIdentityName = GSS_C_NO_NAME;
714     gss_buffer_desc defaultPassword = GSS_C_EMPTY_BUFFER;
715     int isDefaultIdentity = FALSE;
716
717     major = readStaticIdentityFile(minor, &defaultIdentity, &defaultPassword);
718     if (GSS_ERROR(major))
719         goto cleanup;
720
721     major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
722                              gssEapPrimaryMechForCred(cred), &defaultIdentityName);
723     if (GSS_ERROR(major))
724         goto cleanup;
725
726     if (defaultIdentityName == GSS_C_NO_NAME) {
727         if (cred->name == GSS_C_NO_NAME) {
728             major = GSS_S_CRED_UNAVAIL;
729             *minor = GSSEAP_NO_DEFAULT_IDENTITY;
730             goto cleanup;
731         }
732     } else {
733         if (cred->name == GSS_C_NO_NAME) {
734             cred->name = defaultIdentityName;
735             defaultIdentityName = GSS_C_NO_NAME;
736             isDefaultIdentity = TRUE;
737         } else {
738             major = gssEapCompareName(minor, cred->name,
739                                       defaultIdentityName, 0,
740                                       &isDefaultIdentity);
741             if (GSS_ERROR(major))
742                 goto cleanup;
743         }
744     }
745
746     if (isDefaultIdentity &&
747         (cred->flags & CRED_FLAG_PASSWORD) == 0) {
748         major = gssEapSetCredPassword(minor, cred, &defaultPassword);
749         if (GSS_ERROR(major))
750             goto cleanup;
751     }
752
753 cleanup:
754     gssEapReleaseName(&tmpMinor, &defaultIdentityName);
755     zeroAndReleasePassword(&defaultPassword);
756     gss_release_buffer(&tmpMinor, &defaultIdentity);
757
758     return major;
759 }
760
761 OM_uint32
762 gssEapResolveInitiatorCred(OM_uint32 *minor,
763                            const gss_cred_id_t cred,
764                            const gss_name_t targetName
765 #ifndef HAVE_MOONSHOT_GET_IDENTITY
766                                                        GSSEAP_UNUSED
767 #endif
768                            ,
769                            gss_cred_id_t *pResolvedCred)
770 {
771     OM_uint32 major, tmpMinor;
772     gss_cred_id_t resolvedCred = GSS_C_NO_CREDENTIAL;
773
774     if (cred == GSS_C_NO_CREDENTIAL) {
775         major = gssEapAcquireCred(minor,
776                                   GSS_C_NO_NAME,
777                                   GSS_C_INDEFINITE,
778                                   GSS_C_NO_OID_SET,
779                                   GSS_C_INITIATE,
780                                   &resolvedCred,
781                                   NULL,
782                                   NULL);
783         if (GSS_ERROR(major))
784             goto cleanup;
785     } else {
786         if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
787             major = GSS_S_NO_CRED;
788             *minor = GSSEAP_CRED_USAGE_MISMATCH;
789             goto cleanup;
790         }
791
792         major = gssEapDuplicateCred(minor, cred, &resolvedCred);
793         if (GSS_ERROR(major))
794             goto cleanup;
795     }
796
797     if ((resolvedCred->flags & CRED_FLAG_RESOLVED) == 0) {
798 #ifdef HAVE_MOONSHOT_GET_IDENTITY
799         major = libMoonshotResolveInitiatorCred(minor, resolvedCred, targetName);
800         if (major == GSS_S_CRED_UNAVAIL)
801 #endif
802             major = staticIdentityFileResolveInitiatorCred(minor, resolvedCred);
803         if (GSS_ERROR(major) && major != GSS_S_CRED_UNAVAIL)
804             goto cleanup;
805
806         /* If we have a caller-supplied password, the credential is resolved. */
807         if ((resolvedCred->flags &
808              (CRED_FLAG_PASSWORD | CRED_FLAG_CERTIFICATE)) == 0) {
809             major = GSS_S_CRED_UNAVAIL;
810             *minor = GSSEAP_NO_DEFAULT_CRED;
811             goto cleanup;
812         }
813
814         resolvedCred->flags |= CRED_FLAG_RESOLVED;
815     }
816
817     *pResolvedCred = resolvedCred;
818     resolvedCred = GSS_C_NO_CREDENTIAL;
819
820     major = GSS_S_COMPLETE;
821     *minor = 0;
822
823 cleanup:
824     gssEapReleaseCred(&tmpMinor, &resolvedCred);
825
826     return major;
827 }