Fixes for Heimdal (macOS) builds from Stefan.
[mech_eap.git] / mech_eap / util_cred.c
index c468f97..707e029 100644 (file)
 
 #include "gssapiP_eap.h"
 
-#include <pwd.h>
-#include <stdio.h> // for BUFSIZ
-
+#ifdef WIN32
+# include <shlobj.h>     /* may need to use ShFolder.h instead */
+# include <stdio.h>
+#else
+# include <pwd.h>
+#endif
 
 OM_uint32
 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
@@ -55,7 +58,7 @@ gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
     }
 
     if (GSSEAP_MUTEX_INIT(&cred->mutex) != 0) {
-        *minor = errno;
+        *minor = GSSEAP_GET_LAST_ERROR();
         gssEapReleaseCred(&tmpMinor, &cred);
         return GSS_S_FAILURE;
     }
@@ -66,6 +69,20 @@ gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
     return GSS_S_COMPLETE;
 }
 
+static void
+zeroAndReleasePassword(gss_buffer_t password)
+{
+    GSSEAP_ASSERT(password != GSS_C_NO_BUFFER);
+
+    if (password->value != NULL) {
+        memset(password->value, 0, password->length);
+        GSSEAP_FREE(password->value);
+    }
+
+    password->value = NULL;
+    password->length = 0;
+}
+
 OM_uint32
 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
 {
@@ -80,16 +97,18 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
     GSSEAP_KRB_INIT(&krbContext);
 
     gssEapReleaseName(&tmpMinor, &cred->name);
+    gssEapReleaseName(&tmpMinor, &cred->target);
 
-    if (cred->password.value != NULL) {
-        memset(cred->password.value, 0, cred->password.length);
-        GSSEAP_FREE(cred->password.value);
-    }
+    zeroAndReleasePassword(&cred->password);
 
-    if (cred->radiusConfigFile != NULL)
-        GSSEAP_FREE(cred->radiusConfigFile);
-    if (cred->radiusConfigStanza != NULL)
-        GSSEAP_FREE(cred->radiusConfigStanza);
+    gss_release_buffer(&tmpMinor, &cred->radiusConfigFile);
+    gss_release_buffer(&tmpMinor, &cred->radiusConfigStanza);
+    gss_release_buffer(&tmpMinor, &cred->caCertificate);
+    gss_release_buffer(&tmpMinor, &cred->caCertificateBlob);
+    gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
+    gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
+    gss_release_buffer(&tmpMinor, &cred->clientCertificate);
+    gss_release_buffer(&tmpMinor, &cred->privateKey);
 
 #ifdef GSSEAP_ENABLE_REAUTH
     if (cred->krbCredCache != NULL) {
@@ -112,32 +131,54 @@ gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
 }
 
 static OM_uint32
-readDefaultIdentityAndCreds(OM_uint32 *minor,
-                            gss_buffer_t defaultIdentity,
-                            gss_buffer_t defaultCreds)
+readStaticIdentityFile(OM_uint32 *minor,
+                       gss_buffer_t defaultIdentity,
+                       gss_buffer_t defaultPassword)
 {
     OM_uint32 major, tmpMinor;
     FILE *fp = NULL;
-    char pwbuf[BUFSIZ], buf[BUFSIZ];
+    char buf[BUFSIZ];
     char *ccacheName;
+    int i = 0;
+#ifndef WIN32
     struct passwd *pw = NULL, pwd;
+    char pwbuf[BUFSIZ];
+#endif
 
     defaultIdentity->length = 0;
     defaultIdentity->value = NULL;
 
-    defaultCreds->length = 0;
-    defaultCreds->value = NULL;
+    if (defaultPassword != GSS_C_NO_BUFFER) {
+        defaultPassword->length = 0;
+        defaultPassword->value = NULL;
+    }
 
     ccacheName = getenv("GSSEAP_IDENTITY");
     if (ccacheName == NULL) {
+#ifdef WIN32
+        TCHAR szPath[MAX_PATH];
+
+        if (!SUCCEEDED(SHGetFolderPath(NULL,
+                                       CSIDL_APPDATA, /* |CSIDL_FLAG_CREATE */
+                                       NULL, /* User access token */
+                                       0,    /* SHGFP_TYPE_CURRENT */
+                                       szPath))) {
+            major = GSS_S_CRED_UNAVAIL;
+            *minor = GSSEAP_GET_LAST_ERROR(); /* XXX */
+            goto cleanup;
+        }
+
+        snprintf(buf, sizeof(buf), "%s/.gss_eap_id", szPath);
+#else
         if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 ||
             pw == NULL || pw->pw_dir == NULL) {
             major = GSS_S_CRED_UNAVAIL;
-            *minor = errno;
+            *minor = GSSEAP_GET_LAST_ERROR();
             goto cleanup;
         }
 
         snprintf(buf, sizeof(buf), "%s/.gss_eap_id", pw->pw_dir);
+#endif /* WIN32 */
         ccacheName = buf;
     }
 
@@ -163,16 +204,20 @@ readDefaultIdentityAndCreds(OM_uint32 *minor,
                 break;
         }
 
-        if (defaultIdentity->value == NULL)
+        if (i == 0)
             dst = defaultIdentity;
-        else if (defaultCreds->value == NULL)
-            dst = defaultCreds;
+        else if (i == 1)
+            dst = defaultPassword;
         else
             break;
 
-        major = duplicateBuffer(minor, &src, dst);
-        if (GSS_ERROR(major))
-            goto cleanup;
+        if (dst != GSS_C_NO_BUFFER) {
+            major = duplicateBuffer(minor, &src, dst);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        }
+
+        i++;
     }
 
     if (defaultIdentity->length == 0) {
@@ -190,16 +235,30 @@ cleanup:
 
     if (GSS_ERROR(major)) {
         gss_release_buffer(&tmpMinor, defaultIdentity);
-        gss_release_buffer(&tmpMinor, defaultCreds);
+        zeroAndReleasePassword(defaultPassword);
     }
 
+    memset(buf, 0, sizeof(buf));
+
     return major;
 }
 
