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