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