+gss_OID
+gssEapPrimaryMechForCred(gss_cred_id_t cred)
+{
+    gss_OID credMech = GSS_C_NO_OID;
+
+    if (cred != GSS_C_NO_CREDENTIAL &&
+        cred->mechanisms != GSS_C_NO_OID_SET &&
+        cred->mechanisms->count == 1)
+        credMech = &cred->mechanisms->elements[0];
+
+    return credMech;
+}
+
 OM_uint32
 gssEapAcquireCred(OM_uint32 *minor,
-                  const gss_name_t desiredName,
-                  const gss_buffer_t password,
+                  gss_const_name_t desiredName,
                   OM_uint32 timeReq GSSEAP_UNUSED,
                   const gss_OID_set desiredMechs,
                   int credUsage,
@@ -209,10 +268,6 @@ gssEapAcquireCred(OM_uint32 *minor,
 {
     OM_uint32 major, tmpMinor;
     gss_cred_id_t cred;
-    gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
-    gss_name_t defaultIdentityName = GSS_C_NO_NAME;
-    gss_buffer_desc defaultCreds = GSS_C_EMPTY_BUFFER;
-    gss_OID nameMech = GSS_C_NO_OID;
 
     /* XXX TODO validate with changed set_cred_option API */
     *pCred = GSS_C_NO_CREDENTIAL;
@@ -246,104 +301,29 @@ gssEapAcquireCred(OM_uint32 *minor,
     if (GSS_ERROR(major))
         goto cleanup;
 
-    if (cred->mechanisms != GSS_C_NO_OID_SET &&
-        cred->mechanisms->count == 1)
-        nameMech = &cred->mechanisms->elements[0];
-
-    if (cred->flags & CRED_FLAG_INITIATE) {
-        major = readDefaultIdentityAndCreds(minor, &defaultIdentity, &defaultCreds);
-        if (major == GSS_S_COMPLETE) {
-            major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
-                                     nameMech, &defaultIdentityName);
-            if (GSS_ERROR(major))
-                goto cleanup;
-        } else if (major != GSS_S_CRED_UNAVAIL)
-            goto cleanup;
-    }
-
     if (desiredName != GSS_C_NO_NAME) {
-        GSSEAP_MUTEX_LOCK(&desiredName->mutex);
+        GSSEAP_MUTEX_LOCK(&((gss_name_t)desiredName)->mutex);
 
         major = gssEapDuplicateName(minor, desiredName, &cred->name);
         if (GSS_ERROR(major)) {
-            GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
+            GSSEAP_MUTEX_UNLOCK(&((gss_name_t)desiredName)->mutex);
             goto cleanup;
         }
 
-        GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
-
-        if (defaultIdentityName != GSS_C_NO_NAME) {
-            int nameEqual;
-
-            major = gssEapCompareName(minor, desiredName,
-                                      defaultIdentityName, &nameEqual);
-            if (GSS_ERROR(major))
-                goto cleanup;
-            else if (nameEqual)
-                cred->flags |= CRED_FLAG_DEFAULT_IDENTITY;
-        }
-    } else {
-        if (cred->flags & CRED_FLAG_ACCEPT) {
-            gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
-            char serviceName[5 + MAXHOSTNAMELEN];
-
-            /* default host-based service is host@localhost */
-            memcpy(serviceName, "host@", 5);
-            if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
-                major = GSS_S_FAILURE;
-                *minor = GSSEAP_NO_HOSTNAME;
-                goto cleanup;
-            }
-
-            nameBuf.value = serviceName;
-            nameBuf.length = strlen((char *)nameBuf.value);
-
-            major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
-                                     nameMech, &cred->name);
-            if (GSS_ERROR(major))
-                goto cleanup;
-        } else if (cred->flags & CRED_FLAG_INITIATE) {
-            if (defaultIdentityName == GSS_C_NO_NAME) {
-                major = GSS_S_CRED_UNAVAIL;
-                *minor = GSSEAP_NO_DEFAULT_IDENTITY;
-                goto cleanup;
-            }
-
-            cred->name = defaultIdentityName;
-            defaultIdentityName = GSS_C_NO_NAME;
-        }
-        cred->flags |= CRED_FLAG_DEFAULT_IDENTITY;
+        GSSEAP_MUTEX_UNLOCK(&((gss_name_t)desiredName)->mutex);
     }
 
-    assert(cred->name != GSS_C_NO_NAME);
+#ifdef GSSEAP_ENABLE_ACCEPTOR
+    if (cred->flags & CRED_FLAG_ACCEPT) {
+        struct rs_context *radContext;
 
-    if (password != GSS_C_NO_BUFFER) {
-        major = duplicateBuffer(minor, password, &cred->password);
+        major = gssEapCreateRadiusContext(minor, cred, &radContext);
         if (GSS_ERROR(major))
             goto cleanup;
 
-        cred->flags |= CRED_FLAG_PASSWORD;
-    } else if (defaultCreds.value != NULL &&
-        (cred->flags & CRED_FLAG_DEFAULT_IDENTITY)) {
-        cred->password = defaultCreds;
-
-        defaultCreds.length = 0;
-        defaultCreds.value = NULL;
-
-        cred->flags |= CRED_FLAG_PASSWORD;
-    } else if (cred->flags & CRED_FLAG_INITIATE) {
-        /*
-         * OK, here we need to ask the supplicant if we have creds or it
-         * will acquire them, so GS2 can know whether to prompt for a
-         * password or not.
-         */
-#if 0
-        && !gssEapCanReauthP(cred, GSS_C_NO_NAME, timeReq)
-#endif
-        major = GSS_S_CRED_UNAVAIL;
-        *minor = GSSEAP_NO_DEFAULT_CRED;
-        goto cleanup;
+        rs_context_destroy(radContext);
     }
+#endif
 
     if (pActualMechs != NULL) {
         major = duplicateOidSet(minor, cred->mechanisms, pActualMechs);
@@ -362,12 +342,8 @@ gssEapAcquireCred(OM_uint32 *minor,
 cleanup:
     if (GSS_ERROR(major))
         gssEapReleaseCred(&tmpMinor, &cred);
-    gssEapReleaseName(&tmpMinor, &defaultIdentityName);
-    gss_release_buffer(&tmpMinor, &defaultIdentity);
-    if (defaultCreds.value != NULL) {
-        memset(defaultCreds.value, 0, defaultCreds.length);
-        gss_release_buffer(&tmpMinor, &defaultCreds);
-    }
+
+    gssEapTraceStatus("gss_acquire_cred", major, *minor);
 
     return major;
 }
@@ -377,12 +353,12 @@ cleanup:
  * lock because mechanisms list is immutable.
  */
 int
-gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
+gssEapCredAvailable(gss_const_cred_id_t cred, gss_OID mech)
 {
     OM_uint32 minor;
     int present = 0;
 
-    assert(mech != GSS_C_NO_OID);
+    GSSEAP_ASSERT(mech != GSS_C_NO_OID);
 
     if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
         return TRUE;
@@ -392,6 +368,72 @@ gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
     return present;
 }
 
+static OM_uint32
+staticIdentityFileResolveDefaultIdentity(OM_uint32 *minor,
+                                         const gss_cred_id_t cred,
+                                         gss_name_t *pName)
+{
+    OM_uint32 major, tmpMinor;
+    gss_OID nameMech = gssEapPrimaryMechForCred(cred);
+    gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
+
+    *pName = GSS_C_NO_NAME;
+
+    major = readStaticIdentityFile(minor, &defaultIdentity, GSS_C_NO_BUFFER);
+    if (major == GSS_S_COMPLETE) {
+        major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
+                                 nameMech, pName);
+    }
+
+    gss_release_buffer(&tmpMinor, &defaultIdentity);
+
+    return major;
+}
+
+static OM_uint32
+gssEapResolveCredIdentity(OM_uint32 *minor,
+                          gss_cred_id_t cred)
+{
+    OM_uint32 major;
+    gss_OID nameMech = gssEapPrimaryMechForCred(cred);
+
+    if (cred->name != GSS_C_NO_NAME) {
+        *minor = 0;
+        return GSS_S_COMPLETE;
+    }
+
+    if (cred->flags & CRED_FLAG_ACCEPT) {
+        gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
+        char serviceName[5 + MAXHOSTNAMELEN];
+
+        /* default host-based service is host@localhost */
+        memcpy(serviceName, "host@", 5);
+        if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
+            *minor = GSSEAP_NO_HOSTNAME;
+            return GSS_S_FAILURE;
+        }
+
+        nameBuf.value = serviceName;
+        nameBuf.length = strlen((char *)nameBuf.value);
+
+        major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
+                                 nameMech, &cred->name);
+        if (GSS_ERROR(major))
+            return major;
+    } else if (cred->flags & CRED_FLAG_INITIATE) {
+#ifdef HAVE_MOONSHOT_GET_IDENTITY
+        major = libMoonshotResolveDefaultIdentity(minor, cred, &cred->name);
+        if (major == GSS_S_CRED_UNAVAIL)
+#endif
+            major = staticIdentityFileResolveDefaultIdentity(minor, cred, &cred->name);
+        if (major != GSS_S_CRED_UNAVAIL)
+            return major;
+    }
+
+    *minor = 0;
+    return GSS_S_COMPLETE;
+}
+
 OM_uint32
 gssEapInquireCred(OM_uint32 *minor,
                   gss_cred_id_t cred,
@@ -404,9 +446,16 @@ gssEapInquireCred(OM_uint32 *minor,
     time_t now, lifetime;
 
     if (name != NULL) {
-        major = gssEapDuplicateName(minor, cred->name, name);
+        major = gssEapResolveCredIdentity(minor, cred);
         if (GSS_ERROR(major))
-            return major;
+            goto cleanup;
+
+        if (cred->name != GSS_C_NO_NAME) {
+            major = gssEapDuplicateName(minor, cred->name, name);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        } else
+            *name = GSS_C_NO_NAME;
     }
 
     if (cred_usage != NULL) {
@@ -431,7 +480,7 @@ gssEapInquireCred(OM_uint32 *minor,
         else
             major = gssEapIndicateMechs(minor, mechanisms);
         if (GSS_ERROR(major))
-            return major;
+            goto cleanup;
     }
 
     if (cred->expiryTime == 0) {
@@ -448,12 +497,338 @@ gssEapInquireCred(OM_uint32 *minor,
     }
 
     if (lifetime == 0) {
+        major = GSS_S_CREDENTIALS_EXPIRED;
         *minor = GSSEAP_CRED_EXPIRED;
-        return GSS_S_CREDENTIALS_EXPIRED;
+        goto cleanup;
+    }
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    return major;
+}
+
+OM_uint32
+gssEapSetCredPassword(OM_uint32 *minor,
+                      gss_cred_id_t cred,
+                      const gss_buffer_t password)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc newPassword = GSS_C_EMPTY_BUFFER;
+
+    if (cred->flags & CRED_FLAG_RESOLVED) {
+        major = GSS_S_FAILURE;
+        *minor = GSSEAP_CRED_RESOLVED;
+        goto cleanup;
     }
 
+    if (password != GSS_C_NO_BUFFER) {
+        major = duplicateBuffer(minor, password, &newPassword);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        cred->flags |= CRED_FLAG_PASSWORD;
+    } else {
+        cred->flags &= ~(CRED_FLAG_PASSWORD);
+    }
+
+    gss_release_buffer(&tmpMinor, &cred->password);
+    cred->password = newPassword;
+
     major = GSS_S_COMPLETE;
     *minor = 0;
 
+cleanup:
+    return major;
+}
+
+/*
+ * Currently only the privateKey path is exposed to the application
+ * (via gss_set_cred_option() or the third line in ~/.gss_eap_id).
+ * At some point in the future we may add support for setting the
+ * client certificate separately.
+ */
+OM_uint32
+gssEapSetCredClientCertificate(OM_uint32 *minor,
+                              gss_cred_id_t cred,
+                              const gss_buffer_t clientCert,
+                              const gss_buffer_t privateKey)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc newClientCert = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc newPrivateKey = GSS_C_EMPTY_BUFFER;
+
+    if (cred->flags & CRED_FLAG_RESOLVED) {
+        major = GSS_S_FAILURE;
+        *minor = GSSEAP_CRED_RESOLVED;
+        goto cleanup;
+    }
+
+    if (clientCert == GSS_C_NO_BUFFER &&
+        privateKey == GSS_C_NO_BUFFER) {
+        cred->flags &= ~(CRED_FLAG_CERTIFICATE);
+        major = GSS_S_COMPLETE;
+        *minor = 0;
+        goto cleanup;
+    }
+
+    if (clientCert != GSS_C_NO_BUFFER) {
+        major = duplicateBuffer(minor, clientCert, &newClientCert);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    if (privateKey != GSS_C_NO_BUFFER) {
+        major = duplicateBuffer(minor, privateKey, &newPrivateKey);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    cred->flags |= CRED_FLAG_CERTIFICATE;
+
+    gss_release_buffer(&tmpMinor, &cred->clientCertificate);
+    cred->clientCertificate = newClientCert;
+
+    gss_release_buffer(&tmpMinor, &cred->privateKey);
+    cred->privateKey = newPrivateKey;
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    if (GSS_ERROR(major)) {
+        gss_release_buffer(&tmpMinor, &newClientCert);
+        gss_release_buffer(&tmpMinor, &newPrivateKey);
+    }
+
+    return major;
+}
+
+OM_uint32
+gssEapSetCredService(OM_uint32 *minor,
+                     gss_cred_id_t cred,
+                     gss_const_name_t target)
+{
+    OM_uint32 major, tmpMinor;
+    gss_name_t newTarget = GSS_C_NO_NAME;
+
+    if (cred->flags & CRED_FLAG_RESOLVED) {
+        major = GSS_S_FAILURE;
+        *minor = GSSEAP_CRED_RESOLVED;
+        goto cleanup;
+    }
+
+    if (target != GSS_C_NO_NAME) {
+        major = gssEapDuplicateName(minor, target, &newTarget);
+        if (GSS_ERROR(major))
+            goto cleanup;
+
+        cred->flags |= CRED_FLAG_TARGET;
+    } else {
+        cred->flags &= ~(CRED_FLAG_TARGET);
+    }
+
+    gssEapReleaseName(&tmpMinor, &cred->target);
+    cred->target = newTarget;
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    return major;
+}
+
+static OM_uint32
+gssEapDuplicateCred(OM_uint32 *minor,
+                    const gss_cred_id_t src,
+                    gss_cred_id_t *pDst)
+{
+    OM_uint32 major, tmpMinor;
+    gss_cred_id_t dst = GSS_C_NO_CREDENTIAL;
+
+    *pDst = GSS_C_NO_CREDENTIAL;
+
+    major = gssEapAllocCred(minor, &dst);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    dst->flags = src->flags;
+
+    if (src->name != GSS_C_NO_NAME) {
+        major = gssEapDuplicateName(minor, src->name, &dst->name);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    if (src->target != GSS_C_NO_NAME) {
+        major = gssEapDuplicateName(minor, src->target, &dst->target);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    if (src->password.value != NULL) {
+        major = duplicateBuffer(minor, &src->password, &dst->password);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    major = duplicateOidSet(minor, src->mechanisms, &dst->mechanisms);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    dst->expiryTime = src->expiryTime;
+
+    if (src->radiusConfigFile.value != NULL)
+        duplicateBufferOrCleanup(&src->radiusConfigFile, &dst->radiusConfigFile);
+    if (src->radiusConfigStanza.value != NULL)
+        duplicateBufferOrCleanup(&src->radiusConfigStanza, &dst->radiusConfigStanza);
+    if (src->caCertificate.value != NULL)
+        duplicateBufferOrCleanup(&src->caCertificate, &dst->caCertificate);
+    if (src->caCertificateBlob.value != NULL)
+        duplicateBufferOrCleanup(&src->caCertificateBlob, &dst->caCertificateBlob);
+    if (src->subjectNameConstraint.value != NULL)
+        duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint);
+    if (src->subjectAltNameConstraint.value != NULL)
+        duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint);
+    if (src->clientCertificate.value != NULL)
+        duplicateBufferOrCleanup(&src->clientCertificate, &dst->clientCertificate);
+    if (src->privateKey.value != NULL)
+        duplicateBufferOrCleanup(&src->privateKey, &dst->privateKey);
+
+#ifdef GSSEAP_ENABLE_REAUTH
+    /* XXX krbCredCache, reauthCred */
+#endif
+
+    *pDst = dst;
+    dst = GSS_C_NO_CREDENTIAL;
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    gssEapReleaseCred(&tmpMinor, &dst);
+
+    return major;
+}
+
+static OM_uint32
+staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
+{
+    OM_uint32 major, tmpMinor;
+    gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
+    gss_name_t defaultIdentityName = GSS_C_NO_NAME;
+    gss_buffer_desc defaultPassword = GSS_C_EMPTY_BUFFER;
+    int isDefaultIdentity = FALSE;
+
+    major = readStaticIdentityFile(minor, &defaultIdentity, &defaultPassword);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
+                             gssEapPrimaryMechForCred(cred), &defaultIdentityName);
+    if (GSS_ERROR(major))
+        goto cleanup;
+
+    if (defaultIdentityName == GSS_C_NO_NAME) {
+        if (cred->name == GSS_C_NO_NAME) {
+            major = GSS_S_CRED_UNAVAIL;
+            *minor = GSSEAP_NO_DEFAULT_IDENTITY;
+            goto cleanup;
+        }
+    } else {
+        if (cred->name == GSS_C_NO_NAME) {
+            cred->name = defaultIdentityName;
+            defaultIdentityName = GSS_C_NO_NAME;
+            isDefaultIdentity = TRUE;
+        } else {
+            major = gssEapCompareName(minor, cred->name,
+                                      defaultIdentityName, 0,
+                                      &isDefaultIdentity);
+            if (GSS_ERROR(major))
+                goto cleanup;
+        }
+    }
+
+    if (isDefaultIdentity &&
+        (cred->flags & CRED_FLAG_PASSWORD) == 0) {
+        major = gssEapSetCredPassword(minor, cred, &defaultPassword);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+cleanup:
+    gssEapReleaseName(&tmpMinor, &defaultIdentityName);
+    zeroAndReleasePassword(&defaultPassword);
+    gss_release_buffer(&tmpMinor, &defaultIdentity);
+
+    return major;
+}
+
+OM_uint32
+gssEapResolveInitiatorCred(OM_uint32 *minor,
+                           const gss_cred_id_t cred,
+                           gss_const_name_t targetName
+#ifndef HAVE_MOONSHOT_GET_IDENTITY
+                                                       GSSEAP_UNUSED
+#endif
+                           ,
+                           gss_cred_id_t *pResolvedCred)
+{
+    OM_uint32 major, tmpMinor;
+    gss_cred_id_t resolvedCred = GSS_C_NO_CREDENTIAL;
+
+    if (cred == GSS_C_NO_CREDENTIAL) {
+        major = gssEapAcquireCred(minor,
+                                  GSS_C_NO_NAME,
+                                  GSS_C_INDEFINITE,
+                                  GSS_C_NO_OID_SET,
+                                  GSS_C_INITIATE,
+                                  &resolvedCred,
+                                  NULL,
+                                  NULL);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    } else {
+        if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
+            major = GSS_S_NO_CRED;
+            *minor = GSSEAP_CRED_USAGE_MISMATCH;
+            goto cleanup;
+        }
+
+        major = gssEapDuplicateCred(minor, cred, &resolvedCred);
+        if (GSS_ERROR(major))
+            goto cleanup;
+    }
+
+    if ((resolvedCred->flags & CRED_FLAG_RESOLVED) == 0) {
+#ifdef HAVE_MOONSHOT_GET_IDENTITY
+        major = libMoonshotResolveInitiatorCred(minor, resolvedCred, targetName);
+        if (major == GSS_S_CRED_UNAVAIL)
+#endif
+            major = staticIdentityFileResolveInitiatorCred(minor, resolvedCred);
+        if (GSS_ERROR(major) && major != GSS_S_CRED_UNAVAIL)
+            goto cleanup;
+
+        /* If we have a caller-supplied password, the credential is resolved. */
+        if ((resolvedCred->flags &
+             (CRED_FLAG_PASSWORD | CRED_FLAG_CERTIFICATE)) == 0) {
+            major = GSS_S_CRED_UNAVAIL;
+            *minor = GSSEAP_NO_DEFAULT_CRED;
+            goto cleanup;
+        }
+
+        resolvedCred->flags |= CRED_FLAG_RESOLVED;
+    }
+
+    *pResolvedCred = resolvedCred;
+    resolvedCred = GSS_C_NO_CREDENTIAL;
+
+    major = GSS_S_COMPLETE;
+    *minor = 0;
+
+cleanup:
+    gssEapReleaseCred(&tmpMinor, &resolvedCred);
+
     return major;
 }