2 * Copyright (c) 2011, JANET(UK)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 * Utility routines for credential handles.
37 #include "gssapiP_eap.h"
40 # include <shlobj.h> /* may need to use ShFolder.h instead */
47 gssEapAllocCred(OM_uint32 *minor, gss_cred_id_t *pCred)
52 *pCred = GSS_C_NO_CREDENTIAL;
54 cred = (gss_cred_id_t)GSSEAP_CALLOC(1, sizeof(*cred));
60 if (GSSEAP_MUTEX_INIT(&cred->mutex) != 0) {
61 *minor = GSSEAP_GET_LAST_ERROR();
62 gssEapReleaseCred(&tmpMinor, &cred);
69 return GSS_S_COMPLETE;
73 zeroAndReleasePassword(gss_buffer_t password)
75 if (password->value != NULL) {
76 memset(password->value, 0, password->length);
77 GSSEAP_FREE(password->value);
80 password->value = NULL;
85 gssEapReleaseCred(OM_uint32 *minor, gss_cred_id_t *pCred)
88 gss_cred_id_t cred = *pCred;
89 krb5_context krbContext = NULL;
91 if (cred == GSS_C_NO_CREDENTIAL) {
92 return GSS_S_COMPLETE;
95 GSSEAP_KRB_INIT(&krbContext);
97 gssEapReleaseName(&tmpMinor, &cred->name);
98 gssEapReleaseName(&tmpMinor, &cred->target);
100 zeroAndReleasePassword(&cred->password);
102 gss_release_buffer(&tmpMinor, &cred->radiusConfigFile);
103 gss_release_buffer(&tmpMinor, &cred->radiusConfigStanza);
104 gss_release_buffer(&tmpMinor, &cred->caCertificate);
105 gss_release_buffer(&tmpMinor, &cred->subjectNameConstraint);
106 gss_release_buffer(&tmpMinor, &cred->subjectAltNameConstraint);
107 gss_release_buffer(&tmpMinor, &cred->privateKey);
108 gss_release_buffer(&tmpMinor, &cred->clientCertificate);
110 #ifdef GSSEAP_ENABLE_REAUTH
111 if (cred->krbCredCache != NULL) {
112 if (cred->flags & CRED_FLAG_DEFAULT_CCACHE)
113 krb5_cc_close(krbContext, cred->krbCredCache);
115 krb5_cc_destroy(krbContext, cred->krbCredCache);
117 if (cred->reauthCred != GSS_C_NO_CREDENTIAL)
118 gssReleaseCred(&tmpMinor, &cred->reauthCred);
121 GSSEAP_MUTEX_DESTROY(&cred->mutex);
122 memset(cred, 0, sizeof(*cred));
127 return GSS_S_COMPLETE;
131 readStaticIdentityFile(OM_uint32 *minor,
132 gss_buffer_t defaultIdentity,
133 gss_buffer_t defaultPassword,
134 gss_buffer_t defaultPrivateKey)
136 OM_uint32 major, tmpMinor;
142 struct passwd *pw = NULL, pwd;
146 defaultIdentity->length = 0;
147 defaultIdentity->value = NULL;
149 if (defaultPassword != GSS_C_NO_BUFFER) {
150 defaultPassword->length = 0;
151 defaultPassword->value = NULL;
154 if (defaultPrivateKey != GSS_C_NO_BUFFER) {
155 defaultPrivateKey->length = 0;
156 defaultPrivateKey->value = NULL;
159 ccacheName = getenv("GSSEAP_IDENTITY");
160 if (ccacheName == NULL) {
162 TCHAR szPath[MAX_PATH];
164 if (!SUCCEEDED(SHGetFolderPath(NULL,
165 CSIDL_APPDATA, /* |CSIDL_FLAG_CREATE */
166 NULL, /* User access token */
167 0, /* SHGFP_TYPE_CURRENT */
169 major = GSS_S_CRED_UNAVAIL;
170 *minor = GSSEAP_GET_LAST_ERROR(); /* XXX */
174 snprintf(buf, sizeof(buf), "%s/.gss_eap_id", szPath);
176 if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 ||
177 pw == NULL || pw->pw_dir == NULL) {
178 major = GSS_S_CRED_UNAVAIL;
179 *minor = GSSEAP_GET_LAST_ERROR();
183 snprintf(buf, sizeof(buf), "%s/.gss_eap_id", pw->pw_dir);
188 fp = fopen(ccacheName, "r");
190 major = GSS_S_CRED_UNAVAIL;
191 *minor = GSSEAP_NO_DEFAULT_CRED;
195 while (fgets(buf, sizeof(buf), fp) != NULL) {
196 gss_buffer_desc src, *dst;
198 src.length = strlen(buf);
204 if (buf[src.length - 1] == '\n') {
205 buf[src.length - 1] = '\0';
206 if (--src.length == 0)
211 dst = defaultIdentity;
213 dst = defaultPassword;
215 dst = defaultPrivateKey;
219 if (dst != GSS_C_NO_BUFFER) {
220 major = duplicateBuffer(minor, &src, dst);
221 if (GSS_ERROR(major))
228 if (defaultIdentity->length == 0) {
229 major = GSS_S_CRED_UNAVAIL;
230 *minor = GSSEAP_NO_DEFAULT_CRED;
234 major = GSS_S_COMPLETE;
241 if (GSS_ERROR(major)) {
242 gss_release_buffer(&tmpMinor, defaultIdentity);
243 zeroAndReleasePassword(defaultPassword);
244 gss_release_buffer(&tmpMinor, defaultPrivateKey);
247 memset(buf, 0, sizeof(buf));
253 gssEapPrimaryMechForCred(gss_cred_id_t cred)
255 gss_OID nameMech = GSS_C_NO_OID;
257 if (cred->mechanisms != GSS_C_NO_OID_SET &&
258 cred->mechanisms->count == 1)
259 nameMech = &cred->mechanisms->elements[0];
265 gssEapAcquireCred(OM_uint32 *minor,
266 const gss_name_t desiredName,
267 OM_uint32 timeReq GSSEAP_UNUSED,
268 const gss_OID_set desiredMechs,
270 gss_cred_id_t *pCred,
271 gss_OID_set *pActualMechs,
274 OM_uint32 major, tmpMinor;
277 /* XXX TODO validate with changed set_cred_option API */
278 *pCred = GSS_C_NO_CREDENTIAL;
280 major = gssEapAllocCred(minor, &cred);
281 if (GSS_ERROR(major))
286 cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT;
289 cred->flags |= CRED_FLAG_INITIATE;
292 cred->flags |= CRED_FLAG_ACCEPT;
295 major = GSS_S_FAILURE;
296 *minor = GSSEAP_BAD_USAGE;
301 major = gssEapValidateMechs(minor, desiredMechs);
302 if (GSS_ERROR(major))
305 major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms);
306 if (GSS_ERROR(major))
309 if (desiredName != GSS_C_NO_NAME) {
310 GSSEAP_MUTEX_LOCK(&desiredName->mutex);
312 major = gssEapDuplicateName(minor, desiredName, &cred->name);
313 if (GSS_ERROR(major)) {
314 GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
318 GSSEAP_MUTEX_UNLOCK(&desiredName->mutex);
321 #ifdef GSSEAP_ENABLE_ACCEPTOR
322 if (cred->flags & CRED_FLAG_ACCEPT) {
323 struct rs_context *radContext;
325 major = gssEapCreateRadiusContext(minor, cred, &radContext);
326 if (GSS_ERROR(major))
329 rs_context_destroy(radContext);
333 if (pActualMechs != NULL) {
334 major = duplicateOidSet(minor, cred->mechanisms, pActualMechs);
335 if (GSS_ERROR(major))
340 *timeRec = GSS_C_INDEFINITE;
344 major = GSS_S_COMPLETE;
348 if (GSS_ERROR(major))
349 gssEapReleaseCred(&tmpMinor, &cred);
355 * Return TRUE if cred available for mechanism. Caller need no acquire
356 * lock because mechanisms list is immutable.
359 gssEapCredAvailable(gss_cred_id_t cred, gss_OID mech)
364 GSSEAP_ASSERT(mech != GSS_C_NO_OID);
366 if (cred == GSS_C_NO_CREDENTIAL || cred->mechanisms == GSS_C_NO_OID_SET)
369 gss_test_oid_set_member(&minor, mech, cred->mechanisms, &present);
375 staticIdentityFileResolveDefaultIdentity(OM_uint32 *minor,
376 const gss_cred_id_t cred,
379 OM_uint32 major, tmpMinor;
380 gss_OID nameMech = gssEapPrimaryMechForCred(cred);
381 gss_buffer_desc defaultIdentity = GSS_C_EMPTY_BUFFER;
383 *pName = GSS_C_NO_NAME;
385 major = readStaticIdentityFile(minor, &defaultIdentity,
386 GSS_C_NO_BUFFER, GSS_C_NO_BUFFER);
387 if (major == GSS_S_COMPLETE) {
388 major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
392 gss_release_buffer(&tmpMinor, &defaultIdentity);
398 gssEapResolveCredIdentity(OM_uint32 *minor,
402 gss_OID nameMech = gssEapPrimaryMechForCred(cred);
404 if (cred->name != GSS_C_NO_NAME) {
406 return GSS_S_COMPLETE;
409 if (cred->flags & CRED_FLAG_ACCEPT) {
410 gss_buffer_desc nameBuf = GSS_C_EMPTY_BUFFER;
411 char serviceName[5 + MAXHOSTNAMELEN];
413 /* default host-based service is host@localhost */
414 memcpy(serviceName, "host@", 5);
415 if (gethostname(&serviceName[5], MAXHOSTNAMELEN) != 0) {
416 *minor = GSSEAP_NO_HOSTNAME;
417 return GSS_S_FAILURE;
420 nameBuf.value = serviceName;
421 nameBuf.length = strlen((char *)nameBuf.value);
423 major = gssEapImportName(minor, &nameBuf, GSS_C_NT_HOSTBASED_SERVICE,
424 nameMech, &cred->name);
425 if (GSS_ERROR(major))
427 } else if (cred->flags & CRED_FLAG_INITIATE) {
428 #ifdef HAVE_MOONSHOT_GET_IDENTITY
429 major = libMoonshotResolveDefaultIdentity(minor, cred, &cred->name);
430 if (major == GSS_S_CRED_UNAVAIL)
432 major = staticIdentityFileResolveDefaultIdentity(minor, cred, &cred->name);
433 if (major != GSS_S_CRED_UNAVAIL)
438 return GSS_S_COMPLETE;
442 gssEapInquireCred(OM_uint32 *minor,
445 OM_uint32 *pLifetime,
446 gss_cred_usage_t *cred_usage,
447 gss_OID_set *mechanisms)
450 time_t now, lifetime;
453 major = gssEapResolveCredIdentity(minor, cred);
454 if (GSS_ERROR(major))
457 if (cred->name != GSS_C_NO_NAME) {
458 major = gssEapDuplicateName(minor, cred->name, name);
459 if (GSS_ERROR(major))
462 *name = GSS_C_NO_NAME;
465 if (cred_usage != NULL) {
466 OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT));
469 case CRED_FLAG_INITIATE:
470 *cred_usage = GSS_C_INITIATE;
472 case CRED_FLAG_ACCEPT:
473 *cred_usage = GSS_C_ACCEPT;
476 *cred_usage = GSS_C_BOTH;
481 if (mechanisms != NULL) {
482 if (cred->mechanisms != GSS_C_NO_OID_SET)
483 major = duplicateOidSet(minor, cred->mechanisms, mechanisms);
485 major = gssEapIndicateMechs(minor, mechanisms);
486 if (GSS_ERROR(major))
490 if (cred->expiryTime == 0) {
491 lifetime = GSS_C_INDEFINITE;
494 lifetime = now - cred->expiryTime;
499 if (pLifetime != NULL) {
500 *pLifetime = lifetime;
504 major = GSS_S_CREDENTIALS_EXPIRED;
505 *minor = GSSEAP_CRED_EXPIRED;
509 major = GSS_S_COMPLETE;
517 gssEapSetCredPassword(OM_uint32 *minor,
519 const gss_buffer_t password)
521 OM_uint32 major, tmpMinor;
522 gss_buffer_desc newPassword = GSS_C_EMPTY_BUFFER;
524 if (cred->flags & CRED_FLAG_RESOLVED) {
525 major = GSS_S_FAILURE;
526 *minor = GSSEAP_CRED_RESOLVED;
530 if (password != GSS_C_NO_BUFFER) {
531 major = duplicateBuffer(minor, password, &newPassword);
532 if (GSS_ERROR(major))
535 cred->flags |= CRED_FLAG_PASSWORD;
537 cred->flags &= ~(CRED_FLAG_PASSWORD);
540 gss_release_buffer(&tmpMinor, &cred->password);
541 cred->password = newPassword;
543 major = GSS_S_COMPLETE;
551 * Currently only the privateKey path is exposed to the application
552 * (via gss_set_cred_option() or the third line in ~/.gss_eap_id).
553 * At some point in the future we may add support for setting the
554 * client certificate separately.
557 gssEapSetCredClientCertificate(OM_uint32 *minor,
559 const gss_buffer_t clientCert,
560 const gss_buffer_t privateKey)
562 OM_uint32 major, tmpMinor;
563 gss_buffer_desc newClientCert = GSS_C_EMPTY_BUFFER;
564 gss_buffer_desc newPrivateKey = GSS_C_EMPTY_BUFFER;
566 if (cred->flags & CRED_FLAG_RESOLVED) {
567 major = GSS_S_FAILURE;
568 *minor = GSSEAP_CRED_RESOLVED;
572 if (clientCert == GSS_C_NO_BUFFER &&
573 privateKey == GSS_C_NO_BUFFER) {
574 cred->flags &= ~(CRED_FLAG_CERTIFICATE);
575 major = GSS_S_COMPLETE;
580 if (clientCert != GSS_C_NO_BUFFER) {
581 major = duplicateBuffer(minor, clientCert, &newClientCert);
582 if (GSS_ERROR(major))
586 if (privateKey != GSS_C_NO_BUFFER) {
587 major = duplicateBuffer(minor, privateKey, &newPrivateKey);
588 if (GSS_ERROR(major))
592 cred->flags |= CRED_FLAG_CERTIFICATE;
594 gss_release_buffer(&tmpMinor, &cred->clientCertificate);
595 cred->clientCertificate = newClientCert;
597 gss_release_buffer(&tmpMinor, &cred->privateKey);
598 cred->privateKey = newPrivateKey;
600 major = GSS_S_COMPLETE;
604 if (GSS_ERROR(major)) {
605 gss_release_buffer(&tmpMinor, &newClientCert);
606 gss_release_buffer(&tmpMinor, &newPrivateKey);
613 gssEapSetCredService(OM_uint32 *minor,
615 const gss_name_t target)
617 OM_uint32 major, tmpMinor;
618 gss_name_t newTarget = GSS_C_NO_NAME;
620 if (cred->flags & CRED_FLAG_RESOLVED) {
621 major = GSS_S_FAILURE;
622 *minor = GSSEAP_CRED_RESOLVED;
626 if (target != GSS_C_NO_NAME) {
627 major = gssEapDuplicateName(minor, target, &newTarget);
628 if (GSS_ERROR(major))
631 cred->flags |= CRED_FLAG_TARGET;
633 cred->flags &= ~(CRED_FLAG_TARGET);
636 gssEapReleaseName(&tmpMinor, &cred->target);
637 cred->target = newTarget;
639 major = GSS_S_COMPLETE;
647 gssEapDuplicateCred(OM_uint32 *minor,
648 const gss_cred_id_t src,
651 OM_uint32 major, tmpMinor;
652 gss_cred_id_t dst = GSS_C_NO_CREDENTIAL;
654 *pDst = GSS_C_NO_CREDENTIAL;
656 major = gssEapAllocCred(minor, &dst);
657 if (GSS_ERROR(major))
660 dst->flags = src->flags;
662 if (src->name != GSS_C_NO_NAME) {
663 major = gssEapDuplicateName(minor, src->name, &dst->name);
664 if (GSS_ERROR(major))
668 if (src->target != GSS_C_NO_NAME) {
669 major = gssEapDuplicateName(minor, src->target, &dst->target);
670 if (GSS_ERROR(major))
674 if (src->password.value != NULL) {
675 major = duplicateBuffer(minor, &src->password, &dst->password);
676 if (GSS_ERROR(major))
680 major = duplicateOidSet(minor, src->mechanisms, &dst->mechanisms);
681 if (GSS_ERROR(major))
684 dst->expiryTime = src->expiryTime;
686 if (src->radiusConfigFile.value != NULL)
687 duplicateBufferOrCleanup(&src->radiusConfigFile, &dst->radiusConfigFile);
688 if (src->radiusConfigStanza.value != NULL)
689 duplicateBufferOrCleanup(&src->radiusConfigStanza, &dst->radiusConfigStanza);
690 if (src->caCertificate.value != NULL)
691 duplicateBufferOrCleanup(&src->caCertificate, &dst->caCertificate);
692 if (src->subjectNameConstraint.value != NULL)
693 duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint);
694 if (src->subjectAltNameConstraint.value != NULL)
695 duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint);
696 if (src->privateKey.value != NULL)
697 duplicateBufferOrCleanup(&src->privateKey, &dst->privateKey);
699 #ifdef GSSEAP_ENABLE_REAUTH
700 /* XXX krbCredCache, reauthCred */
704 dst = GSS_C_NO_CREDENTIAL;
706 major = GSS_S_COMPLETE;
710 gssEapReleaseCred(&tmpMinor, &dst);
716 staticIdentityFileResolveInitiatorCred(OM_uint32 *minor, gss_cred_id_t cred)
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 gss_buffer_desc defaultPrivateKey = GSS_C_EMPTY_BUFFER;
723 int isDefaultIdentity = FALSE;
725 major = readStaticIdentityFile(minor, &defaultIdentity,
726 &defaultPassword, &defaultPrivateKey);
727 if (GSS_ERROR(major))
730 major = gssEapImportName(minor, &defaultIdentity, GSS_C_NT_USER_NAME,
731 gssEapPrimaryMechForCred(cred), &defaultIdentityName);
732 if (GSS_ERROR(major))
735 if (defaultIdentityName == GSS_C_NO_NAME) {
736 if (cred->name == GSS_C_NO_NAME) {
737 major = GSS_S_CRED_UNAVAIL;
738 *minor = GSSEAP_NO_DEFAULT_IDENTITY;
742 if (cred->name == GSS_C_NO_NAME) {
743 cred->name = defaultIdentityName;
744 defaultIdentityName = GSS_C_NO_NAME;
745 isDefaultIdentity = TRUE;
747 major = gssEapCompareName(minor, cred->name,
748 defaultIdentityName, &isDefaultIdentity);
749 if (GSS_ERROR(major))
754 if (isDefaultIdentity) {
755 if (defaultPrivateKey.length != 0) {
756 major = gssEapSetCredClientCertificate(minor, cred, GSS_C_NO_BUFFER,
758 if (GSS_ERROR(major))
762 if ((cred->flags & CRED_FLAG_PASSWORD) == 0) {
763 major = gssEapSetCredPassword(minor, cred, &defaultPassword);
764 if (GSS_ERROR(major))
770 gssEapReleaseName(&tmpMinor, &defaultIdentityName);
771 zeroAndReleasePassword(&defaultPassword);
772 gss_release_buffer(&tmpMinor, &defaultIdentity);
773 gss_release_buffer(&tmpMinor, &defaultPrivateKey);
779 gssEapResolveInitiatorCred(OM_uint32 *minor,
780 const gss_cred_id_t cred,
781 const gss_name_t targetName
782 #ifndef HAVE_MOONSHOT_GET_IDENTITY
786 gss_cred_id_t *pResolvedCred)
788 OM_uint32 major, tmpMinor;
789 gss_cred_id_t resolvedCred = GSS_C_NO_CREDENTIAL;
791 if (cred == GSS_C_NO_CREDENTIAL) {
792 major = gssEapAcquireCred(minor,
800 if (GSS_ERROR(major))
803 if ((cred->flags & CRED_FLAG_INITIATE) == 0) {
804 major = GSS_S_NO_CRED;
805 *minor = GSSEAP_CRED_USAGE_MISMATCH;
809 major = gssEapDuplicateCred(minor, cred, &resolvedCred);
810 if (GSS_ERROR(major))
814 if ((resolvedCred->flags & CRED_FLAG_RESOLVED) == 0) {
815 #ifdef HAVE_MOONSHOT_GET_IDENTITY
816 major = libMoonshotResolveInitiatorCred(minor, resolvedCred, targetName);
817 if (major == GSS_S_CRED_UNAVAIL)
819 major = staticIdentityFileResolveInitiatorCred(minor, resolvedCred);
820 if (GSS_ERROR(major) && major != GSS_S_CRED_UNAVAIL)
823 /* If we have a caller-supplied password, the credential is resolved. */
824 if ((resolvedCred->flags &
825 (CRED_FLAG_PASSWORD | CRED_FLAG_CERTIFICATE)) == 0) {
826 major = GSS_S_CRED_UNAVAIL;
827 *minor = GSSEAP_NO_DEFAULT_CRED;
831 resolvedCred->flags |= CRED_FLAG_RESOLVED;
834 *pResolvedCred = resolvedCred;
835 resolvedCred = GSS_C_NO_CREDENTIAL;
837 major = GSS_S_COMPLETE;
841 gssEapReleaseCred(&tmpMinor, &resolvedCred);