remove ~/.gss_eap_id PKCS#12 directive for now
[moonshot.git] / moonshot / 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 nameMech = GSS_C_NO_OID;
247
248     if (cred->mechanisms != GSS_C_NO_OID_SET &&
249         cred->mechanisms->count == 1)
250         nameMech = &cred->mechanisms->elements[0];
251
252     return nameMech;
253 }
254
255 OM_uint32
256 gssEapAcquireCred(OM_uint32 *minor,
257                   const gss_name_t desiredName,
258                   OM_uint32 timeReq GSSEAP_UNUSED,
259                   const gss_OID_set desiredMechs,
260                   int credUsage,
261                   gss_cred_id_t *pCred,
262                   gss_OID_set *pActualMechs,
263                   OM_uint32 *timeRec)
264 {
265     OM_uint32 major, tmpMinor;
266     gss_cred_id_t cred;
267
268     /* XXX TODO validate with changed set_cred_option API */
269     *pCred = GSS_C_NO_CREDENTIAL;
270
271     major = gssEapAllocCred(minor, &cred);
272     if (GSS_ERROR(major))
273         goto cleanup;
274
275     switch (credUsage) {
276     case GSS_C_BOTH:
277         cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT;
278         break;
279     case GSS_C_INITIATE:
280         cred->flags |= CRED_FLAG_INITIATE;
281         break;
282     case GSS_C_ACCEPT:
283         cred->flags |= CRED_FLAG_ACCEPT;
284         break;
285     default:
286         major = GSS_S_FAILURE;
287         *minor = GSSEAP_BAD_USAGE;
288         goto cleanup;
289         break;
290     }
291
292     major = gssEapValidateMechs(minor, desiredMechs);
293     if (GSS_ERROR(major))
294         goto cleanup;
295
296     major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms);
297     if (GSS_ERROR(major))
298         goto cleanup;
299
300     if (desiredName != GSS_C_NO_NAME) {
301         GSSEAP_MUTEX_LOCK(&desiredName->mutex);
302
303         major = gssEapDuplicateName(minor, desiredName, &cred->name);
304         if (GSS_ERROR(major)) {
305             GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
306             goto cleanup;
307         }
308
309         GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
310     }
311
312 #ifdef GSSEAP_ENABLE_ACCEPTOR
313     if (cred->flags & CRED_FLAG_ACCEPT) {
314         struct rs_context *radContext;
315
316         major = gssEapCreateRadiusContext(minor, cred, &radContext);
317         if (GSS_ERROR(major))
318             goto cleanup;
319
320         rs_context_destroy(radContext);
321     }
322 #endif
323
324     if (pActualMechs != NULL) {
325         major = duplicateOidSet(minor, cred->mechanisms, pActualMechs);
326         if (GSS_ERROR(major))
327             goto cleanup;
328     }
329
330     if (timeRec != NULL)
331         *timeRec = GSS_C_INDEFINITE;
332
333     *pCred = cred;
334
335     major = GSS_S_COMPLETE;
336     *minor = 0;
337
338 cleanup:
339     if (GSS_ERROR(major))
340         gssEapReleaseCred(&tmpMinor, &cred);
341
342     return major;
343 }
344
345 /*
346  * Return TRUE if cred available for mechanism. Caller need no acquire
347  * lock because mechanisms list is immutable.
348  */
349 int
350 gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
351 {
352     OM_uint32 minor;
353     int present = 0;
354
355     GSSEAP_ASSERT(mech != GSS_C_NO_OID);
356
357     if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
358         return TRUE;
359
360     gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present);
361
362     return present;
363 }
364
365 static OM_uint32
366 staticIdentityFileResolveDefaultIdentity(OM_uint32 *minor,
367                                          const gss_cred_id_t cred,
368                                          gss_name_t *pName)
369 {
370     OM_uint32 major, tmpMinor;
371     gss_OID nameMech = gssEapPrimaryMechForCred(cred);
372     gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
373
374     *pName = GSS_C_NO_NAME;
375
376     major = readStaticIdentityFile(minor, &defaultIdentity, GSS_C_NO_BUFFER);
377     if (major == GSS_S_COMPLETE) {
378         major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
379                                  nameMech, pName);
380     }
381
382     gss_release_buffer(&tmpMinor, &defaultIdentity);
383
384     return major;
385 }
386
387 static OM_uint32
388 gssEapResolveCredIdentity(OM_uint32 *minor,
389                           gss_cred_id_t cred)
390 {
391     OM_uint32 major;
392     gss_OID nameMech = gssEapPrimaryMechForCred(cred);
393
394     if (cred->name != GSS_C_NO_NAME) {
395         *minor = 0;
396         return GSS_S_COMPLETE;
397     }
398
399     if (cred->flags & CRED_FLAG_ACCEPT) {
400         gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
401         char serviceName[5 + MAXHOSTNAMELEN];
402
403         /* default host-based service is host@localhost */
404         memcpy(serviceName, "host@", 5);
405         if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
406             *minor = GSSEAP_NO_HOSTNAME;
407             return GSS_S_FAILURE;
408         }
409
410         nameBuf.value = serviceName;
411         nameBuf.length = strlen((char *)nameBuf.value);
412
413         major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
414                                  nameMech, &cred->name);
415         if (GSS_ERROR(major))
416             return major;
417     } else if (cred->flags & CRED_FLAG_INITIATE) {
418 #ifdef HAVE_MOONSHOT_GET_IDENTITY
419         major = libMoonshotResolveDefaultIdentity(minor, cred, &cred->name);
420         if (major == GSS_S_CRED_UNAVAIL)
421 #endif
422             major = staticIdentityFileResolveDefaultIdentity(minor, cred, &cred->name);
423         if (major != GSS_S_CRED_UNAVAIL)
424             return major;
425     }
426
427     *minor = 0;
428     return GSS_S_COMPLETE;
429 }
430
431 OM_uint32
432 gssEapInquireCred(OM_uint32 *minor,
433                   gss_cred_id_t cred,
434                   gss_name_t *name,
435                   OM_uint32 *pLifetime,
436                   gss_cred_usage_t *cred_usage,
437                   gss_OID_set *mechanisms)
438 {
439     OM_uint32 major;
440     time_t now, lifetime;
441
442     if (name != NULL) {
443         major = gssEapResolveCredIdentity(minor, cred);
444         if (GSS_ERROR(major))
445             goto cleanup;
446
447         if (cred->name != GSS_C_NO_NAME) {
448             major = gssEapDuplicateName(minor, cred->name, name);
449             if (GSS_ERROR(major))
450                 goto cleanup;
451         } else
452             *name = GSS_C_NO_NAME;
453     }
454
455     if (cred_usage != NULL) {
456         OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT));
457
458         switch (flags) {
459         case CRED_FLAG_INITIATE:
460             *cred_usage = GSS_C_INITIATE;
461             break;
462         case CRED_FLAG_ACCEPT:
463             *cred_usage = GSS_C_ACCEPT;
464             break;
465         default:
466             *cred_usage = GSS_C_BOTH;
467             break;
468         }
469     }
470
471     if (mechanisms != NULL) {
472         if (cred->mechanisms != GSS_C_NO_OID_SET)
473             major = duplicateOidSet(minor, cred->mechanisms, mechanisms);
474         else
475             major = gssEapIndicateMechs(minor, mechanisms);
476         if (GSS_ERROR(major))
477             goto cleanup;
478     }
479
480     if (cred->expiryTime == 0) {
481         lifetime = GSS_C_INDEFINITE;
482     } else  {
483         now = time(NULL);
484         lifetime = now - cred->expiryTime;
485         if (lifetime < 0)
486             lifetime = 0;
487     }
488
489     if (pLifetime != NULL) {
490         *pLifetime = lifetime;
491     }
492
493     if (lifetime == 0) {
494         major = GSS_S_CREDENTIALS_EXPIRED;
495         *minor = GSSEAP_CRED_EXPIRED;
496         goto cleanup;
497     }
498
499     major = GSS_S_COMPLETE;
500     *minor = 0;
501
502 cleanup:
503     return major;
504 }
505
506 OM_uint32
507 gssEapSetCredPassword(OM_uint32 *minor,
508                       gss_cred_id_t cred,
509                       const gss_buffer_t password)
510 {
511     OM_uint32 major, tmpMinor;
512     gss_buffer_desc newPassword = GSS_C_EMPTY_BUFFER;
513
514     if (cred->flags & CRED_FLAG_RESOLVED) {
515         major = GSS_S_FAILURE;
516         *minor = GSSEAP_CRED_RESOLVED;
517         goto cleanup;
518     }
519
520     if (password != GSS_C_NO_BUFFER) {
521         major = duplicateBuffer(minor, password, &newPassword);
522         if (GSS_ERROR(major))
523             goto cleanup;
524
525         cred->flags |= CRED_FLAG_PASSWORD;
526     } else {
527         cred->flags &= ~(CRED_FLAG_PASSWORD);
528     }
529
530     gss_release_buffer(&tmpMinor, &cred->password);
531     cred->password = newPassword;
532
533     major = GSS_S_COMPLETE;
534     *minor = 0;
535
536 cleanup:
537     return major;
538 }
539
540 /*
541  * Currently only the privateKey path is exposed to the application
542  * (via gss_set_cred_option() or the third line in ~/.gss_eap_id).
543  * At some point in the future we may add support for setting the
544  * client certificate separately.
545  */
546 OM_uint32
547 gssEapSetCredClientCertificate(OM_uint32 *minor,
548                               gss_cred_id_t cred,
549                               const gss_buffer_t clientCert,
550                               const gss_buffer_t privateKey)
551 {
552     OM_uint32 major, tmpMinor;
553     gss_buffer_desc newClientCert = GSS_C_EMPTY_BUFFER;
554     gss_buffer_desc newPrivateKey = GSS_C_EMPTY_BUFFER;
555
556     if (cred->flags & CRED_FLAG_RESOLVED) {
557         major = GSS_S_FAILURE;
558         *minor = GSSEAP_CRED_RESOLVED;
559         goto cleanup;
560     }
561
562     if (clientCert == GSS_C_NO_BUFFER &&
563         privateKey == GSS_C_NO_BUFFER) {
564         cred->flags &= ~(CRED_FLAG_CERTIFICATE);
565         major = GSS_S_COMPLETE;
566         *minor = 0;
567         goto cleanup;
568     }
569
570     if (clientCert != GSS_C_NO_BUFFER) {
571         major = duplicateBuffer(minor, clientCert, &newClientCert);
572         if (GSS_ERROR(major))
573             goto cleanup;
574     }
575
576     if (privateKey != GSS_C_NO_BUFFER) {
577         major = duplicateBuffer(minor, privateKey, &newPrivateKey);
578         if (GSS_ERROR(major))
579             goto cleanup;
580     }
581
582     cred->flags |= CRED_FLAG_CERTIFICATE;
583
584     gss_release_buffer(&tmpMinor, &cred->clientCertificate);
585     cred->clientCertificate = newClientCert;
586
587     gss_release_buffer(&tmpMinor, &cred->privateKey);
588     cred->privateKey = newPrivateKey;
589
590     major = GSS_S_COMPLETE;
591     *minor = 0;
592
593 cleanup:
594     if (GSS_ERROR(major)) {
595         gss_release_buffer(&tmpMinor, &newClientCert);
596         gss_release_buffer(&tmpMinor, &newPrivateKey);
597     }
598
599     return major;
600 }
601
602 OM_uint32
603 gssEapSetCredService(OM_uint32 *minor,
604                      gss_cred_id_t cred,
605                      const gss_name_t target)
606 {
607     OM_uint32 major, tmpMinor;
608     gss_name_t newTarget = GSS_C_NO_NAME;
609
610     if (cred->flags & CRED_FLAG_RESOLVED) {
611         major = GSS_S_FAILURE;
612         *minor = GSSEAP_CRED_RESOLVED;
613         goto cleanup;
614     }
615
616     if (target != GSS_C_NO_NAME) {
617         major = gssEapDuplicateName(minor, target, &newTarget);
618         if (GSS_ERROR(major))
619             goto cleanup;
620
621         cred->flags |= CRED_FLAG_TARGET;
622     } else {
623         cred->flags &= ~(CRED_FLAG_TARGET);
624     }
625
626     gssEapReleaseName(&tmpMinor, &cred->target);
627     cred->target = newTarget;
628
629     major = GSS_S_COMPLETE;
630     *minor = 0;
631
632 cleanup:
633     return major;
634 }
635
636 static OM_uint32
637 gssEapDuplicateCred(OM_uint32 *minor,
638                     const gss_cred_id_t src,
639                     gss_cred_id_t *pDst)
640 {
641     OM_uint32 major, tmpMinor;
642     gss_cred_id_t dst = GSS_C_NO_CREDENTIAL;
643
644     *pDst = GSS_C_NO_CREDENTIAL;
645
646     major = gssEapAllocCred(minor, &dst);
647     if (GSS_ERROR(major))
648         goto cleanup;
649
650     dst->flags = src->flags;
651
652     if (src->name != GSS_C_NO_NAME) {
653         major = gssEapDuplicateName(minor, src->name, &dst->name);
654         if (GSS_ERROR(major))
655             goto cleanup;
656     }
657
658     if (src->target != GSS_C_NO_NAME) {
659         major = gssEapDuplicateName(minor, src->target, &dst->target);
660         if (GSS_ERROR(major))
661             goto cleanup;
662     }
663
664     if (src->password.value != NULL) {
665         major = duplicateBuffer(minor, &src->password, &dst->password);
666         if (GSS_ERROR(major))
667             goto cleanup;
668     }
669
670     major = duplicateOidSet(minor, src->mechanisms, &dst->mechanisms);
671     if (GSS_ERROR(major))
672         goto cleanup;
673
674     dst->expiryTime = src->expiryTime;
675
676     if (src->radiusConfigFile.value != NULL)
677         duplicateBufferOrCleanup(&src->radiusConfigFile, &dst->radiusConfigFile);
678     if (src->radiusConfigStanza.value != NULL)
679         duplicateBufferOrCleanup(&src->radiusConfigStanza, &dst->radiusConfigStanza);
680     if (src->caCertificate.value != NULL)
681         duplicateBufferOrCleanup(&src->caCertificate, &dst->caCertificate);
682     if (src->subjectNameConstraint.value != NULL)
683         duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint);
684     if (src->subjectAltNameConstraint.value != NULL)
685         duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint);
686     if (src->clientCertificate.value != NULL)
687         duplicateBufferOrCleanup(&src->clientCertificate, &dst->clientCertificate);
688     if (src->privateKey.value != NULL)
689         duplicateBufferOrCleanup(&src->privateKey, &dst->privateKey);
690
691 #ifdef GSSEAP_ENABLE_REAUTH
692     /* XXX krbCredCache, reauthCred */
693 #endif
694
695     *pDst = dst;
696     dst = GSS_C_NO_CREDENTIAL;
697
698     major = GSS_S_COMPLETE;
699     *minor = 0;
700
701 cleanup:
702     gssEapReleaseCred(&tmpMinor, &dst);
703
704     return major;
705 }
706
707 static OM_uint32
708 staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
709 {
710     OM_uint32 major, tmpMinor;
711     gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
712     gss_name_t defaultIdentityName = GSS_C_NO_NAME;
713     gss_buffer_desc defaultPassword = GSS_C_EMPTY_BUFFER;
714     int isDefaultIdentity = FALSE;
715
716     major = readStaticIdentityFile(minor, &defaultIdentity, &defaultPassword);
717     if (GSS_ERROR(major))
718         goto cleanup;
719
720     major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
721                              gssEapPrimaryMechForCred(cred), &defaultIdentityName);
722     if (GSS_ERROR(major))
723         goto cleanup;
724
725     if (defaultIdentityName == GSS_C_NO_NAME) {
726         if (cred->name == GSS_C_NO_NAME) {
727             major = GSS_S_CRED_UNAVAIL;
728             *minor = GSSEAP_NO_DEFAULT_IDENTITY;
729             goto cleanup;
730         }
731     } else {
732         if (cred->name == GSS_C_NO_NAME) {
733             cred->name = defaultIdentityName;
734             defaultIdentityName = GSS_C_NO_NAME;
735             isDefaultIdentity = TRUE;
736         } else {
737             major = gssEapCompareName(minor, cred->name,
738                                       defaultIdentityName, &isDefaultIdentity);
739             if (GSS_ERROR(major))
740                 goto cleanup;
741         }
742     }
743
744     if (isDefaultIdentity &&
745         (cred->flags & CRED_FLAG_PASSWORD) == 0) {
746         major = gssEapSetCredPassword(minor, cred, &defaultPassword);
747         if (GSS_ERROR(major))
748             goto cleanup;
749     }
750
751 cleanup:
752     gssEapReleaseName(&tmpMinor, &defaultIdentityName);
753     zeroAndReleasePassword(&defaultPassword);
754     gss_release_buffer(&tmpMinor, &defaultIdentity);
755
756     return major;
757 }
758
759 OM_uint32
760 gssEapResolveInitiatorCred(OM_uint32 *minor,
761                            const gss_cred_id_t cred,
762                            const gss_name_t targetName
763 #ifndef HAVE_MOONSHOT_GET_IDENTITY
764                                                        GSSEAP_UNUSED
765 #endif
766                            ,
767                            gss_cred_id_t *pResolvedCred)
768 {
769     OM_uint32 major, tmpMinor;
770     gss_cred_id_t resolvedCred = GSS_C_NO_CREDENTIAL;
771
772     if (cred == GSS_C_NO_CREDENTIAL) {
773         major = gssEapAcquireCred(minor,
774                                   GSS_C_NO_NAME,
775                                   GSS_C_INDEFINITE,
776                                   GSS_C_NO_OID_SET,
777                                   GSS_C_INITIATE,
778                                   &resolvedCred,
779                                   NULL,
780                                   NULL);
781         if (GSS_ERROR(major))
782             goto cleanup;
783     } else {
784         if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
785             major = GSS_S_NO_CRED;
786             *minor = GSSEAP_CRED_USAGE_MISMATCH;
787             goto cleanup;
788         }
789
790         major = gssEapDuplicateCred(minor, cred, &resolvedCred);
791         if (GSS_ERROR(major))
792             goto cleanup;
793     }
794
795     if ((resolvedCred->flags & CRED_FLAG_RESOLVED) == 0) {
796 #ifdef HAVE_MOONSHOT_GET_IDENTITY
797         major = libMoonshotResolveInitiatorCred(minor, resolvedCred, targetName);
798         if (major == GSS_S_CRED_UNAVAIL)
799 #endif
800             major = staticIdentityFileResolveInitiatorCred(minor, resolvedCred);
801         if (GSS_ERROR(major) && major != GSS_S_CRED_UNAVAIL)
802             goto cleanup;
803
804         /* If we have a caller-supplied password, the credential is resolved. */
805         if ((resolvedCred->flags &
806              (CRED_FLAG_PASSWORD | CRED_FLAG_CERTIFICATE)) == 0) {
807             major = GSS_S_CRED_UNAVAIL;
808             *minor = GSSEAP_NO_DEFAULT_CRED;
809             goto cleanup;
810         }
811
812         resolvedCred->flags |= CRED_FLAG_RESOLVED;
813     }
814
815     *pResolvedCred = resolvedCred;
816     resolvedCred = GSS_C_NO_CREDENTIAL;
817
818     major = GSS_S_COMPLETE;
819     *minor = 0;
820
821 cleanup:
822     gssEapReleaseCred(&tmpMinor, &resolvedCred);
823
824     return major;
825 }