static OM_uint32 _acquire_uuid_name(OM_uint32 *minor_status, krb5_context context, krb5_const_principal princ, int *iakerb, gsskrb5_cred handle) { krb5_error_code ret; krb5_uuid uuid; *iakerb = 0; if (princ->name.name_type != KRB5_NT_CACHE_UUID) return GSS_S_BAD_NAMETYPE; if (princ->name.name_string.len != 1 || strcmp(princ->realm, "UUID") != 0) return GSS_S_BAD_NAME; if (krb5_string_to_uuid(princ->name.name_string.val[0], uuid)) return GSS_S_BAD_NAME; ret = krb5_cc_resolve_by_uuid(context, NULL, &handle->ccache, uuid); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_cc_get_principal(context, handle->ccache, &handle->principal); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } { krb5_data data; ret = krb5_cc_get_config(context, handle->ccache, NULL, "iakerb", &data); if (ret == 0) { *iakerb = 1; handle->endtime = INT_MAX; krb5_data_free(&data); return 0; } } return __gsskrb5_ccache_lifetime(minor_status, context, handle->ccache, handle->principal, &handle->endtime); }
OM_uint32 _gsskrb5_krb5_import_cred(OM_uint32 *minor_status, krb5_ccache id, krb5_principal keytab_principal, krb5_keytab keytab, gss_cred_id_t *cred) { krb5_context context; krb5_error_code kret; gsskrb5_cred handle; OM_uint32 ret; *cred = NULL; GSSAPI_KRB5_INIT (&context); handle = calloc(1, sizeof(*handle)); if (handle == NULL) { _gsskrb5_clear_status (); *minor_status = ENOMEM; return (GSS_S_FAILURE); } HEIMDAL_MUTEX_init(&handle->cred_id_mutex); handle->usage = 0; if (id) { char *str; handle->usage |= GSS_C_INITIATE; kret = krb5_cc_get_principal(context, id, &handle->principal); if (kret) { free(handle); *minor_status = kret; return GSS_S_FAILURE; } if (keytab_principal) { krb5_boolean match; match = krb5_principal_compare(context, handle->principal, keytab_principal); if (match == FALSE) { krb5_free_principal(context, handle->principal); free(handle); _gsskrb5_clear_status (); *minor_status = EINVAL; return GSS_S_FAILURE; } } ret = __gsskrb5_ccache_lifetime(minor_status, context, id, handle->principal, &handle->lifetime); if (ret != GSS_S_COMPLETE) { krb5_free_principal(context, handle->principal); free(handle); return ret; } kret = krb5_cc_get_full_name(context, id, &str); if (kret) goto out; kret = krb5_cc_resolve(context, str, &handle->ccache); free(str); if (kret) goto out; } if (keytab) { char *str; handle->usage |= GSS_C_ACCEPT; if (keytab_principal && handle->principal == NULL) { kret = krb5_copy_principal(context, keytab_principal, &handle->principal); if (kret) goto out; } kret = krb5_kt_get_full_name(context, keytab, &str); if (kret) goto out; kret = krb5_kt_resolve(context, str, &handle->keytab); free(str); if (kret) goto out; } if (id || keytab) { ret = gss_create_empty_oid_set(minor_status, &handle->mechanisms); if (ret == GSS_S_COMPLETE) ret = gss_add_oid_set_member(minor_status, GSS_KRB5_MECHANISM, &handle->mechanisms); if (ret != GSS_S_COMPLETE) { kret = *minor_status; goto out; } } *minor_status = 0; *cred = (gss_cred_id_t)handle; return GSS_S_COMPLETE; out: gss_release_oid_set(minor_status, &handle->mechanisms); if (handle->ccache) krb5_cc_close(context, handle->ccache); if (handle->keytab) krb5_kt_close(context, handle->keytab); if (handle->principal) krb5_free_principal(context, handle->principal); HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); *minor_status = kret; return GSS_S_FAILURE; }
/* * This function produces a cred with a MEMORY ccache containing a TGT * acquired with a password. */ static OM_uint32 acquire_cred_with_password(OM_uint32 *minor_status, krb5_context context, const char *password, OM_uint32 time_req, gss_const_OID desired_mech, gss_cred_usage_t cred_usage, gsskrb5_cred handle) { OM_uint32 ret = GSS_S_FAILURE; krb5_creds cred; krb5_get_init_creds_opt *opt; krb5_ccache ccache = NULL; krb5_error_code kret; time_t now; OM_uint32 left; if (cred_usage == GSS_C_ACCEPT) { /* * TODO: Here we should eventually support user2user (when we get * support for that via an extension to the mechanism * allowing for more than two security context tokens), * and/or new unique MEMORY keytabs (we have MEMORY keytab * support, but we don't have a keytab equivalent of * krb5_cc_new_unique()). Either way, for now we can't * support this. */ *minor_status = ENOTSUP; /* XXX Better error? */ return GSS_S_FAILURE; } memset(&cred, 0, sizeof(cred)); if (handle->principal == NULL) { kret = krb5_get_default_principal(context, &handle->principal); if (kret) goto end; } kret = krb5_get_init_creds_opt_alloc(context, &opt); if (kret) goto end; /* * Get the current time before the AS exchange so we don't * accidentally end up returning a value that puts advertised * expiration past the real expiration. * * We need to do this because krb5_cc_get_lifetime() returns a * relative time that we need to add to the current time. We ought * to have a version of krb5_cc_get_lifetime() that returns absolute * time... */ krb5_timeofday(context, &now); kret = krb5_get_init_creds_password(context, &cred, handle->principal, password, NULL, NULL, 0, NULL, opt); krb5_get_init_creds_opt_free(context, opt); if (kret) goto end; kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache); if (kret) goto end; kret = krb5_cc_initialize(context, ccache, cred.client); if (kret) goto end; kret = krb5_cc_store_cred(context, ccache, &cred); if (kret) goto end; handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache, handle->principal, &left); if (ret != GSS_S_COMPLETE) goto end; handle->endtime = now + left; handle->ccache = ccache; ccache = NULL; ret = GSS_S_COMPLETE; kret = 0; end: if (ccache != NULL) krb5_cc_destroy(context, ccache); if (cred.client != NULL) krb5_free_cred_contents(context, &cred); if (ret != GSS_S_COMPLETE && kret != 0) *minor_status = kret; return (ret); }
/* * Acquires an initiator credential from a ccache or using a keytab. */ static OM_uint32 acquire_initiator_cred(OM_uint32 *minor_status, krb5_context context, OM_uint32 time_req, gss_const_OID desired_mech, gss_cred_usage_t cred_usage, gsskrb5_cred handle) { OM_uint32 ret = GSS_S_FAILURE; krb5_creds cred; krb5_get_init_creds_opt *opt; krb5_principal def_princ = NULL; krb5_ccache def_ccache = NULL; krb5_ccache ccache = NULL; /* we may store into this ccache */ krb5_keytab keytab = NULL; krb5_error_code kret = 0; OM_uint32 left; time_t lifetime = 0; time_t now; memset(&cred, 0, sizeof(cred)); /* * Get current time early so we can set handle->endtime to a value that * cannot accidentally be past the real endtime. We need a variant of * krb5_cc_get_lifetime() that returns absolute endtime. */ krb5_timeofday(context, &now); /* * First look for a ccache that has the desired_name (which may be * the default credential name). * * If we don't have an unexpired credential, acquire one with a * keytab. * * If we acquire one with a keytab, save it in the ccache we found * with the expired credential, if any. * * If we don't have any such ccache, then use a MEMORY ccache. */ if (handle->principal != NULL) { /* * Not default credential case. See if we can find a ccache in * the cccol for the desired_name. */ kret = krb5_cc_cache_match(context, handle->principal, &ccache); if (kret == 0) { kret = krb5_cc_get_lifetime(context, ccache, &lifetime); if (kret == 0) { if (lifetime > 0) goto found; else goto try_keytab; } } /* * Fall through. We shouldn't find this in the default ccache * either, but we'll give it a try, then we'll try using a keytab. */ } /* * Either desired_name was GSS_C_NO_NAME (default cred) or * krb5_cc_cache_match() failed (or found expired). */ kret = krb5_cc_default(context, &def_ccache); if (kret != 0) goto try_keytab; kret = krb5_cc_get_lifetime(context, def_ccache, &lifetime); if (kret != 0) lifetime = 0; kret = krb5_cc_get_principal(context, def_ccache, &def_princ); if (kret != 0) goto try_keytab; /* * Have a default ccache; see if it matches desired_name. */ if (handle->principal == NULL || krb5_principal_compare(context, handle->principal, def_princ) == TRUE) { /* * It matches. * * If we end up trying a keytab then we can write the result to * the default ccache. */ if (handle->principal == NULL) { kret = krb5_copy_principal(context, def_princ, &handle->principal); if (kret) goto end; } if (ccache != NULL) krb5_cc_close(context, ccache); ccache = def_ccache; def_ccache = NULL; if (lifetime > 0) goto found; /* else we fall through and try using a keytab */ } try_keytab: if (handle->principal == NULL) { /* We need to know what client principal to use */ kret = krb5_get_default_principal(context, &handle->principal); if (kret) goto end; } kret = get_keytab(context, &keytab); if (kret) goto end; kret = krb5_get_init_creds_opt_alloc(context, &opt); if (kret) goto end; krb5_timeofday(context, &now); kret = krb5_get_init_creds_keytab(context, &cred, handle->principal, keytab, 0, NULL, opt); krb5_get_init_creds_opt_free(context, opt); if (kret) goto end; /* * We got a credential with a keytab. Save it if we can. */ if (ccache == NULL) { /* * There's no ccache we can overwrite with the credentials we acquired * with a keytab. We'll use a MEMORY ccache then. * * Note that an application that falls into this repeatedly will do an * AS exchange every time it acquires a credential handle. Hopefully * this doesn't happen much. A workaround is to kinit -k once so that * we always re-initialize the matched/default ccache here. I.e., once * there's a FILE/DIR ccache, we'll keep it frash automatically if we * have a keytab, but if there's no FILE/DIR ccache, then we'll * get a fresh credential *every* time we're asked. */ kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache); if (kret) goto end; handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; } /* else we'll re-initialize whichever ccache we matched above */ kret = krb5_cc_initialize(context, ccache, cred.client); if (kret) goto end; kret = krb5_cc_store_cred(context, ccache, &cred); if (kret) goto end; found: assert(handle->principal != NULL); ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache, handle->principal, &left); if (ret != GSS_S_COMPLETE) goto end; handle->endtime = now + left; handle->ccache = ccache; ccache = NULL; ret = GSS_S_COMPLETE; kret = 0; end: if (ccache != NULL) { if ((handle->cred_flags & GSS_CF_DESTROY_CRED_ON_RELEASE) != 0) krb5_cc_destroy(context, ccache); else krb5_cc_close(context, ccache); } if (def_ccache != NULL) krb5_cc_close(context, def_ccache); if (cred.client != NULL) krb5_free_cred_contents(context, &cred); if (def_princ != NULL) krb5_free_principal(context, def_princ); if (keytab != NULL) krb5_kt_close(context, keytab); if (ret != GSS_S_COMPLETE && kret != 0) *minor_status = kret; return (ret); }
static OM_uint32 acquire_initiator_cred (OM_uint32 * minor_status, krb5_context context, gss_const_OID credential_type, const void *credential_data, gss_const_name_t desired_name, OM_uint32 time_req, gss_const_OID desired_mech, gss_cred_usage_t cred_usage, gsskrb5_cred handle ) { OM_uint32 ret; krb5_creds cred; krb5_principal def_princ; krb5_get_init_creds_opt *opt; krb5_ccache ccache; krb5_keytab keytab; krb5_error_code kret; keytab = NULL; ccache = NULL; def_princ = NULL; ret = GSS_S_FAILURE; memset(&cred, 0, sizeof(cred)); /* * If we have a preferred principal, lets try to find it in all * caches, otherwise, fall back to default cache, ignore all * errors while searching. */ if (credential_type != GSS_C_NO_OID && !gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { kret = KRB5_NOCREDS_SUPPLIED; /* XXX */ goto end; } if (handle->principal) { kret = krb5_cc_cache_match (context, handle->principal, &ccache); if (kret == 0) { ret = GSS_S_COMPLETE; goto found; } } if (ccache == NULL) { kret = krb5_cc_default(context, &ccache); if (kret) goto end; } kret = krb5_cc_get_principal(context, ccache, &def_princ); if (kret != 0) { /* we'll try to use a keytab below */ krb5_cc_close(context, ccache); def_princ = NULL; kret = 0; } else if (handle->principal == NULL) { kret = krb5_copy_principal(context, def_princ, &handle->principal); if (kret) goto end; } else if (handle->principal != NULL && krb5_principal_compare(context, handle->principal, def_princ) == FALSE) { krb5_free_principal(context, def_princ); def_princ = NULL; krb5_cc_close(context, ccache); ccache = NULL; } if (def_princ == NULL) { /* We have no existing credentials cache, * so attempt to get a TGT using a keytab. */ if (handle->principal == NULL) { kret = krb5_get_default_principal(context, &handle->principal); if (kret) goto end; } kret = krb5_get_init_creds_opt_alloc(context, &opt); if (kret) goto end; if (credential_type != GSS_C_NO_OID && gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { gss_buffer_t password = (gss_buffer_t)credential_data; /* XXX are we requiring password to be NUL terminated? */ kret = krb5_get_init_creds_password(context, &cred, handle->principal, password->value, NULL, NULL, 0, NULL, opt); } else { kret = get_keytab(context, &keytab); if (kret) { krb5_get_init_creds_opt_free(context, opt); goto end; } kret = krb5_get_init_creds_keytab(context, &cred, handle->principal, keytab, 0, NULL, opt); } krb5_get_init_creds_opt_free(context, opt); if (kret) goto end; kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache); if (kret) goto end; kret = krb5_cc_initialize(context, ccache, cred.client); if (kret) { krb5_cc_destroy(context, ccache); goto end; } kret = krb5_cc_store_cred(context, ccache, &cred); if (kret) { krb5_cc_destroy(context, ccache); goto end; } handle->lifetime = cred.times.endtime; handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; } else { ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache, handle->principal, &handle->lifetime); if (ret != GSS_S_COMPLETE) { krb5_cc_close(context, ccache); goto end; } kret = 0; } found: handle->ccache = ccache; ret = GSS_S_COMPLETE; end: if (cred.client != NULL) krb5_free_cred_contents(context, &cred); if (def_princ != NULL) krb5_free_principal(context, def_princ); if (keytab != NULL) krb5_kt_close(context, keytab); if (ret != GSS_S_COMPLETE && kret != 0) *minor_status = kret; return (ret); }
static OM_uint32 acquire_initiator_cred (OM_uint32 * minor_status, krb5_context context, const gss_name_t desired_name, OM_uint32 time_req, gss_cred_usage_t cred_usage, gsskrb5_cred handle ) { OM_uint32 ret = GSS_S_FAILURE; krb5_creds cred; krb5_principal def_princ = NULL; krb5_get_init_creds_opt *opt; krb5_ccache ccache = NULL; krb5_error_code kret; memset(&cred, 0, sizeof(cred)); /* * If we have a preferred principal, lets try to find it in all * caches, otherwise, fall back to default cache, ignore all * errors while searching. */ if (handle->principal) { kret = krb5_cc_cache_match (context, handle->principal, &ccache); if (kret == 0) { goto found; } } if (ccache == NULL) { kret = krb5_cc_default(context, &ccache); if (kret) goto end; } kret = krb5_cc_get_principal(context, ccache, &def_princ); if (kret != 0) { /* we'll try to use a keytab below */ krb5_cc_close(context, ccache); def_princ = NULL; kret = 0; } else if (handle->principal == NULL) { kret = krb5_copy_principal(context, def_princ, &handle->principal); if (kret) goto end; } else if (handle->principal != NULL && krb5_principal_compare(context, handle->principal, def_princ) == FALSE) { krb5_free_principal(context, def_princ); def_princ = NULL; krb5_cc_close(context, ccache); ccache = NULL; } if (def_princ == NULL) { /* We have no existing credentials cache, * so attempt to get a TGT using a keytab. */ if (handle->principal == NULL) { kret = krb5_get_default_principal(context, &handle->principal); if (kret) goto end; } /* * Require user is in the keytab before trying to talk to * the KDC. */ kret = get_keytab(context, handle, 0); if (kret) goto end; /* since the name might have changed, let double check the credential cache */ kret = krb5_cc_cache_match(context, handle->principal, &ccache); if (kret == 0) goto found; kret = krb5_get_init_creds_opt_alloc(context, &opt); if (kret) goto end; kret = krb5_get_init_creds_keytab(context, &cred, handle->principal, handle->keytab, 0, NULL, opt); krb5_get_init_creds_opt_free(context, opt); if (kret) goto end; kret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, &ccache); if (kret) goto end; kret = krb5_cc_initialize(context, ccache, cred.client); if (kret) { krb5_cc_destroy(context, ccache); goto end; } kret = krb5_cc_store_cred(context, ccache, &cred); if (kret) { krb5_cc_destroy(context, ccache); goto end; } handle->endtime = cred.times.endtime; handle->cred_flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; } else { found: ret = __gsskrb5_ccache_lifetime(minor_status, context, ccache, handle->principal, &handle->endtime); if (ret != GSS_S_COMPLETE) { krb5_cc_close(context, ccache); goto end; } kret = 0; } handle->ccache = ccache; ret = GSS_S_COMPLETE; end: if (cred.client != NULL) krb5_free_cred_contents(context, &cred); if (def_princ != NULL) krb5_free_principal(context, def_princ); if (ret != GSS_S_COMPLETE && kret != 0) *minor_status = kret; return (ret); }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_import_cred(OM_uint32 * minor_status, gss_buffer_t cred_token, gss_cred_id_t * cred_handle) { krb5_context context; krb5_error_code ret; gsskrb5_cred handle; krb5_ccache id; krb5_storage *sp; char *str; uint32_t type; int flags = 0; *cred_handle = GSS_C_NO_CREDENTIAL; GSSAPI_KRB5_INIT (&context); sp = krb5_storage_from_mem(cred_token->value, cred_token->length); if (sp == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } ret = krb5_ret_uint32(sp, &type); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } switch (type) { case 0: { krb5_creds creds; ret = krb5_ret_creds(sp, &creds); krb5_storage_free(sp); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_cc_new_unique(context, "API", NULL, &id); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_cc_initialize(context, id, creds.client); if (ret) { krb5_cc_destroy(context, id); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_cc_store_cred(context, id, &creds); krb5_free_cred_contents(context, &creds); if (ret) { krb5_cc_destroy(context, id); *minor_status = ret; return GSS_S_FAILURE; } flags |= GSS_CF_DESTROY_CRED_ON_RELEASE; break; } case 1: ret = krb5_ret_string(sp, &str); krb5_storage_free(sp); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_cc_resolve(context, str, &id); krb5_xfree(str); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } break; default: krb5_storage_free(sp); *minor_status = 0; return GSS_S_NO_CRED; } handle = calloc(1, sizeof(*handle)); if (handle == NULL) { krb5_cc_close(context, id); *minor_status = ENOMEM; return GSS_S_FAILURE; } handle->usage = GSS_C_INITIATE; krb5_cc_get_principal(context, id, &handle->principal); handle->ccache = id; handle->cred_flags = flags; if (handle->principal) __gsskrb5_ccache_lifetime(minor_status, context, id, handle->principal, &handle->endtime); *cred_handle = (gss_cred_id_t)handle; return GSS_S_COMPLETE; }