OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_context ( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, gss_name_t * src_name, gss_name_t * targ_name, OM_uint32 * lifetime_rec, gss_OID * mech_type, OM_uint32 * ctx_flags, int * locally_initiated, int * open_context ) { krb5_context context; OM_uint32 ret; gsskrb5_ctx ctx = (gsskrb5_ctx)context_handle; gss_name_t name; if (src_name) *src_name = GSS_C_NO_NAME; if (targ_name) *targ_name = GSS_C_NO_NAME; GSSAPI_KRB5_INIT (&context); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (src_name) { name = (gss_name_t)ctx->source; ret = _gsskrb5_duplicate_name (minor_status, name, src_name); if (ret) goto failed; } if (targ_name) { name = (gss_name_t)ctx->target; ret = _gsskrb5_duplicate_name (minor_status, name, targ_name); if (ret) goto failed; } if (lifetime_rec) { ret = _gsskrb5_lifetime_left(minor_status, context, ctx->lifetime, lifetime_rec); if (ret) goto failed; } if (mech_type) *mech_type = GSS_KRB5_MECHANISM; if (ctx_flags) *ctx_flags = ctx->flags; if (locally_initiated) *locally_initiated = ctx->more_flags & LOCAL; if (open_context) *open_context = ctx->more_flags & OPEN; *minor_status = 0; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_COMPLETE; failed: if (src_name) _gsskrb5_release_name(NULL, src_name); if (targ_name) _gsskrb5_release_name(NULL, targ_name); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_export_sec_context ( OM_uint32 * minor_status, gss_ctx_id_t * context_handle, gss_buffer_t interprocess_token ) { krb5_context context; const gsskrb5_ctx ctx = (const gsskrb5_ctx) *context_handle; krb5_storage *sp; krb5_auth_context ac; OM_uint32 ret = GSS_S_COMPLETE; krb5_data data; gss_buffer_desc buffer; int flags; OM_uint32 minor; krb5_error_code kret; GSSAPI_KRB5_INIT (&context); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (!(ctx->flags & GSS_C_TRANS_FLAG)) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); *minor_status = 0; return GSS_S_UNAVAILABLE; } sp = krb5_storage_emem (); if (sp == NULL) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); *minor_status = ENOMEM; return GSS_S_FAILURE; } ac = ctx->auth_context; /* flagging included fields */ flags = 0; if (ac->local_address) flags |= SC_LOCAL_ADDRESS; if (ac->remote_address) flags |= SC_REMOTE_ADDRESS; if (ac->keyblock) flags |= SC_KEYBLOCK; if (ac->local_subkey) flags |= SC_LOCAL_SUBKEY; if (ac->remote_subkey) flags |= SC_REMOTE_SUBKEY; kret = krb5_store_int32 (sp, flags); if (kret) { *minor_status = kret; goto failure; } /* marshall auth context */ kret = krb5_store_int32 (sp, ac->flags); if (kret) { *minor_status = kret; goto failure; } if (ac->local_address) { kret = krb5_store_address (sp, *ac->local_address); if (kret) { *minor_status = kret; goto failure; } } if (ac->remote_address) { kret = krb5_store_address (sp, *ac->remote_address); if (kret) { *minor_status = kret; goto failure; } } kret = krb5_store_int16 (sp, ac->local_port); if (kret) { *minor_status = kret; goto failure; } kret = krb5_store_int16 (sp, ac->remote_port); if (kret) { *minor_status = kret; goto failure; } if (ac->keyblock) { kret = krb5_store_keyblock (sp, *ac->keyblock); if (kret) { *minor_status = kret; goto failure; } } if (ac->local_subkey) { kret = krb5_store_keyblock (sp, *ac->local_subkey); if (kret) { *minor_status = kret; goto failure; } } if (ac->remote_subkey) { kret = krb5_store_keyblock (sp, *ac->remote_subkey); if (kret) { *minor_status = kret; goto failure; } } kret = krb5_store_int32 (sp, ac->local_seqnumber); if (kret) { *minor_status = kret; goto failure; } kret = krb5_store_int32 (sp, ac->remote_seqnumber); if (kret) { *minor_status = kret; goto failure; } kret = krb5_store_int32 (sp, ac->keytype); if (kret) { *minor_status = kret; goto failure; } kret = krb5_store_int32 (sp, ac->cksumtype); if (kret) { *minor_status = kret; goto failure; } /* names */ ret = _gsskrb5_export_name (minor_status, (gss_name_t)ctx->source, &buffer); if (ret) goto failure; data.data = buffer.value; data.length = buffer.length; kret = krb5_store_data (sp, data); _gsskrb5_release_buffer (&minor, &buffer); if (kret) { *minor_status = kret; goto failure; } ret = _gsskrb5_export_name (minor_status, (gss_name_t)ctx->target, &buffer); if (ret) goto failure; data.data = buffer.value; data.length = buffer.length; ret = GSS_S_FAILURE; kret = krb5_store_data (sp, data); _gsskrb5_release_buffer (&minor, &buffer); if (kret) { *minor_status = kret; goto failure; } kret = krb5_store_int32 (sp, ctx->flags); if (kret) { *minor_status = kret; goto failure; } kret = krb5_store_int32 (sp, ctx->more_flags); if (kret) { *minor_status = kret; goto failure; } kret = krb5_store_int32 (sp, ctx->lifetime); if (kret) { *minor_status = kret; goto failure; } kret = _gssapi_msg_order_export(sp, ctx->order); if (kret ) { *minor_status = kret; goto failure; } kret = krb5_storage_to_data (sp, &data); krb5_storage_free (sp); if (kret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); *minor_status = kret; return GSS_S_FAILURE; } interprocess_token->length = data.length; interprocess_token->value = data.data; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); ret = _gsskrb5_delete_sec_context (minor_status, context_handle, GSS_C_NO_BUFFER); if (ret != GSS_S_COMPLETE) _gsskrb5_release_buffer (NULL, interprocess_token); *minor_status = 0; return ret; failure: HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); krb5_storage_free (sp); return ret; }
static OM_uint32 import_krb5_name(OM_uint32 *minor_status, gss_const_OID mech, const gss_buffer_t input_name_buffer, gss_const_OID input_name_type, gss_name_t *output_name) { krb5_context context; krb5_principal princ; krb5_error_code ret; char *tmp; GSSAPI_KRB5_INIT (&context); tmp = malloc (input_name_buffer->length + 1); if (tmp == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy (tmp, input_name_buffer->value, input_name_buffer->length); tmp[input_name_buffer->length] = '\0'; if (tmp[0] == '@') { princ = calloc(1, sizeof(*princ)); if (princ == NULL) { free(tmp); *minor_status = ENOMEM; return GSS_S_FAILURE; } princ->realm = strdup(&tmp[1]); if (princ->realm == NULL) { free(tmp); free(princ); return GSS_S_FAILURE; } } else { ret = krb5_parse_name (context, tmp, &princ); if (ret) { free(tmp); *minor_status = ret; if (ret == KRB5_PARSE_ILLCHAR || ret == KRB5_PARSE_MALFORMED) return GSS_S_BAD_NAME; return GSS_S_FAILURE; } } if (mech && gss_oid_equal(mech, GSS_PKU2U_MECHANISM) && strchr(tmp, '@') == NULL) krb5_principal_set_realm(context, princ, KRB5_PKU2U_REALM_NAME); free(tmp); if (princ->name.name_string.len == 2 && gss_oid_equal(input_name_type, GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL)) krb5_principal_set_type(context, princ, KRB5_NT_GSS_HOSTBASED_SERVICE); *output_name = (gss_name_t)princ; return GSS_S_COMPLETE; }
OM_uint32 GSSAPI_CALLCONV _gss_krb5_acquire_cred_ext(OM_uint32 * minor_status, const gss_name_t desired_name, gss_const_OID credential_type, const void *credential_data, OM_uint32 time_req, gss_const_OID desired_mech, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle) { krb5_init_creds_context ctx = NULL; krb5_get_init_creds_opt *opt = NULL; krb5_principal principal; krb5_context context; krb5_error_code kret; gsskrb5_cred handle = NULL; krb5_ccache ccache = NULL, ccachereplace = NULL; char *passwordstr = NULL; char *cache_name = NULL; char *lkdc_hostname = NULL; hx509_cert hxcert = NULL; heim_array_t bundleacl = NULL; krb5_principal new_name = NULL; GSSAPI_KRB5_INIT(&context); cred_usage &= GSS_C_OPTION_MASK; if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } if (desired_name == GSS_C_NO_NAME) return GSS_S_FAILURE; if (gss_oid_equal(credential_type, GSS_C_CRED_HEIMBASE)) { heim_object_t pw, cname, cert, lkdc; heim_dict_t dict = (heim_dict_t)credential_data; pw = heim_dict_copy_value(dict, _gsskrb5_kGSSICPassword); if (pw) { if (heim_get_tid(pw) == heim_string_get_type_id()) { passwordstr = heim_string_copy_utf8(pw); if (passwordstr == NULL) { kret = ENOMEM; goto out; } } else if (heim_get_tid(pw) == heim_data_get_type_id()) { passwordstr = malloc(heim_data_get_length(pw) + 1); if (passwordstr == NULL) { kret = ENOMEM; goto out; } memcpy(passwordstr, heim_data_get_bytes(pw), heim_data_get_length(pw)); passwordstr[heim_data_get_length(pw)] = '\0'; } heim_release(pw); } cname = heim_dict_copy_value(dict, _gsskrb5_kGSSICKerberosCacheName); if (cname) { cache_name = heim_string_copy_utf8(cname); heim_release(cname); } bundleacl = heim_dict_copy_value(dict, _gsskrb5_kGSSICAppIdentifierACL); #ifdef PKINIT cert = heim_dict_copy_value(dict, _gsskrb5_kGSSICCertificate); if (cert) { kret = hx509_cert_init_SecFramework(context->hx509ctx, cert, &hxcert); if (kret) goto out; heim_release(cert); } #endif lkdc = heim_dict_copy_value(dict, _gsskrb5_kGSSICLKDCHostname); if (lkdc) { lkdc_hostname = heim_string_copy_utf8(lkdc); heim_release(lkdc); } } else if (gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { gss_buffer_t password = (gss_buffer_t)credential_data; passwordstr = malloc(password->length + 1); if (passwordstr == NULL) { kret = ENOMEM; goto out; } memcpy(passwordstr, password->value, password->length); passwordstr[password->length] = '\0'; } else { *minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */ return GSS_S_FAILURE; } if (passwordstr == NULL && hxcert == NULL) { *minor_status = KRB5_NOCREDS_SUPPLIED; /* XXX */ return GSS_S_FAILURE; } *output_cred_handle = NULL; handle = calloc(1, sizeof(*handle)); if (handle == NULL) { *minor_status = ENOMEM; return (GSS_S_FAILURE); } principal = (krb5_principal)desired_name; HEIMDAL_MUTEX_init(&handle->cred_id_mutex); kret = krb5_copy_principal(context, principal, &handle->principal); if (kret) goto out; kret = krb5_cc_new_unique(context, NULL, NULL, &ccache); if (kret) goto out; kret = krb5_get_init_creds_opt_alloc(context, &opt); if (kret) goto out; krb5_get_init_creds_opt_set_default_flags(context, "gss", krb5_principal_get_realm(context, principal), opt); krb5_get_init_creds_opt_set_forwardable(opt, 1); krb5_get_init_creds_opt_set_proxiable(opt, 1); krb5_get_init_creds_opt_set_renew_life(opt, 3600 * 24 * 30); /* 1 month */ if (hxcert) { char *cert_pool[2] = { "KEYCHAIN:", NULL }; kret = krb5_get_init_creds_opt_set_pkinit(context, opt, principal, NULL, "KEYCHAIN:", cert_pool, NULL, 8, NULL, NULL, NULL); if (kret) goto out; } kret = krb5_init_creds_init(context, handle->principal, NULL, NULL, NULL, opt, &ctx); if (kret) goto out; if (passwordstr) { kret = krb5_init_creds_set_password(context, ctx, passwordstr); memset(passwordstr, 0, strlen(passwordstr)); free(passwordstr); passwordstr = NULL; if (kret) goto out; } if (hxcert) { kret = krb5_init_creds_set_pkinit_client_cert(context, ctx, hxcert); if (kret) goto out; } if (lkdc_hostname) { kret = krb5_init_creds_set_kdc_hostname(context, ctx, lkdc_hostname); free(lkdc_hostname); lkdc_hostname = NULL; if (kret) goto out; } kret = krb5_init_creds_get(context, ctx); if (kret) goto out; handle->endtime = _krb5_init_creds_get_cred_endtime(context, ctx); /* * If we where subjected to a referral, update the name of the credential */ new_name = _krb5_init_creds_get_cred_client(context, ctx); if (new_name && !krb5_principal_compare(context, new_name, handle->principal)) { krb5_free_principal(context, handle->principal); kret = krb5_copy_principal(context, new_name, &handle->principal); if (kret) goto out; } /* * Now store the credential */ if (cache_name) { /* check if caller told us to use a specific cache */ kret = krb5_cc_resolve(context, cache_name, &ccachereplace); if (kret) goto out; } else { /* * check if there an existing cache to overwrite before we lay * down the new cache */ (void)krb5_cc_cache_match(context, principal, &ccachereplace); } kret = krb5_init_creds_store(context, ctx, ccache); if (kret == 0) kret = krb5_init_creds_store_config(context, ctx, ccache); if (bundleacl) krb5_cc_set_acl(context, ccache, "kHEIMAttrBundleIdentifierACL", bundleacl); krb5_init_creds_free(context, ctx); ctx = NULL; if (kret) goto out; krb5_get_init_creds_opt_free(context, opt); opt = NULL; /* * If we have a credential with the same naame, lets overwrite it */ if (ccachereplace) { kret = krb5_cc_move(context, ccache, ccachereplace); if (kret) goto out; handle->ccache = ccachereplace; ccachereplace = NULL; } else { handle->ccache = ccache; } handle->usage = cred_usage; *minor_status = 0; *output_cred_handle = (gss_cred_id_t)handle; if (cache_name) free(cache_name); heim_release(bundleacl); return GSS_S_COMPLETE; out: if (bundleacl) heim_release(bundleacl); if (opt) krb5_get_init_creds_opt_free(context, opt); if (ctx) krb5_init_creds_free(context, ctx); if (lkdc_hostname) free(lkdc_hostname); if (cache_name) free(cache_name); if (passwordstr) { memset(passwordstr, 0, strlen(passwordstr)); free(passwordstr); } if (ccachereplace) krb5_cc_close(context, ccachereplace); if (ccache) krb5_cc_destroy(context, ccache); if (handle) { 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; }
OM_uint32 _gsskrb5_init_sec_context (OM_uint32 * minor_status, const gss_cred_id_t initiator_cred_handle, gss_ctx_id_t * context_handle, const gss_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { gsskrb5_cred cred = (gsskrb5_cred)initiator_cred_handle; krb5_const_principal name = (krb5_const_principal)target_name; gsskrb5_ctx ctx; OM_uint32 ret; GSSAPI_KRB5_INIT (); output_token->length = 0; output_token->value = NULL; if (context_handle == NULL) { *minor_status = 0; return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; } if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = 0; if (target_name == GSS_C_NO_NAME) { if (actual_mech_type) *actual_mech_type = GSS_C_NO_OID; *minor_status = 0; return GSS_S_BAD_NAME; } if (mech_type != GSS_C_NO_OID && !gss_oid_equal(mech_type, GSS_KRB5_MECHANISM)) return GSS_S_BAD_MECH; if (input_token == GSS_C_NO_BUFFER || input_token->length == 0) { OM_uint32 ret; if (*context_handle != GSS_C_NO_CONTEXT) { *minor_status = 0; return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; } ret = _gsskrb5_create_ctx(minor_status, context_handle, input_chan_bindings, INITIATOR_START); if (ret) return ret; } if (*context_handle == GSS_C_NO_CONTEXT) { *minor_status = 0; return GSS_S_FAILURE | GSS_S_CALL_BAD_STRUCTURE; } ctx = (gsskrb5_ctx) *context_handle; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); switch (ctx->state) { case INITIATOR_START: ret = init_auth(minor_status, cred, ctx, name, mech_type, req_flags, time_req, input_chan_bindings, input_token, actual_mech_type, output_token, ret_flags, time_rec); break; case INITIATOR_WAIT_FOR_MUTAL: ret = repl_mutual(minor_status, ctx, mech_type, req_flags, time_req, input_chan_bindings, input_token, actual_mech_type, output_token, ret_flags, time_rec); break; case INITIATOR_READY: /* * If we get there, the caller have called * gss_init_sec_context() one time too many. */ *minor_status = 0; ret = GSS_S_BAD_STATUS; break; default: *minor_status = 0; ret = GSS_S_BAD_STATUS; break; } HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); /* destroy context in case of error */ if (GSS_ERROR(ret)) { OM_uint32 min2; _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER); } return ret; }
OM_uint32 _gsskrb5_set_sec_context_option (OM_uint32 *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value) { OM_uint32 maj_stat; GSSAPI_KRB5_INIT (); if (value == GSS_C_NO_BUFFER) { *minor_status = EINVAL; return GSS_S_FAILURE; } if (gss_oid_equal(desired_object, GSS_KRB5_COMPAT_DES3_MIC_X)) { gsskrb5_ctx ctx; int flag; if (*context_handle == GSS_C_NO_CONTEXT) { *minor_status = EINVAL; return GSS_S_NO_CONTEXT; } maj_stat = get_bool(minor_status, value, &flag); if (maj_stat != GSS_S_COMPLETE) return maj_stat; ctx = (gsskrb5_ctx)*context_handle; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (flag) ctx->more_flags |= COMPAT_OLD_DES3; else ctx->more_flags &= ~COMPAT_OLD_DES3; ctx->more_flags |= COMPAT_OLD_DES3_SELECTED; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DNS_CANONICALIZE_X)) { int flag; maj_stat = get_bool(minor_status, value, &flag); if (maj_stat != GSS_S_COMPLETE) return maj_stat; krb5_set_dns_canonicalize_hostname(_gsskrb5_context, flag); return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X)) { char *str; if (value == NULL || value->length == 0) { str = NULL; } else { str = malloc(value->length + 1); if (str) { *minor_status = 0; return GSS_S_UNAVAILABLE; } memcpy(str, value->value, value->length); str[value->length] = '\0'; } _gsskrb5_register_acceptor_identity(str); free(str); *minor_status = 0; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DEFAULT_REALM_X)) { char *str; if (value == NULL || value->length == 0) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_READ; } str = malloc(value->length + 1); if (str) { *minor_status = 0; return GSS_S_UNAVAILABLE; } memcpy(str, value->value, value->length); str[value->length] = '\0'; krb5_set_default_realm(_gsskrb5_context, str); free(str); *minor_status = 0; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_SEND_TO_KDC_X)) { if (value == NULL || value->length == 0) { krb5_set_send_to_kdc_func(_gsskrb5_context, NULL, NULL); } else { struct gsskrb5_send_to_kdc c; if (value->length != sizeof(c)) { *minor_status = EINVAL; return GSS_S_FAILURE; } memcpy(&c, value->value, sizeof(c)); krb5_set_send_to_kdc_func(_gsskrb5_context, (krb5_send_to_kdc_func)c.func, c.ptr); } *minor_status = 0; return GSS_S_COMPLETE; } *minor_status = EINVAL; return GSS_S_FAILURE; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred(OM_uint32 * minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle, gss_OID_set * actual_mechs, OM_uint32 * time_rec) { krb5_const_principal principal = (krb5_const_principal)desired_name; krb5_context context; gsskrb5_cred handle; OM_uint32 ret, junk; cred_usage &= GSS_C_OPTION_MASK; if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } GSSAPI_KRB5_INIT(&context); *output_cred_handle = NULL; handle = calloc(1, sizeof(*handle)); if (handle == NULL) { *minor_status = ENOMEM; return (GSS_S_FAILURE); } HEIMDAL_MUTEX_init(&handle->cred_id_mutex); if (principal && principal->name.name_type == KRB5_NT_CACHE_UUID) { int iakerb = 0; ret = _acquire_uuid_name(minor_status, context, principal, &iakerb, handle); if (iakerb) { *minor_status = 0; ret = GSS_S_BAD_NAME; } if (ret) { _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); return ret; } goto out; } if (principal) { krb5_error_code kret; kret = krb5_copy_principal(context, principal, &handle->principal); if (kret) { _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = kret; return GSS_S_FAILURE; } } if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { ret = acquire_initiator_cred(minor_status, context, desired_name, time_req, cred_usage, handle); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } } if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { ret = acquire_acceptor_cred(minor_status, context, desired_name, time_req, cred_usage, handle); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } } out: handle->usage = cred_usage; *minor_status = 0; *output_cred_handle = (gss_cred_id_t)handle; ret = _gsskrb5_inquire_cred(minor_status, *output_cred_handle, NULL, time_rec, NULL, actual_mechs); if (ret) { _gsskrb5_release_cred(&junk, output_cred_handle); return ret; } return (GSS_S_COMPLETE); }
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; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_export_cred(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_buffer_t cred_token) { gsskrb5_cred handle = (gsskrb5_cred)cred_handle; krb5_context context; krb5_error_code ret; krb5_storage *sp; krb5_data data, mech; char *str; GSSAPI_KRB5_INIT (&context); if (handle->usage != GSS_C_INITIATE && handle->usage != GSS_C_BOTH) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } sp = krb5_storage_emem(); if (sp == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } /* type = krb5_cc_get_type(context, handle->ccache); * * XXX Always use reference keys since that makes it easier to * transport between processing in seprate authentication domain. * * We should encrypt credentials in KCM though using the kcm * session key. */ if (1 /*strcmp(type, "MEMORY") == 0 */) { krb5_creds *creds; ret = krb5_store_uint32(sp, 0); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } ret = _krb5_get_krbtgt(context, handle->ccache, handle->principal->realm, &creds); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_store_creds(sp, creds); krb5_free_creds(context, creds); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } } else { ret = krb5_store_uint32(sp, 1); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_cc_get_full_name(context, handle->ccache, &str); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_store_string(sp, str); free(str); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } } ret = krb5_storage_to_data(sp, &data); krb5_storage_free(sp); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } sp = krb5_storage_emem(); if (sp == NULL) { krb5_data_free(&data); *minor_status = ENOMEM; return GSS_S_FAILURE; } mech.data = GSS_KRB5_MECHANISM->elements; mech.length = GSS_KRB5_MECHANISM->length; ret = krb5_store_data(sp, mech); if (ret) { krb5_data_free(&data); krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_store_data(sp, data); krb5_data_free(&data); if (ret) { krb5_storage_free(sp); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_storage_to_data(sp, &data); krb5_storage_free(sp); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } cred_token->value = data.data; cred_token->length = data.length; return GSS_S_COMPLETE; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_pseudo_random(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int prf_key, const gss_buffer_t prf_in, ssize_t desired_output_len, gss_buffer_t prf_out) { gsskrb5_ctx ctx = (gsskrb5_ctx)context_handle; krb5_context context; krb5_error_code ret; krb5_crypto crypto; krb5_data input, output; uint32_t num; OM_uint32 junk; unsigned char *p; krb5_keyblock *key = NULL; size_t dol; if (ctx == NULL) { *minor_status = 0; return GSS_S_NO_CONTEXT; } if (desired_output_len <= 0 || prf_in->length + 4 < prf_in->length) { *minor_status = 0; return GSS_S_FAILURE; } dol = desired_output_len; GSSAPI_KRB5_INIT (&context); switch(prf_key) { case GSS_C_PRF_KEY_FULL: _gsskrb5i_get_acceptor_subkey(ctx, context, &key); break; case GSS_C_PRF_KEY_PARTIAL: _gsskrb5i_get_initiator_subkey(ctx, context, &key); break; default: _gsskrb5_set_status(EINVAL, "unknown kerberos prf_key"); *minor_status = EINVAL; return GSS_S_FAILURE; } if (key == NULL) { _gsskrb5_set_status(EINVAL, "no prf_key found"); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = krb5_crypto_init(context, key, 0, &crypto); krb5_free_keyblock (context, key); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } prf_out->value = malloc(dol); if (prf_out->value == NULL) { _gsskrb5_set_status(GSS_KRB5_S_KG_INPUT_TOO_LONG, "Out of memory"); *minor_status = GSS_KRB5_S_KG_INPUT_TOO_LONG; krb5_crypto_destroy(context, crypto); return GSS_S_FAILURE; } prf_out->length = dol; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); input.length = prf_in->length + 4; input.data = malloc(prf_in->length + 4); if (input.data == NULL) { _gsskrb5_set_status(GSS_KRB5_S_KG_INPUT_TOO_LONG, "Out of memory"); *minor_status = GSS_KRB5_S_KG_INPUT_TOO_LONG; gss_release_buffer(&junk, prf_out); krb5_crypto_destroy(context, crypto); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_FAILURE; } memcpy(((uint8_t *)input.data) + 4, prf_in->value, prf_in->length); num = 0; p = prf_out->value; while(dol > 0) { size_t tsize; _gsskrb5_encode_om_uint32(num, input.data); ret = krb5_crypto_prf(context, crypto, &input, &output); if (ret) { *minor_status = ret; free(input.data); gss_release_buffer(&junk, prf_out); krb5_crypto_destroy(context, crypto); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_FAILURE; } tsize = min(dol, output.length); memcpy(p, output.data, tsize); p += output.length; dol -= tsize; krb5_data_free(&output); num++; } free(input.data); krb5_crypto_destroy(context, crypto); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_COMPLETE; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_inquire_cred (OM_uint32 * minor_status, const gss_cred_id_t cred_handle, gss_name_t * output_name, OM_uint32 * lifetime, gss_cred_usage_t * cred_usage, gss_OID_set * mechanisms ) { krb5_context context; gss_cred_id_t aqcred_init = GSS_C_NO_CREDENTIAL; gss_cred_id_t aqcred_accept = GSS_C_NO_CREDENTIAL; gsskrb5_cred acred = NULL, icred = NULL; OM_uint32 ret; *minor_status = 0; if (output_name) *output_name = NULL; if (mechanisms) *mechanisms = GSS_C_NO_OID_SET; GSSAPI_KRB5_INIT (&context); if (cred_handle == GSS_C_NO_CREDENTIAL) { ret = _gsskrb5_acquire_cred(minor_status, GSS_C_NO_NAME, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &aqcred_accept, NULL, NULL); if (ret == GSS_S_COMPLETE) acred = (gsskrb5_cred)aqcred_accept; ret = _gsskrb5_acquire_cred(minor_status, GSS_C_NO_NAME, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &aqcred_init, NULL, NULL); if (ret == GSS_S_COMPLETE) icred = (gsskrb5_cred)aqcred_init; if (icred == NULL && acred == NULL) { *minor_status = 0; return GSS_S_NO_CRED; } } else acred = (gsskrb5_cred)cred_handle; if (acred) HEIMDAL_MUTEX_lock(&acred->cred_id_mutex); if (icred) HEIMDAL_MUTEX_lock(&icred->cred_id_mutex); if (output_name != NULL) { if (icred && icred->principal != NULL) { gss_name_t name; if (acred && acred->principal) name = (gss_name_t)acred->principal; else name = (gss_name_t)icred->principal; ret = _gsskrb5_duplicate_name(minor_status, name, output_name); if (ret) goto out; } else if (acred && acred->usage == GSS_C_ACCEPT) { krb5_principal princ; *minor_status = krb5_sname_to_principal(context, NULL, NULL, KRB5_NT_SRV_HST, &princ); if (*minor_status) { ret = GSS_S_FAILURE; goto out; } *output_name = (gss_name_t)princ; } else { krb5_principal princ; *minor_status = krb5_get_default_principal(context, &princ); if (*minor_status) { ret = GSS_S_FAILURE; goto out; } *output_name = (gss_name_t)princ; } } if (lifetime != NULL) { OM_uint32 alife = GSS_C_INDEFINITE, ilife = GSS_C_INDEFINITE; if (acred) alife = acred->lifetime; if (icred) ilife = icred->lifetime; ret = _gsskrb5_lifetime_left(minor_status, context, min(alife,ilife), lifetime); if (ret) goto out; } if (cred_usage != NULL) { if (acred && icred) *cred_usage = GSS_C_BOTH; else if (acred) *cred_usage = GSS_C_ACCEPT; else if (icred) *cred_usage = GSS_C_INITIATE; else abort(); } if (mechanisms != NULL) { ret = gss_create_empty_oid_set(minor_status, mechanisms); if (ret) goto out; if (acred) ret = gss_add_oid_set_member(minor_status, &acred->mechanisms->elements[0], mechanisms); if (ret == GSS_S_COMPLETE && icred) ret = gss_add_oid_set_member(minor_status, &icred->mechanisms->elements[0], mechanisms); if (ret) goto out; } ret = GSS_S_COMPLETE; out: if (acred) HEIMDAL_MUTEX_unlock(&acred->cred_id_mutex); if (icred) HEIMDAL_MUTEX_unlock(&icred->cred_id_mutex); if (aqcred_init != GSS_C_NO_CREDENTIAL) ret = _gsskrb5_release_cred(minor_status, &aqcred_init); if (aqcred_accept != GSS_C_NO_CREDENTIAL) ret = _gsskrb5_release_cred(minor_status, &aqcred_accept); return ret; }
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; }
OM_uint32 _gsskrb5_unwrap (OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_buffer_t input_message_buffer, gss_buffer_t output_message_buffer, int * conf_state, gss_qop_t * qop_state ) { krb5_keyblock *key; krb5_context context; OM_uint32 ret; krb5_keytype keytype; gsskrb5_ctx ctx = (gsskrb5_ctx) context_handle; output_message_buffer->value = NULL; output_message_buffer->length = 0; if (qop_state != NULL) *qop_state = GSS_C_QOP_DEFAULT; GSSAPI_KRB5_INIT (&context); if (ctx->more_flags & IS_CFX) return _gssapi_unwrap_cfx (minor_status, ctx, context, input_message_buffer, output_message_buffer, conf_state, qop_state); HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ret = _gsskrb5i_get_token_key(ctx, context, &key); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (ret) { *minor_status = ret; return GSS_S_FAILURE; } krb5_enctype_to_keytype (context, key->keytype, &keytype); *minor_status = 0; switch (keytype) { case KEYTYPE_DES : ret = unwrap_des (minor_status, ctx, input_message_buffer, output_message_buffer, conf_state, qop_state, key); break; case KEYTYPE_DES3 : ret = unwrap_des3 (minor_status, ctx, context, input_message_buffer, output_message_buffer, conf_state, qop_state, key); break; case KEYTYPE_ARCFOUR: case KEYTYPE_ARCFOUR_56: ret = _gssapi_unwrap_arcfour (minor_status, ctx, context, input_message_buffer, output_message_buffer, conf_state, qop_state, key); break; default : abort(); break; } krb5_free_keyblock (context, key); return ret; }
OM_uint32 _gsskrb5_set_sec_context_option (OM_uint32 *minor_status, gss_ctx_id_t *context_handle, const gss_OID desired_object, const gss_buffer_t value) { krb5_context context; OM_uint32 maj_stat; GSSAPI_KRB5_INIT (&context); if (value == GSS_C_NO_BUFFER) { *minor_status = EINVAL; return GSS_S_FAILURE; } if (gss_oid_equal(desired_object, GSS_KRB5_COMPAT_DES3_MIC_X)) { gsskrb5_ctx ctx; int flag; if (*context_handle == GSS_C_NO_CONTEXT) { *minor_status = EINVAL; return GSS_S_NO_CONTEXT; } maj_stat = get_bool(minor_status, value, &flag); if (maj_stat != GSS_S_COMPLETE) return maj_stat; ctx = (gsskrb5_ctx)*context_handle; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (flag) ctx->more_flags |= COMPAT_OLD_DES3; else ctx->more_flags &= ~COMPAT_OLD_DES3; ctx->more_flags |= COMPAT_OLD_DES3_SELECTED; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DNS_CANONICALIZE_X)) { int flag; maj_stat = get_bool(minor_status, value, &flag); if (maj_stat != GSS_S_COMPLETE) return maj_stat; krb5_set_dns_canonicalize_hostname(context, flag); return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_REGISTER_ACCEPTOR_IDENTITY_X)) { char *str; maj_stat = get_string(minor_status, value, &str); if (maj_stat != GSS_S_COMPLETE) return maj_stat; _gsskrb5_register_acceptor_identity(str); free(str); *minor_status = 0; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_DEFAULT_REALM_X)) { char *str; maj_stat = get_string(minor_status, value, &str); if (maj_stat != GSS_S_COMPLETE) return maj_stat; if (str == NULL) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_READ; } krb5_set_default_realm(context, str); free(str); *minor_status = 0; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_SEND_TO_KDC_X)) { if (value == NULL || value->length == 0) { krb5_set_send_to_kdc_func(context, NULL, NULL); } else { struct gsskrb5_send_to_kdc c; if (value->length != sizeof(c)) { *minor_status = EINVAL; return GSS_S_FAILURE; } memcpy(&c, value->value, sizeof(c)); krb5_set_send_to_kdc_func(context, (krb5_send_to_kdc_func)c.func, c.ptr); } *minor_status = 0; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_CCACHE_NAME_X)) { char *str; maj_stat = get_string(minor_status, value, &str); if (maj_stat != GSS_S_COMPLETE) return maj_stat; if (str == NULL) { *minor_status = 0; return GSS_S_CALL_INACCESSIBLE_READ; } *minor_status = krb5_cc_set_default_name(context, str); free(str); if (*minor_status) return GSS_S_FAILURE; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_SET_TIME_OFFSET_X)) { OM_uint32 offset; time_t t; maj_stat = get_int32(minor_status, value, &offset); if (maj_stat != GSS_S_COMPLETE) return maj_stat; t = time(NULL) + offset; krb5_set_real_time(context, t, 0); *minor_status = 0; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_TIME_OFFSET_X)) { krb5_timestamp sec; int32_t usec; time_t t; t = time(NULL); krb5_us_timeofday (context, &sec, &usec); maj_stat = set_int32(minor_status, value, sec - t); if (maj_stat != GSS_S_COMPLETE) return maj_stat; *minor_status = 0; return GSS_S_COMPLETE; } else if (gss_oid_equal(desired_object, GSS_KRB5_PLUGIN_REGISTER_X)) { struct gsskrb5_krb5_plugin c; if (value->length != sizeof(c)) { *minor_status = EINVAL; return GSS_S_FAILURE; } memcpy(&c, value->value, sizeof(c)); krb5_plugin_register(context, c.type, c.name, c.symbol); *minor_status = 0; return GSS_S_COMPLETE; } *minor_status = EINVAL; return GSS_S_FAILURE; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_import_sec_context ( OM_uint32 * minor_status, const gss_buffer_t interprocess_token, gss_ctx_id_t * context_handle ) { OM_uint32 ret = GSS_S_FAILURE; krb5_context context; krb5_error_code kret; krb5_storage *sp; krb5_auth_context ac; krb5_address local, remote; krb5_address *localp, *remotep; krb5_data data; gss_buffer_desc buffer; krb5_keyblock keyblock; int32_t flags, tmp; gsskrb5_ctx ctx; gss_name_t name; GSSAPI_KRB5_INIT (&context); *context_handle = GSS_C_NO_CONTEXT; localp = remotep = NULL; sp = krb5_storage_from_mem (interprocess_token->value, interprocess_token->length); if (sp == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) { *minor_status = ENOMEM; krb5_storage_free (sp); return GSS_S_FAILURE; } HEIMDAL_MUTEX_init(&ctx->ctx_id_mutex); kret = krb5_auth_con_init (context, &ctx->auth_context); if (kret) { *minor_status = kret; ret = GSS_S_FAILURE; goto failure; } /* flags */ *minor_status = 0; if (krb5_ret_int32 (sp, &flags) != 0) goto failure; /* retrieve the auth context */ ac = ctx->auth_context; if (krb5_ret_int32 (sp, &tmp) != 0) goto failure; ac->flags = tmp; if (flags & SC_LOCAL_ADDRESS) { if (krb5_ret_address (sp, localp = &local) != 0) goto failure; } if (flags & SC_REMOTE_ADDRESS) { if (krb5_ret_address (sp, remotep = &remote) != 0) goto failure; } krb5_auth_con_setaddrs (context, ac, localp, remotep); if (localp) krb5_free_address (context, localp); if (remotep) krb5_free_address (context, remotep); localp = remotep = NULL; if (krb5_ret_int16 (sp, &ac->local_port) != 0) goto failure; if (krb5_ret_int16 (sp, &ac->remote_port) != 0) goto failure; if (flags & SC_KEYBLOCK) { if (krb5_ret_keyblock (sp, &keyblock) != 0) goto failure; krb5_auth_con_setkey (context, ac, &keyblock); krb5_free_keyblock_contents (context, &keyblock); } if (flags & SC_LOCAL_SUBKEY) { if (krb5_ret_keyblock (sp, &keyblock) != 0) goto failure; krb5_auth_con_setlocalsubkey (context, ac, &keyblock); krb5_free_keyblock_contents (context, &keyblock); } if (flags & SC_REMOTE_SUBKEY) { if (krb5_ret_keyblock (sp, &keyblock) != 0) goto failure; krb5_auth_con_setremotesubkey (context, ac, &keyblock); krb5_free_keyblock_contents (context, &keyblock); } if (krb5_ret_uint32 (sp, &ac->local_seqnumber)) goto failure; if (krb5_ret_uint32 (sp, &ac->remote_seqnumber)) goto failure; if (krb5_ret_int32 (sp, &tmp) != 0) goto failure; ac->keytype = tmp; if (krb5_ret_int32 (sp, &tmp) != 0) goto failure; ac->cksumtype = tmp; /* names */ if (krb5_ret_data (sp, &data)) goto failure; buffer.value = data.data; buffer.length = data.length; ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NT_EXPORT_NAME, &name); if (ret) { ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NO_OID, &name); if (ret) { krb5_data_free (&data); goto failure; } } ctx->source = (krb5_principal)name; krb5_data_free (&data); if (krb5_ret_data (sp, &data) != 0) goto failure; buffer.value = data.data; buffer.length = data.length; ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NT_EXPORT_NAME, &name); if (ret) { ret = _gsskrb5_import_name (minor_status, &buffer, GSS_C_NO_OID, &name); if (ret) { krb5_data_free (&data); goto failure; } } ctx->target = (krb5_principal)name; krb5_data_free (&data); if (krb5_ret_int32 (sp, &tmp)) goto failure; ctx->flags = tmp; if (krb5_ret_int32 (sp, &tmp)) goto failure; ctx->more_flags = tmp; if (krb5_ret_int32 (sp, &tmp)) goto failure; ctx->endtime = tmp; ret = _gssapi_msg_order_import(minor_status, sp, &ctx->gk5c.order); if (ret) goto failure; krb5_storage_free (sp); _gsskrb5i_is_cfx(context, ctx, (ctx->more_flags & LOCAL) == 0); *context_handle = (gss_ctx_id_t)ctx; return GSS_S_COMPLETE; failure: krb5_auth_con_free (context, ctx->auth_context); if (ctx->source != NULL) krb5_free_principal(context, ctx->source); if (ctx->target != NULL) krb5_free_principal(context, ctx->target); if (localp) krb5_free_address (context, localp); if (remotep) krb5_free_address (context, remotep); if(ctx->gk5c.order) _gssapi_msg_order_destroy(&ctx->gk5c.order); HEIMDAL_MUTEX_destroy(&ctx->ctx_id_mutex); krb5_storage_free (sp); free (ctx); *context_handle = GSS_C_NO_CONTEXT; return ret; }
OM_uint32 _gsskrb5_appl_change_password(OM_uint32 *minor_status, gss_name_t name, const char *oldpw, const char *newpw) { krb5_data result_code_string, result_string; krb5_get_init_creds_opt *opt = NULL; krb5_context context; krb5_principal principal = (krb5_principal)name; krb5_creds kcred; krb5_error_code ret; int result_code; GSSAPI_KRB5_INIT (&context); memset(&kcred, 0, sizeof(kcred)); ret = krb5_get_init_creds_opt_alloc(context, &opt); if (ret) goto out; krb5_get_init_creds_opt_set_tkt_life(opt, 300); krb5_get_init_creds_opt_set_forwardable(opt, FALSE); krb5_get_init_creds_opt_set_proxiable(opt, FALSE); ret = krb5_get_init_creds_password(context, &kcred, principal, oldpw, NULL, NULL, 0, "kadmin/changepw", opt); if (ret) goto out; ret = krb5_set_password(context, &kcred, newpw, NULL, &result_code, &result_code_string, &result_string); if (ret) goto out; krb5_data_free(&result_string); krb5_data_free(&result_code_string); if (result_code) { krb5_set_error_message(context, KRB5KRB_AP_ERR_BAD_INTEGRITY, "Failed to change invalid password: %d", result_code); ret = KRB5KRB_AP_ERR_BAD_INTEGRITY; goto out; } out: if (opt) krb5_get_init_creds_opt_free(context, opt); krb5_free_cred_contents(context, &kcred); *minor_status = ret; return ret ? GSS_S_FAILURE : GSS_S_COMPLETE; }
static OM_uint32 accept_sec_context(OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle, gss_OID mech, gsskrb5_acceptor_state acceptor_state) { krb5_context context; OM_uint32 ret; gsskrb5_ctx ctx; GSSAPI_KRB5_INIT(&context); output_token->length = 0; output_token->value = NULL; if (src_name != NULL) *src_name = NULL; if (mech_type) *mech_type = mech; if (*context_handle == GSS_C_NO_CONTEXT) { ret = _gsskrb5_create_ctx(minor_status, context_handle, context, input_chan_bindings, mech); if (ret) return ret; /* mark as acceptor */ ctx = (gsskrb5_ctx)*context_handle; ctx->gk5c.flags |= GK5C_ACCEPTOR; ctx->acceptor_state = acceptor_state; } else { ctx = (gsskrb5_ctx)*context_handle; } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); do { ret = ctx->acceptor_state(minor_status, ctx, context, acceptor_cred_handle, input_token_buffer, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); } while (output_token->length == 0 && ret == GSS_S_COMPLETE && ctx->acceptor_state != step_acceptor_completed); HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (GSS_ERROR(ret)) { OM_uint32 min2; _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER); } return ret; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_store_cred(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, gss_cred_usage_t cred_usage, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_OID_set *elements_stored, gss_cred_usage_t *cred_usage_stored) { krb5_context context; krb5_error_code ret; gsskrb5_cred cred; krb5_ccache id = NULL; krb5_ccache def_ccache = NULL; const char *def_type = NULL; time_t exp_current; time_t exp_new; *minor_status = 0; if (cred_usage != GSS_C_INITIATE) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } if (desired_mech != GSS_C_NO_OID && gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0) return GSS_S_BAD_MECH; cred = (gsskrb5_cred)input_cred_handle; if (cred == NULL) return GSS_S_NO_CRED; GSSAPI_KRB5_INIT (&context); HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); if (cred->usage != cred_usage && cred->usage != GSS_C_BOTH) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } ret = krb5_cc_get_lifetime(context, cred->ccache, &exp_new); if (ret) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = ret; return GSS_S_NO_CRED; } if (cred->principal == NULL) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = GSS_KRB5_S_KG_TGT_MISSING; return GSS_S_FAILURE; } ret = krb5_cc_default(context, &def_ccache); if (ret == 0) { def_type = krb5_cc_get_type(context, def_ccache); krb5_cc_close(context, def_ccache); } def_ccache = NULL; /* write out cred to credential cache */ ret = krb5_cc_cache_match(context, cred->principal, &id); if (ret) { if (default_cred) { ret = krb5_cc_default(context, &id); if (ret) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = ret; return GSS_S_FAILURE; } } else { if (def_type == NULL || !krb5_cc_support_switch(context, def_type)) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = 0; /* XXX */ return GSS_S_NO_CRED; /* XXX */ } ret = krb5_cc_new_unique(context, def_type, NULL, &id); if (ret) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = ret; return GSS_S_FAILURE; } overwrite_cred = 1; } } if (!overwrite_cred) { /* If current creds are expired or near it, overwrite */ ret = krb5_cc_get_lifetime(context, id, &exp_current); if (ret != 0 || exp_new > exp_current) overwrite_cred = 1; } if (!overwrite_cred) { /* Nothing to do */ krb5_cc_close(context, id); HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = 0; return GSS_S_DUPLICATE_ELEMENT; } ret = krb5_cc_initialize(context, id, cred->principal); if (ret == 0) ret = krb5_cc_copy_match_f(context, cred->ccache, id, NULL, NULL, NULL); if (ret) { krb5_cc_close(context, id); HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = ret; return(GSS_S_FAILURE); } if (default_cred && def_type != NULL && krb5_cc_support_switch(context, def_type)) krb5_cc_switch(context, id); krb5_cc_close(context, id); HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = 0; return GSS_S_COMPLETE; }
OM_uint32 _gsspku2u_acquire_cred(OM_uint32 * minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle, gss_OID_set * actual_mechs, OM_uint32 * time_rec) { krb5_context context; gsskrb5_cred handle; hx509_query *q; hx509_certs certs = NULL; OM_uint32 ret; krb5_principal name = (krb5_principal)desired_name; /* remove non-options from cred_usage */ cred_usage = (cred_usage & GSS_C_OPTION_MASK); if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } GSSAPI_KRB5_INIT(&context); *output_cred_handle = NULL; if (time_rec) *time_rec = GSS_C_INDEFINITE; if (actual_mechs) *actual_mechs = GSS_C_NO_OID_SET; /* * We can't acquire credential for specific names that are not * PKU2U names, so don't try. */ if (name && !krb5_principal_is_pku2u(context, name)) { *minor_status = 0; return GSS_S_BAD_NAME; } handle = calloc(1, sizeof(*handle)); if (handle == NULL) return (GSS_S_FAILURE); HEIMDAL_MUTEX_init(&handle->cred_id_mutex); handle->usage = cred_usage; if ((cred_usage == GSS_C_INITIATE) || (cred_usage == GSS_C_BOTH)) { struct search s; ret = hx509_certs_init(context->hx509ctx, "KEYCHAIN:", 0, NULL, &certs); if (ret) { *minor_status = ret; goto fail; } ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { *minor_status = ret; goto fail; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); if (name) { s.context = context; s.principal = name; hx509_query_match_cmp_func(q, match_pkinit_san, &s); } ret = _krb5_pk_find_cert(context, 1, certs, q, &handle->cert); hx509_query_free(context->hx509ctx, q); if (ret) { *minor_status = ret; goto fail; } if (name) ret = krb5_copy_principal(context, name, &handle->principal); else ret = _gsspku2u_principal(context, handle->cert, &handle->principal); if (ret) { *minor_status = ret; goto fail; } } if ((cred_usage == GSS_C_ACCEPT) || (cred_usage == GSS_C_BOTH)) { ret = get_keytab(context, handle, 1); if (ret) { *minor_status = ret; goto fail; } } if (certs) hx509_certs_free(&certs); *output_cred_handle = (gss_cred_id_t)handle; return GSS_S_COMPLETE; fail: if (certs) hx509_certs_free(&certs); if (handle->keytab) krb5_kt_close(context, handle->keytab); HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); return GSS_S_FAILURE; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_store_cred(OM_uint32 *minor_status, gss_cred_id_t input_cred_handle, gss_cred_usage_t cred_usage, const gss_OID desired_mech, OM_uint32 overwrite_cred, OM_uint32 default_cred, gss_OID_set *elements_stored, gss_cred_usage_t *cred_usage_stored) { krb5_context context; krb5_error_code ret; gsskrb5_cred cred; krb5_ccache id; int destroy = 0; *minor_status = 0; if (cred_usage != GSS_C_INITIATE) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } if (gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0) return GSS_S_BAD_MECH; cred = (gsskrb5_cred)input_cred_handle; if (cred == NULL) return GSS_S_NO_CRED; GSSAPI_KRB5_INIT (&context); HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); if (cred->usage != cred_usage && cred->usage != GSS_C_BOTH) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = GSS_KRB5_S_G_BAD_USAGE; return(GSS_S_FAILURE); } if (cred->principal == NULL) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = GSS_KRB5_S_KG_TGT_MISSING; return(GSS_S_FAILURE); } /* write out cred to credential cache */ ret = krb5_cc_cache_match(context, cred->principal, &id); if (ret) { ret = krb5_cc_new_unique(context, NULL, NULL, &id); if (ret) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = ret; return(GSS_S_FAILURE); } destroy = 1; } ret = krb5_cc_initialize(context, id, cred->principal); if (ret == 0) ret = krb5_cc_copy_match_f(context, cred->ccache, id, NULL, NULL, NULL); if (ret) { if (destroy) krb5_cc_destroy(context, id); else krb5_cc_close(context, id); HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = ret; return(GSS_S_FAILURE); } if (default_cred) krb5_cc_switch(context, id); krb5_cc_close(context, id); HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = 0; return GSS_S_COMPLETE; }
OM_uint32 _gssiakerb_acquire_cred(OM_uint32 * minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle, gss_OID_set * actual_mechs, OM_uint32 * time_rec) { krb5_principal princ = (krb5_principal)desired_name; OM_uint32 major_status, junk; krb5_context context; krb5_error_code ret; gsskrb5_cred handle; krb5_data data; int iakerb = 0; GSSAPI_KRB5_INIT(&context); *minor_status = 0; *output_cred_handle = NULL; if (cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) return GSS_S_FAILURE; if (princ == NULL) return GSS_S_FAILURE; handle = calloc(1, sizeof(*handle)); if (handle == NULL) return GSS_S_FAILURE; HEIMDAL_MUTEX_init(&handle->cred_id_mutex); major_status = _acquire_uuid_name(minor_status, context, princ, &iakerb, handle); if (major_status) return major_status; if (!iakerb) return GSS_S_BAD_NAME; if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "password", &data)) == 0) { ret = asprintf(&handle->password, "%.*s", (int)data.length, (char *)data.data); memset(data.data, 0, data.length); krb5_data_free(&data); if (ret <= 0 || handle->password == NULL) { _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ENOMEM; return GSS_S_FAILURE; } #ifdef PKINIT } else if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "certificate-ref", &data)) == 0) { hx509_certs certs; hx509_query *q; ret = hx509_certs_init(context->hx509ctx, "KEYCHAIN:", 0, NULL, &certs); if (ret) { krb5_data_free(&data); hx509_certs_free(&certs); _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ret; return GSS_S_FAILURE; } ret = hx509_query_alloc(context->hx509ctx, &q); if (ret) { krb5_data_free(&data); hx509_certs_free(&certs); _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ret; return GSS_S_FAILURE; } hx509_query_match_option(q, HX509_QUERY_OPTION_PRIVATE_KEY); hx509_query_match_option(q, HX509_QUERY_OPTION_KU_DIGITALSIGNATURE); hx509_query_match_persistent(q, &data); ret = _krb5_pk_find_cert(context, 1, certs, q, &handle->cert); krb5_data_free(&data); hx509_certs_free(&certs); hx509_query_free(context->hx509ctx, q); if (ret != 0) { _gss_mg_log(1, "gss-krb5: failed to find certificate ref %d", ret); _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = ret; return GSS_S_FAILURE; } #endif } else if ((ret = krb5_cc_get_config(context, handle->ccache, NULL, "iakerb", &data)) == 0) { handle->cred_flags |= GSS_CF_IAKERB_RESOLVED; krb5_data_free(&data); } else { _gsskrb5_release_cred(&junk, (gss_cred_id_t *)&handle); *minor_status = 0; return GSS_S_FAILURE; } handle->usage = GSS_C_INITIATE; handle->endtime = INT_MAX; *output_cred_handle = (gss_cred_id_t)handle; *minor_status = 0; return GSS_S_COMPLETE; }
OM_uint32 _gsskrb5_inquire_sec_context_by_oid (OM_uint32 *minor_status, const gss_ctx_id_t context_handle, const gss_OID desired_object, gss_buffer_set_t *data_set) { krb5_context context; const gsskrb5_ctx ctx = (const gsskrb5_ctx) context_handle; unsigned suffix; if (ctx == NULL) { *minor_status = EINVAL; return GSS_S_NO_CONTEXT; } GSSAPI_KRB5_INIT (&context); if (gss_oid_equal(desired_object, GSS_KRB5_GET_TKT_FLAGS_X)) { return inquire_sec_context_tkt_flags(minor_status, ctx, data_set); } else if (gss_oid_equal(desired_object, GSS_C_PEER_HAS_UPDATED_SPNEGO)) { return inquire_sec_context_has_updated_spnego(minor_status, ctx, data_set); } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SUBKEY_X)) { return inquire_sec_context_get_subkey(minor_status, ctx, context, TOKEN_KEY, data_set); } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_INITIATOR_SUBKEY_X)) { return inquire_sec_context_get_subkey(minor_status, ctx, context, INITIATOR_KEY, data_set); } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_ACCEPTOR_SUBKEY_X)) { return inquire_sec_context_get_subkey(minor_status, ctx, context, ACCEPTOR_KEY, data_set); } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_AUTHTIME_X)) { return get_authtime(minor_status, ctx, data_set); } else if (oid_prefix_equal(desired_object, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X, &suffix)) { return inquire_sec_context_authz_data(minor_status, ctx, context, suffix, data_set); } else if (oid_prefix_equal(desired_object, GSS_KRB5_EXPORT_LUCID_CONTEXT_X, &suffix)) { if (suffix == 1) return export_lucid_sec_context_v1(minor_status, ctx, context, data_set); *minor_status = 0; return GSS_S_FAILURE; } else if (gss_oid_equal(desired_object, GSS_KRB5_GET_SERVICE_KEYBLOCK_X)) { return get_service_keyblock(minor_status, ctx, data_set); } else { *minor_status = 0; return GSS_S_FAILURE; } }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext (OM_uint32 * minor_status, gss_const_name_t desired_name, gss_const_OID credential_type, const void *credential_data, OM_uint32 time_req, gss_const_OID desired_mech, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle ) { krb5_context context; gsskrb5_cred handle; OM_uint32 ret; cred_usage &= GSS_C_OPTION_MASK; if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } GSSAPI_KRB5_INIT(&context); *output_cred_handle = NULL; handle = calloc(1, sizeof(*handle)); if (handle == NULL) { *minor_status = ENOMEM; return (GSS_S_FAILURE); } HEIMDAL_MUTEX_init(&handle->cred_id_mutex); if (desired_name != GSS_C_NO_NAME) { ret = _gsskrb5_canon_name(minor_status, context, 1, NULL, desired_name, &handle->principal); if (ret) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); return ret; } } if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { ret = acquire_initiator_cred(minor_status, context, credential_type, credential_data, desired_name, time_req, desired_mech, cred_usage, handle); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } } if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { ret = acquire_acceptor_cred(minor_status, context, credential_type, credential_data, desired_name, time_req, desired_mech, cred_usage, handle); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } } 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) { if (handle->mechanisms != NULL) gss_release_oid_set(NULL, &handle->mechanisms); HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } handle->usage = cred_usage; *minor_status = 0; *output_cred_handle = (gss_cred_id_t)handle; return (GSS_S_COMPLETE); }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_add_cred ( OM_uint32 *minor_status, const gss_cred_id_t input_cred_handle, const gss_name_t desired_name, const gss_OID desired_mech, gss_cred_usage_t cred_usage, OM_uint32 initiator_time_req, OM_uint32 acceptor_time_req, gss_cred_id_t *output_cred_handle, gss_OID_set *actual_mechs, OM_uint32 *initiator_time_rec, OM_uint32 *acceptor_time_rec) { krb5_context context; OM_uint32 ret, lifetime; gsskrb5_cred cred, handle; krb5_const_principal dname; handle = NULL; cred = (gsskrb5_cred)input_cred_handle; dname = (krb5_const_principal)desired_name; GSSAPI_KRB5_INIT (&context); if (gss_oid_equal(desired_mech, GSS_KRB5_MECHANISM) == 0) { *minor_status = 0; return GSS_S_BAD_MECH; } if (cred == NULL && output_cred_handle == NULL) { *minor_status = 0; return GSS_S_NO_CRED; } if (cred == NULL) { /* XXX standard conformance failure */ *minor_status = 0; return GSS_S_NO_CRED; } /* check if requested output usage is compatible with output usage */ if (output_cred_handle != NULL) { HEIMDAL_MUTEX_lock(&cred->cred_id_mutex); if (cred->usage != cred_usage && cred->usage != GSS_C_BOTH) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = GSS_KRB5_S_G_BAD_USAGE; return(GSS_S_FAILURE); } } /* check that we have the same name */ if (dname != NULL && krb5_principal_compare(context, dname, cred->principal) != FALSE) { if (output_cred_handle) HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = 0; return GSS_S_BAD_NAME; } /* make a copy */ if (output_cred_handle) { krb5_error_code kret; handle = calloc(1, sizeof(*handle)); if (handle == NULL) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); *minor_status = ENOMEM; return (GSS_S_FAILURE); } handle->usage = cred_usage; handle->endtime = cred->endtime; handle->principal = NULL; handle->keytab = NULL; handle->ccache = NULL; HEIMDAL_MUTEX_init(&handle->cred_id_mutex); kret = krb5_copy_principal(context, cred->principal, &handle->principal); if (kret) { HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); free(handle); *minor_status = kret; return GSS_S_FAILURE; } if (cred->keytab) { char *name = NULL; ret = GSS_S_FAILURE; kret = krb5_kt_get_full_name(context, cred->keytab, &name); if (kret) { *minor_status = kret; goto failure; } kret = krb5_kt_resolve(context, name, &handle->keytab); krb5_xfree(name); if (kret){ *minor_status = kret; goto failure; } } if (cred->ccache) { const char *type, *name; char *type_name = NULL; ret = GSS_S_FAILURE; type = krb5_cc_get_type(context, cred->ccache); if (type == NULL){ *minor_status = ENOMEM; goto failure; } if (strcmp(type, "MEMORY") == 0) { ret = krb5_cc_new_unique(context, type, NULL, &handle->ccache); if (ret) { *minor_status = ret; goto failure; } ret = krb5_cc_copy_cache(context, cred->ccache, handle->ccache); if (ret) { *minor_status = ret; goto failure; } } else { name = krb5_cc_get_name(context, cred->ccache); if (name == NULL) { *minor_status = ENOMEM; goto failure; } kret = asprintf(&type_name, "%s:%s", type, name); if (kret < 0 || type_name == NULL) { *minor_status = ENOMEM; goto failure; } kret = krb5_cc_resolve(context, type_name, &handle->ccache); free(type_name); if (kret) { *minor_status = kret; goto failure; } } } } HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); ret = _gsskrb5_inquire_cred(minor_status, (gss_cred_id_t)cred, NULL, &lifetime, NULL, actual_mechs); if (ret) goto failure; if (initiator_time_rec) *initiator_time_rec = lifetime; if (acceptor_time_rec) *acceptor_time_rec = lifetime; if (output_cred_handle) { *output_cred_handle = (gss_cred_id_t)handle; } *minor_status = 0; return ret; failure: if (handle) { if (handle->principal) krb5_free_principal(context, handle->principal); if (handle->keytab) krb5_kt_close(context, handle->keytab); if (handle->ccache) krb5_cc_destroy(context, handle->ccache); free(handle); } if (output_cred_handle) HEIMDAL_MUTEX_unlock(&cred->cred_id_mutex); return ret; }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_accept_sec_context(OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_cred_id_t acceptor_cred_handle, const gss_buffer_t input_token_buffer, const gss_channel_bindings_t input_chan_bindings, gss_name_t * src_name, gss_OID * mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec, gss_cred_id_t * delegated_cred_handle) { krb5_context context; OM_uint32 ret; gsskrb5_ctx ctx; GSSAPI_KRB5_INIT(&context); output_token->length = 0; output_token->value = NULL; if (src_name != NULL) *src_name = NULL; if (mech_type) *mech_type = GSS_KRB5_MECHANISM; if (*context_handle == GSS_C_NO_CONTEXT) { ret = _gsskrb5_create_ctx(minor_status, context_handle, context, input_chan_bindings, ACCEPTOR_START); if (ret) return ret; } ctx = (gsskrb5_ctx)*context_handle; /* * TODO: check the channel_bindings * (above just sets them to krb5 layer) */ HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); switch (ctx->state) { case ACCEPTOR_START: ret = gsskrb5_acceptor_start(minor_status, ctx, context, acceptor_cred_handle, input_token_buffer, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); break; case ACCEPTOR_WAIT_FOR_DCESTYLE: ret = acceptor_wait_for_dcestyle(minor_status, ctx, context, acceptor_cred_handle, input_token_buffer, input_chan_bindings, src_name, mech_type, output_token, ret_flags, time_rec, delegated_cred_handle); break; case ACCEPTOR_READY: /* * If we get there, the caller have called * gss_accept_sec_context() one time too many. */ ret = GSS_S_BAD_STATUS; break; default: /* TODO: is this correct here? --metze */ ret = GSS_S_BAD_STATUS; break; } HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); if (GSS_ERROR(ret)) { OM_uint32 min2; _gsskrb5_delete_sec_context(&min2, context_handle, GSS_C_NO_BUFFER); } return ret; }
OM_uint32 gss_acquire_cred (OM_uint32 * minor_status, const gss_name_t desired_name, OM_uint32 time_req, const gss_OID_set desired_mechs, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle, gss_OID_set * actual_mechs, OM_uint32 * time_rec ) { gss_cred_id_t handle; OM_uint32 ret; if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } GSSAPI_KRB5_INIT (); *output_cred_handle = NULL; if (time_rec) *time_rec = 0; if (actual_mechs) *actual_mechs = GSS_C_NO_OID_SET; if (desired_mechs) { int present = 0; ret = gss_test_oid_set_member(minor_status, GSS_KRB5_MECHANISM, desired_mechs, &present); if (ret) return ret; if (!present) { *minor_status = 0; return GSS_S_BAD_MECH; } } handle = (gss_cred_id_t)malloc(sizeof(*handle)); if (handle == GSS_C_NO_CREDENTIAL) { *minor_status = ENOMEM; return (GSS_S_FAILURE); } memset(handle, 0, sizeof (*handle)); HEIMDAL_MUTEX_init(&handle->cred_id_mutex); if (desired_name != GSS_C_NO_NAME) { ret = gss_duplicate_name(minor_status, desired_name, &handle->principal); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); return (ret); } } if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { ret = acquire_initiator_cred(minor_status, desired_name, time_req, desired_mechs, cred_usage, handle, actual_mechs, time_rec); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(gssapi_krb5_context, handle->principal); free(handle); return (ret); } } if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { ret = acquire_acceptor_cred(minor_status, desired_name, time_req, desired_mechs, cred_usage, handle, actual_mechs, time_rec); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(gssapi_krb5_context, handle->principal); free(handle); return (ret); } } 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) ret = gss_inquire_cred(minor_status, handle, NULL, time_rec, NULL, actual_mechs); if (ret != GSS_S_COMPLETE) { if (handle->mechanisms != NULL) gss_release_oid_set(NULL, &handle->mechanisms); HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(gssapi_krb5_context, handle->principal); free(handle); return (ret); } *minor_status = 0; if (time_rec) { ret = gssapi_lifetime_left(minor_status, handle->lifetime, time_rec); if (ret) return ret; } handle->usage = cred_usage; *output_cred_handle = handle; return (GSS_S_COMPLETE); }
OM_uint32 GSSAPI_CALLCONV _gsskrb5_acquire_cred_ext (OM_uint32 * minor_status, gss_const_name_t desired_name, gss_const_OID credential_type, const void *credential_data, OM_uint32 time_req, gss_const_OID desired_mech, gss_cred_usage_t cred_usage, gss_cred_id_t * output_cred_handle ) { krb5_context context; gsskrb5_cred handle; OM_uint32 ret; cred_usage &= GSS_C_OPTION_MASK; if (cred_usage != GSS_C_ACCEPT && cred_usage != GSS_C_INITIATE && cred_usage != GSS_C_BOTH) { *minor_status = GSS_KRB5_S_G_BAD_USAGE; return GSS_S_FAILURE; } GSSAPI_KRB5_INIT(&context); *output_cred_handle = GSS_C_NO_CREDENTIAL; handle = calloc(1, sizeof(*handle)); if (handle == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } HEIMDAL_MUTEX_init(&handle->cred_id_mutex); if (desired_name != GSS_C_NO_NAME) { ret = _gsskrb5_canon_name(minor_status, context, desired_name, &handle->principal); if (ret) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); return ret; } } if (credential_type != GSS_C_NO_OID && gss_oid_equal(credential_type, GSS_C_CRED_PASSWORD)) { /* Acquire a cred with a password */ gss_const_buffer_t pwbuf = credential_data; char *pw; if (pwbuf == NULL) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); *minor_status = KRB5_NOCREDS_SUPPLIED; /* see below */ return GSS_S_CALL_INACCESSIBLE_READ; } /* NUL-terminate the password, if it wasn't already */ pw = strndup(pwbuf->value, pwbuf->length); if (pw == NULL) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); *minor_status = krb5_enomem(context); return GSS_S_CALL_INACCESSIBLE_READ; } ret = acquire_cred_with_password(minor_status, context, pw, time_req, desired_mech, cred_usage, handle); free(pw); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } } else if (credential_type != GSS_C_NO_OID) { /* * _gss_acquire_cred_ext() called with something other than a password. * * Not supported. * * _gss_acquire_cred_ext() is not a supported public interface, so * we don't have to try too hard as to minor status codes here. */ HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); free(handle); *minor_status = ENOTSUP; return GSS_S_FAILURE; } else { /* * Acquire a credential from the background credential store (ccache, * keytab). */ if (cred_usage == GSS_C_INITIATE || cred_usage == GSS_C_BOTH) { ret = acquire_initiator_cred(minor_status, context, time_req, desired_mech, cred_usage, handle); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } } if (cred_usage == GSS_C_ACCEPT || cred_usage == GSS_C_BOTH) { ret = acquire_acceptor_cred(minor_status, context, time_req, desired_mech, cred_usage, handle); if (ret != GSS_S_COMPLETE) { HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } } } 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) { if (handle->mechanisms != NULL) gss_release_oid_set(NULL, &handle->mechanisms); HEIMDAL_MUTEX_destroy(&handle->cred_id_mutex); krb5_free_principal(context, handle->principal); free(handle); return (ret); } handle->usage = cred_usage; *minor_status = 0; *output_cred_handle = (gss_cred_id_t)handle; return (GSS_S_COMPLETE); }
static OM_uint32 import_hostbased_name (OM_uint32 *minor_status, gss_const_OID mech, const gss_buffer_t input_name_buffer, gss_const_OID input_name_type, gss_name_t *output_name) { krb5_context context; krb5_principal princ = NULL; krb5_error_code kerr; char *tmp, *p, *host = NULL, *realm = NULL; if (gss_oid_equal(mech, GSS_PKU2U_MECHANISM)) realm = KRB5_PKU2U_REALM_NAME; else realm = KRB5_GSS_REFERALS_REALM_NAME; /* should never hit the network */ GSSAPI_KRB5_INIT (&context); tmp = malloc (input_name_buffer->length + 1); if (tmp == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy (tmp, input_name_buffer->value, input_name_buffer->length); tmp[input_name_buffer->length] = '\0'; p = strchr (tmp, '@'); if (p != NULL && p[1] != '\0') { size_t len; *p = '\0'; host = p + 1; /* * Squash any trailing . on the hostname since that is jolly * good to have when looking up a DNS name (qualified), but * its no good to have in the kerberos principal since those * are supposed to be in qualified format already. */ len = strlen(host); if (len > 0 && host[len - 1] == '.') host[len - 1] = '\0'; } else { host = KRB5_GSS_HOSTBASED_SERVICE_NAME; } kerr = krb5_make_principal(context, &princ, realm, tmp, host, NULL); free (tmp); *minor_status = kerr; if (kerr == KRB5_PARSE_ILLCHAR || kerr == KRB5_PARSE_MALFORMED) return GSS_S_BAD_NAME; else if (kerr) return GSS_S_FAILURE; krb5_principal_set_type(context, princ, KRB5_NT_GSS_HOSTBASED_SERVICE); *output_name = (gss_name_t)princ; return 0; }