uint32_t sapgss_inquire_cred_by_mech( uint32_t *minor_status, gss_cred_id_t cred_handle, sapgss_OID mech_type, gss_name_t *name, uint32_t *initiator_lifetime, uint32_t *acceptor_lifetime, gss_cred_usage_t *cred_usage) { gss_OID mech_type_loc; uint32_t major_status; int ret; ret = gss_OID_sap_to_loc(mech_type, &mech_type_loc); if (ret != 0) { *minor_status = ret; return GSS_S_FAILURE; } major_status = gss_inquire_cred_by_mech(minor_status, cred_handle, mech_type_loc, name, initiator_lifetime, acceptor_lifetime, cred_usage); /* Comply with the gss_OID_sap_to_loc contract and free mech_type_loc */ gss_OID_loc_release(&mech_type_loc); return major_status; }
OM_uint32 _gss_spnego_inquire_cred_by_mech ( OM_uint32 * minor_status, const gss_cred_id_t cred_handle, const gss_OID mech_type, gss_name_t * name, OM_uint32 * initiator_lifetime, OM_uint32 * acceptor_lifetime, gss_cred_usage_t * cred_usage ) { gssspnego_cred cred; spnego_name sname = NULL; OM_uint32 ret; if (cred_handle == GSS_C_NO_CREDENTIAL) { *minor_status = 0; return GSS_S_NO_CRED; } if (name) { sname = calloc(1, sizeof(*sname)); if (sname == NULL) { *minor_status = ENOMEM; return GSS_S_FAILURE; } } cred = (gssspnego_cred)cred_handle; ret = gss_inquire_cred_by_mech(minor_status, cred->negotiated_cred_id, mech_type, sname ? &sname->mech : NULL, initiator_lifetime, acceptor_lifetime, cred_usage); if (ret) { if (sname) free(sname); return ret; } if (name) *name = (gss_name_t)sname; return GSS_S_COMPLETE; }
uint32_t gp_export_gssx_cred(uint32_t *min, struct gp_call_ctx *gpcall, gss_cred_id_t *in, gssx_cred *out) { uint32_t ret_maj; uint32_t ret_min; gss_name_t name = NULL; uint32_t lifetime; gss_cred_usage_t cred_usage; gss_OID_set mechanisms = NULL; uint32_t initiator_lifetime; uint32_t acceptor_lifetime; struct gssx_cred_element *el; int ret; int i, j; struct gp_creds_handle *handle = NULL; gss_buffer_desc token = GSS_C_EMPTY_BUFFER; ret_maj = gss_inquire_cred(&ret_min, *in, &name, &lifetime, &cred_usage, &mechanisms); if (ret_maj) { goto done; } ret_maj = gp_conv_name_to_gssx(&ret_min, name, &out->desired_name); if (ret_maj) { goto done; } gss_release_name(&ret_min, &name); name = NULL; out->elements.elements_len = mechanisms->count; out->elements.elements_val = calloc(out->elements.elements_len, sizeof(gssx_cred_element)); if (!out->elements.elements_val) { ret_maj = GSS_S_FAILURE; ret_min = ENOMEM; goto done; } for (i = 0, j = 0; i < mechanisms->count; i++, j++) { el = &out->elements.elements_val[j]; ret_maj = gss_inquire_cred_by_mech(&ret_min, *in, &mechanisms->elements[i], &name, &initiator_lifetime, &acceptor_lifetime, &cred_usage); if (ret_maj) { gp_log_failure(&mechanisms->elements[i], ret_maj, ret_min); /* temporarily skip any offender */ out->elements.elements_len--; j--; continue; #if 0 ret = EINVAL; goto done; #endif } ret_maj = gp_conv_name_to_gssx(&ret_min, name, &el->MN); if (ret_maj) { goto done; } gss_release_name(&ret_min, &name); name = NULL; ret = gp_conv_oid_to_gssx(&mechanisms->elements[i], &el->mech); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } el->cred_usage = gp_conv_gssx_to_cred_usage(cred_usage); el->initiator_time_rec = initiator_lifetime; el->acceptor_time_rec = acceptor_lifetime; } handle = gp_service_get_creds_handle(gpcall->service); if (!handle) { ret_maj = GSS_S_FAILURE; ret_min = EINVAL; goto done; } ret_maj = gss_export_cred(&ret_min, *in, &token); if (ret_maj) { goto done; } ret = gp_encrypt_buffer(handle->context, &handle->key, token.length, token.value, &out->cred_handle_reference); if (ret) { ret_maj = GSS_S_FAILURE; ret_min = ret; goto done; } out->needs_release = false; /* now we have serialized creds in the hands of the client. * we can safey free them here so that we can remain sateless and * not leak memory */ gss_release_cred(&ret_min, in); ret_maj = GSS_S_COMPLETE; ret_min = 0; done: *min = ret_min; gss_release_name(&ret_min, &name); gss_release_oid_set(&ret_min, &mechanisms); return ret_maj; }
OM_uint32 gssi_inquire_cred_by_mech(OM_uint32 *minor_status, gss_cred_id_t cred_handle, gss_OID mech_type, gss_name_t *name, OM_uint32 *initiator_lifetime, OM_uint32 *acceptor_lifetime, gss_cred_usage_t *cred_usage) { struct gpp_cred_handle *cred = NULL; struct gpp_name_handle *gpname = NULL; OM_uint32 maj, min; GSSI_TRACE(); if (cred_handle == GSS_C_NO_CREDENTIAL) { maj = gppint_get_def_creds(&min, gpp_get_behavior(), NULL, GSS_C_INITIATE, &cred); if (maj != GSS_S_COMPLETE) { goto done; } } else { cred = (struct gpp_cred_handle *)cred_handle; } if (name) { gpname = calloc(1, sizeof(struct gpp_name_handle)); if (!gpname) { min = ENOMEM; maj = GSS_S_FAILURE; goto done; } } if (cred->local) { maj = gss_inquire_cred_by_mech(&min, cred->local, gpp_special_mech(mech_type), gpname ? &gpname->local : NULL, initiator_lifetime, acceptor_lifetime, cred_usage); } else if (cred->remote) { maj = gpm_inquire_cred_by_mech(&min, cred->remote, gpp_unspecial_mech(mech_type), gpname ? &gpname->remote : NULL, initiator_lifetime, acceptor_lifetime, cred_usage); } else { min = 0; maj = GSS_S_FAILURE; } done: *minor_status = gpp_map_error(min); if (cred_handle == GSS_C_NO_CREDENTIAL) { gssi_release_cred(&min, (gss_cred_id_t*)&cred); } if (name && maj == GSS_S_COMPLETE) { *name = (gss_name_t)gpname; } else { free(gpname); } return maj; }
/* Privileged (called from accept_secure_ctx) */ OM_uint32 ssh_gssapi_getclient(Gssctxt *ctx, ssh_gssapi_client *client) { int i = 0; int equal = 0; gss_name_t new_name = GSS_C_NO_NAME; gss_buffer_desc ename = GSS_C_EMPTY_BUFFER; if (options.gss_store_rekey && client->used && ctx->client_creds) { if (client->mech->oid.length != ctx->oid->length || (memcmp(client->mech->oid.elements, ctx->oid->elements, ctx->oid->length) !=0)) { debug("Rekeyed credentials have different mechanism"); return GSS_S_COMPLETE; } if ((ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ctx->client_creds, ctx->oid, &new_name, NULL, NULL, NULL))) { ssh_gssapi_error(ctx); return (ctx->major); } ctx->major = gss_compare_name(&ctx->minor, client->name, new_name, &equal); if (GSS_ERROR(ctx->major)) { ssh_gssapi_error(ctx); return (ctx->major); } if (!equal) { debug("Rekeyed credentials have different name"); return GSS_S_COMPLETE; } debug("Marking rekeyed credentials for export"); gss_release_name(&ctx->minor, &client->name); gss_release_cred(&ctx->minor, &client->creds); client->name = new_name; client->creds = ctx->client_creds; ctx->client_creds = GSS_C_NO_CREDENTIAL; client->updated = 1; return GSS_S_COMPLETE; } client->mech = NULL; while (supported_mechs[i]->name != NULL) { if (supported_mechs[i]->oid.length == ctx->oid->length && (memcmp(supported_mechs[i]->oid.elements, ctx->oid->elements, ctx->oid->length) == 0)) client->mech = supported_mechs[i]; i++; } if (client->mech == NULL) return GSS_S_FAILURE; if (ctx->client_creds && (ctx->major = gss_inquire_cred_by_mech(&ctx->minor, ctx->client_creds, ctx->oid, &client->name, NULL, NULL, NULL))) { ssh_gssapi_error(ctx); return (ctx->major); } if ((ctx->major = gss_display_name(&ctx->minor, ctx->client, &client->displayname, NULL))) { ssh_gssapi_error(ctx); return (ctx->major); } if ((ctx->major = gss_export_name(&ctx->minor, ctx->client, &ename))) { ssh_gssapi_error(ctx); return (ctx->major); } if ((ctx->major = ssh_gssapi_parse_ename(ctx,&ename, &client->exportedname))) { return (ctx->major); } gss_release_buffer(&ctx->minor, &ename); /* We can't copy this structure, so we just move the pointer to it */ client->creds = ctx->client_creds; ctx->client_creds = GSS_C_NO_CREDENTIAL; return (ctx->major); }
/** @brief returns the OIDs of the mechs that have usable credentials */ static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids) { OM_uint32 maj_stat, min_stat, lifetime; gss_OID_set actual_mechs; gss_buffer_desc namebuf; gss_name_t client_id = GSS_C_NO_NAME; gss_OID oid; unsigned int i; char *ptr; int ret; if (session->gssapi->client.client_deleg_creds == NULL) { if (session->opts.gss_client_identity != NULL) { namebuf.value = (void *)session->opts.gss_client_identity; namebuf.length = strlen(session->opts.gss_client_identity); maj_stat = gss_import_name(&min_stat, &namebuf, GSS_C_NT_USER_NAME, &client_id); if (GSS_ERROR(maj_stat)) { ret = SSH_ERROR; goto end; } } maj_stat = gss_acquire_cred(&min_stat, client_id, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &session->gssapi->client.creds, &actual_mechs, NULL); if (GSS_ERROR(maj_stat)) { ret = SSH_ERROR; goto end; } } else { session->gssapi->client.creds = session->gssapi->client.client_deleg_creds; maj_stat = gss_inquire_cred(&min_stat, session->gssapi->client.creds, &client_id, NULL, NULL, &actual_mechs); if (GSS_ERROR(maj_stat)) { ret = SSH_ERROR; goto end; } } gss_create_empty_oid_set(&min_stat, valid_oids); /* double check each single cred */ for (i = 0; i < actual_mechs->count; i++) { /* check lifetime is not 0 or skip */ lifetime = 0; oid = &actual_mechs->elements[i]; maj_stat = gss_inquire_cred_by_mech(&min_stat, session->gssapi->client.creds, oid, NULL, &lifetime, NULL, NULL); if (maj_stat == GSS_S_COMPLETE && lifetime > 0) { gss_add_oid_set_member(&min_stat, oid, valid_oids); ptr = ssh_get_hexa(oid->elements, oid->length); SSH_LOG(SSH_LOG_DEBUG, "GSSAPI valid oid %d : %s\n", i, ptr); SAFE_FREE(ptr); } } ret = SSH_OK; end: gss_release_name(&min_stat, &client_id); return ret; }
static bool mag_auth_basic(request_rec *req, struct mag_config *cfg, gss_buffer_desc ba_user, gss_buffer_desc ba_pwd, gss_name_t *client, gss_OID *mech_type, gss_cred_id_t *delegated_cred, uint32_t *vtime) { #ifdef HAVE_GSS_KRB5_CCACHE_NAME const char *user_ccache = NULL; const char *orig_ccache = NULL; long long unsigned int rndname; apr_status_t rs; #endif gss_name_t user = GSS_C_NO_NAME; gss_cred_id_t user_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t user_ctx = GSS_C_NO_CONTEXT; gss_name_t server = GSS_C_NO_NAME; gss_cred_id_t server_cred = GSS_C_NO_CREDENTIAL; gss_ctx_id_t server_ctx = GSS_C_NO_CONTEXT; gss_buffer_desc input = GSS_C_EMPTY_BUFFER; gss_buffer_desc output = GSS_C_EMPTY_BUFFER; gss_OID_set allowed_mechs; gss_OID_set filtered_mechs; gss_OID_set actual_mechs = GSS_C_NO_OID_SET; uint32_t init_flags = 0; uint32_t maj, min; int present = 0; bool ret = false; maj = gss_import_name(&min, &ba_user, GSS_C_NT_USER_NAME, &user); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_import_name() failed", maj, min)); goto done; } if (cfg->basic_mechs) { allowed_mechs = cfg->basic_mechs; } else if (cfg->allowed_mechs) { allowed_mechs = cfg->allowed_mechs; } else { struct mag_server_config *scfg; /* Try to fetch the default set if not explicitly configured, * We need to do this because gss_acquire_cred_with_password() * is currently limited to acquire creds for a single "default" * mechanism if no desired mechanisms are passed in. This causes * authentication to fail for secondary mechanisms as no user * credentials are generated for those. */ scfg = ap_get_module_config(req->server->module_config, &auth_gssapi_module); /* In the worst case scenario default_mechs equals to GSS_C_NO_OID_SET. * This generally causes only the krb5 mechanism to be tried due * to implementation constraints, but may change in future. */ allowed_mechs = scfg->default_mechs; } /* Remove Spnego if present, or we'd repeat failed authentiations * multiple times, one within Spnego and then again with an explicit * mechanism. We would normally just force Spnego and use * gss_set_neg_mechs, but due to the way we source the server name * and the fact MIT up to 1.14 at least does no handle union names, * we can't provide spnego with a server name that can be used by * multiple mechanisms, causing any but the first mechanism to fail. * Also remove unwanted krb mechs, or AS requests will be repeated * multiple times uselessly. */ filtered_mechs = mag_filter_unwanted_mechs(allowed_mechs); if (filtered_mechs == allowed_mechs) { /* in case filtered_mechs was not allocated here don't free it */ filtered_mechs = GSS_C_NO_OID_SET; } else if (filtered_mechs == GSS_C_NO_OID_SET) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, req, "Fatal " "failure while filtering mechs, aborting"); goto done; } else { /* use the filtered list */ allowed_mechs = filtered_mechs; } #ifdef HAVE_GSS_KRB5_CCACHE_NAME /* If we are using the krb5 mechanism make sure to set a per thread * memory ccache so that there can't be interferences between threads. * Also make sure we have new cache so no cached results end up being * used. Some implementations of gss_acquire_cred_with_password() do * not reacquire creds if cached ones are around, failing to check * again for the password. */ maj = gss_test_oid_set_member(&min, discard_const(gss_mech_krb5), allowed_mechs, &present); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_test_oid_set_member() failed", maj, min)); goto done; } if (present) { rs = apr_generate_random_bytes((unsigned char *)(&rndname), sizeof(long long unsigned int)); if (rs != APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "Failed to generate random ccache name"); goto done; } user_ccache = apr_psprintf(req->pool, "MEMORY:user_%qu", rndname); maj = gss_krb5_ccache_name(&min, user_ccache, &orig_ccache); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_krb5_ccache_name() " "failed", maj, min)); goto done; } } #endif maj = gss_acquire_cred_with_password(&min, user, &ba_pwd, GSS_C_INDEFINITE, allowed_mechs, GSS_C_INITIATE, &user_cred, &actual_mechs, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "In Basic Auth, %s", mag_error(req, "gss_acquire_cred_with_password() " "failed", maj, min)); goto done; } /* must acquire creds based on the actual mechs we want to try */ if (!mag_acquire_creds(req, cfg, actual_mechs, GSS_C_ACCEPT, &server_cred, NULL)) { goto done; } #ifdef HAVE_CRED_STORE if (cfg->deleg_ccache_dir) { /* delegate ourselves credentials so we store them as requested */ init_flags |= GSS_C_DELEG_FLAG; } #endif for (int i = 0; i < actual_mechs->count; i++) { /* free these if looping */ gss_release_buffer(&min, &output); gss_release_buffer(&min, &input); gss_release_name(&min, &server); maj = gss_inquire_cred_by_mech(&min, server_cred, &actual_mechs->elements[i], &server, NULL, NULL, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_inquired_cred_by_mech() " "failed", maj, min)); continue; } do { /* output and input are inverted here, this is intentional */ maj = gss_init_sec_context(&min, user_cred, &user_ctx, server, &actual_mechs->elements[i], init_flags, 300, GSS_C_NO_CHANNEL_BINDINGS, &output, NULL, &input, NULL, NULL); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_init_sec_context() " "failed", maj, min)); break; } gss_release_buffer(&min, &output); maj = gss_accept_sec_context(&min, &server_ctx, server_cred, &input, GSS_C_NO_CHANNEL_BINDINGS, client, mech_type, &output, NULL, vtime, delegated_cred); if (GSS_ERROR(maj)) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "%s", mag_error(req, "gss_accept_sec_context()" " failed", maj, min)); break; } gss_release_buffer(&min, &input); } while (maj == GSS_S_CONTINUE_NEEDED); if (maj == GSS_S_COMPLETE) { ret = true; break; } } done: gss_release_buffer(&min, &output); gss_release_buffer(&min, &input); gss_release_name(&min, &server); gss_delete_sec_context(&min, &server_ctx, GSS_C_NO_BUFFER); gss_release_cred(&min, &server_cred); gss_release_name(&min, &user); gss_release_cred(&min, &user_cred); gss_delete_sec_context(&min, &user_ctx, GSS_C_NO_BUFFER); gss_release_oid_set(&min, &actual_mechs); gss_release_oid_set(&min, &filtered_mechs); #ifdef HAVE_GSS_KRB5_CCACHE_NAME if (user_ccache != NULL) { maj = gss_krb5_ccache_name(&min, orig_ccache, NULL); if (maj != GSS_S_COMPLETE) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, req, "Failed to restore per-thread ccache, %s", mag_error(req, "gss_krb5_ccache_name() " "failed", maj, min)); } } #endif return ret; }