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