static int input_gssapi_token(int type, u_int32_t plen, struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 maj_status, min_status, flags; u_char *p; size_t len; int r; if (authctxt == NULL || (authctxt->methoddata == NULL && !use_privsep)) fatal("No authentication or GSSAPI context"); gssctxt = authctxt->methoddata; if ((r = sshpkt_get_string(ssh, &p, &len)) != 0 || (r = sshpkt_get_end(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); recv_tok.value = p; recv_tok.length = len; maj_status = PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok, &flags)); xfree(p); if (GSS_ERROR(maj_status)) { if (send_tok.length != 0) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK)) != 0 || (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } authctxt->postponed = 0; ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); userauth_finish(ssh, 0, "gssapi-with-mic", NULL); } else { if (send_tok.length != 0) { if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN)) != 0 || (r = sshpkt_put_string(ssh, send_tok.value, send_tok.length)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } if (maj_status == GSS_S_COMPLETE) { ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); if (flags & GSS_C_INTEG_FLAG) ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_MIC, &input_gssapi_mic); else ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, &input_gssapi_exchange_complete); } } gss_release_buffer(&min_status, &send_tok); return 0; }
static OM_uint32 spnego_initial (OM_uint32 * minor_status, gssspnego_cred cred, 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 ) { NegTokenInit ni; int ret; OM_uint32 sub, minor; gss_buffer_desc mech_token; u_char *buf; size_t buf_size, buf_len; gss_buffer_desc data; size_t ni_len; gss_ctx_id_t context; gssspnego_ctx ctx; memset (&ni, 0, sizeof(ni)); *context_handle = GSS_C_NO_CONTEXT; *minor_status = 0; sub = _gss_spnego_alloc_sec_context(&minor, &context); if (GSS_ERROR(sub)) { *minor_status = minor; return sub; } ctx = (gssspnego_ctx)context; HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); ctx->local = 1; sub = _gss_spnego_indicate_mechtypelist(&minor, 0, cred, &ni.mechTypes, &ctx->preferred_mech_type); if (GSS_ERROR(sub)) { *minor_status = minor; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } ni.reqFlags = NULL; /* * If we have a credential handle, use it to select the mechanism * that we will use */ /* generate optimistic token */ sub = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, target_name, GSS_C_NO_OID, req_flags, time_req, input_chan_bindings, input_token, &ctx->negotiated_mech_type, &mech_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(sub)) { free_NegTokenInit(&ni); *minor_status = minor; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } if (mech_token.length != 0) { ALLOC(ni.mechToken, 1); if (ni.mechToken == NULL) { free_NegTokenInit(&ni); gss_release_buffer(&minor, &mech_token); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); *minor_status = ENOMEM; return GSS_S_FAILURE; } ni.mechToken->length = mech_token.length; ni.mechToken->data = malloc(mech_token.length); if (ni.mechToken->data == NULL && mech_token.length != 0) { free_NegTokenInit(&ni); gss_release_buffer(&minor, &mech_token); *minor_status = ENOMEM; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } memcpy(ni.mechToken->data, mech_token.value, mech_token.length); gss_release_buffer(&minor, &mech_token); } else ni.mechToken = NULL; ni.mechListMIC = NULL; ni_len = length_NegTokenInit(&ni); buf_size = 1 + der_length_len(ni_len) + ni_len; buf = malloc(buf_size); if (buf == NULL) { free_NegTokenInit(&ni); *minor_status = ENOMEM; _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } ret = encode_NegTokenInit(buf + buf_size - 1, ni_len, &ni, &buf_len); if (ret == 0 && ni_len != buf_len) abort(); if (ret == 0) { size_t tmp; ret = der_put_length_and_tag(buf + buf_size - buf_len - 1, buf_size - buf_len, buf_len, ASN1_C_CONTEXT, CONS, 0, &tmp); if (ret == 0 && tmp + buf_len != buf_size) abort(); } if (ret) { *minor_status = ret; free(buf); free_NegTokenInit(&ni); _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return GSS_S_FAILURE; } data.value = buf; data.length = buf_size; ctx->initiator_mech_types.len = ni.mechTypes.len; ctx->initiator_mech_types.val = ni.mechTypes.val; ni.mechTypes.len = 0; ni.mechTypes.val = NULL; free_NegTokenInit(&ni); sub = gss_encapsulate_token(&data, GSS_SPNEGO_MECHANISM, output_token); free (buf); if (sub) { _gss_spnego_internal_delete_sec_context(&minor, &context, GSS_C_NO_BUFFER); return sub; } if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); *context_handle = context; return GSS_S_CONTINUE_NEEDED; }
/* * We only support those mechanisms that we know about (ie ones that we know * how to check local user kuserok and the like) */ static int userauth_gssapi(Authctxt *authctxt) { gss_OID_desc goid = {0, NULL}; Gssctxt *ctxt = NULL; int mechs; int present; OM_uint32 ms; u_int len; u_char *doid = NULL; if (!authctxt->valid || authctxt->user == NULL) return (0); mechs = packet_get_int(); if (mechs == 0) { debug("Mechanism negotiation is not supported"); return (0); } do { mechs--; free(doid); present = 0; doid = packet_get_string(&len); if (len > 2 && doid[0] == SSH_GSS_OIDTYPE && doid[1] == len - 2) { goid.elements = doid + 2; goid.length = len - 2; ssh_gssapi_test_oid_supported(&ms, &goid, &present); } else { logit("Badly formed OID received"); } } while (mechs > 0 && !present); if (!present) { free(doid); authctxt->server_caused_failure = 1; return (0); } if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) { if (ctxt != NULL) ssh_gssapi_delete_ctx(&ctxt); free(doid); authctxt->server_caused_failure = 1; return (0); } authctxt->methoddata = (void *)ctxt; packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE); /* Return the OID that we received */ packet_put_string(doid, len); packet_send(); free(doid); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); authctxt->postponed = 1; return (0); }
int gfarmGssInitiateSecurityContext(int fd, const gss_name_t acceptorName, gss_cred_id_t cred, OM_uint32 reqFlag, gss_ctx_id_t *scPtr, OM_uint32 *majStatPtr, OM_uint32 *minStatPtr, gss_name_t *remoteNamePtr) { OM_uint32 majStat; OM_uint32 minStat, minStat2; OM_uint32 retFlag = 0; gss_buffer_desc inputToken = GSS_C_EMPTY_BUFFER; gss_buffer_t itPtr = &inputToken; gss_buffer_desc outputToken = GSS_C_EMPTY_BUFFER; gss_buffer_t otPtr = &outputToken; gss_OID *actualMechType = NULL; OM_uint32 timeRet; int tknStat; static const char diag[] = "gfarmGssInitiateSecurityContext()"; *scPtr = GSS_C_NO_CONTEXT; /* * Implementation specification: * In gfarm, an initiator must reveal own identity to an acceptor. */ if ((reqFlag & GSS_C_ANON_FLAG) == GSS_C_ANON_FLAG) { /* It is a bit safer to deny the request than to silently ignore it */ gflog_auth_error(GFARM_MSG_1000618, "gfarmGssInitiateSecurityContext(): " "GSS_C_ANON_FLAG is not allowed"); majStat = GSS_S_UNAVAILABLE; minStat = GFSL_DEFAULT_MINOR_ERROR; goto Done; } while (1) { gfarm_mutex_lock(&gss_mutex, diag, gssDiag); majStat = gss_init_sec_context(&minStat, cred, scPtr, acceptorName, GSS_C_NO_OID, reqFlag, 0, GSS_C_NO_CHANNEL_BINDINGS, itPtr, actualMechType, otPtr, &retFlag, &timeRet); gfarm_mutex_unlock(&gss_mutex, diag, gssDiag); if (itPtr->length > 0) gss_release_buffer(&minStat2, itPtr); if (otPtr->length > 0) { tknStat = gfarmGssSendToken(fd, otPtr); gss_release_buffer(&minStat2, otPtr); if (tknStat <= 0) { gflog_auth_error(GFARM_MSG_1000619, "gfarmGssInitiateSecurityContext(): " "failed to send response"); majStat = GSS_S_DEFECTIVE_TOKEN|GSS_S_CALL_INACCESSIBLE_WRITE; minStat = GFSL_DEFAULT_MINOR_ERROR; } } if (GSS_ERROR(majStat)) { break; } if (majStat & GSS_S_CONTINUE_NEEDED) { tknStat = gfarmGssReceiveToken(fd, itPtr, GFARM_GSS_TIMEOUT_INFINITE); if (tknStat <= 0) { gflog_auth_error(GFARM_MSG_1000620, "gfarmGssInitiateSecurityContext(): " "failed to receive response"); majStat = GSS_S_DEFECTIVE_TOKEN|GSS_S_CALL_INACCESSIBLE_READ; minStat = GFSL_DEFAULT_MINOR_ERROR; break; } } else { break; } } if (itPtr->length > 0) gss_release_buffer(&minStat2, itPtr); if (majStat == GSS_S_COMPLETE && remoteNamePtr != NULL) { majStat = gss_inquire_context(&minStat, *scPtr, NULL, remoteNamePtr, NULL, NULL, NULL, NULL, NULL); } Done: if (majStatPtr != NULL) *majStatPtr = majStat; if (minStatPtr != NULL) *minStatPtr = minStat; if (majStat != GSS_S_COMPLETE && *scPtr != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minStat2, scPtr, GSS_C_NO_BUFFER); return majStat == GSS_S_COMPLETE ? 1 : -1; }
gss_cred_id_t read_globus_credentials(const std::string& filename) { Arc::Credential cred(filename, "", "", "", "", true); X509* cert = cred.GetCert(); STACK_OF(X509)* cchain = cred.GetCertChain(); EVP_PKEY* key = cred.GetPrivKey(); globus_gsi_cred_handle_t chandle; globus_gsi_cred_handle_init(&chandle, NULL); if(cert) globus_gsi_cred_set_cert(chandle, cert); if(key) globus_gsi_cred_set_key(chandle, key); if(cchain) globus_gsi_cred_set_cert_chain(chandle, cchain); gss_cred_id_desc* ccred = (gss_cred_id_desc*)::malloc(sizeof(gss_cred_id_desc)); if(ccred) { ::memset(ccred,0,sizeof(gss_cred_id_desc)); ccred->cred_handle = chandle; chandle = NULL; // cred_usage // ssl_context X509* identity_cert = NULL; if(cert) { globus_gsi_cert_utils_cert_type_t ctype = GLOBUS_GSI_CERT_UTILS_TYPE_DEFAULT; globus_gsi_cert_utils_get_cert_type(cert,&ctype); if(ctype == GLOBUS_GSI_CERT_UTILS_TYPE_EEC) { identity_cert = cert; }; }; if(!identity_cert && cchain) { // For compatibility with older globus not using //globus_gsi_cert_utils_get_identity_cert(cchain,&identity_cert); for(int n = 0; n < sk_X509_num(cchain); ++n) { X509* tmp_cert = sk_X509_value(cchain, n); if(tmp_cert) { globus_gsi_cert_utils_cert_type_t ctype = GLOBUS_GSI_CERT_UTILS_TYPE_DEFAULT; globus_gsi_cert_utils_get_cert_type(tmp_cert,&ctype); if(ctype == GLOBUS_GSI_CERT_UTILS_TYPE_EEC) { identity_cert = tmp_cert; break; }; }; }; }; gss_buffer_desc peer_buffer; peer_buffer.value = identity_cert; peer_buffer.length = identity_cert?sizeof(X509):0; OM_uint32 majstat, minstat; majstat = gss_import_name(&minstat, &peer_buffer, identity_cert?GLOBUS_GSS_C_NT_X509:GSS_C_NT_ANONYMOUS, &ccred->globusid); if (GSS_ERROR(majstat)) { logger.msg(Arc::ERROR, "Failed to convert GSI credential to " "GSS credential (major: %d, minor: %d)", majstat, minstat); majstat = gss_release_cred(&minstat, &ccred); }; } else { ccred = GSS_C_NO_CREDENTIAL; }; if(cert) X509_free(cert); if(key) EVP_PKEY_free(key); if(cchain) sk_X509_pop_free(cchain, X509_free); if(chandle) globus_gsi_cred_handle_destroy(chandle); return ccred; }
/* returning zero (0) means success, everything else is treated as "failure" with no care exactly what the failure was */ int Curl_input_negotiate(struct connectdata *conn, bool proxy, const char *header) { struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: &conn->data->state.negotiate; OM_uint32 major_status, minor_status, minor_status2; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret; size_t len, rawlen; bool gss; const char* protocol; while(*header && ISSPACE(*header)) header++; if(checkprefix("GSS-Negotiate", header)) { protocol = "GSS-Negotiate"; gss = TRUE; } else if(checkprefix("Negotiate", header)) { protocol = "Negotiate"; gss = FALSE; } else return -1; if(neg_ctx->context) { if(neg_ctx->gss != gss) { return -1; } } else { neg_ctx->protocol = protocol; neg_ctx->gss = gss; } if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { /* We finished successfully our part of authentication, but server * rejected it (since we're again here). Exit with an error since we * can't invent anything better */ Curl_cleanup_negotiate(conn->data); return -1; } if(neg_ctx->server_name == NULL && (ret = get_gss_name(conn, proxy, &neg_ctx->server_name))) return ret; header += strlen(neg_ctx->protocol); while(*header && ISSPACE(*header)) header++; len = strlen(header); if(len > 0) { rawlen = Curl_base64_decode(header, (unsigned char **)&input_token.value); if(rawlen == 0) return -1; input_token.length = rawlen; #ifdef HAVE_SPNEGO /* Handle SPNEGO */ if(checkprefix("Negotiate", header)) { ASN1_OBJECT * object = NULL; int rc = 1; unsigned char * spnegoToken = NULL; size_t spnegoTokenLength = 0; unsigned char * mechToken = NULL; size_t mechTokenLength = 0; if(input_token.value == NULL) return CURLE_OUT_OF_MEMORY; spnegoToken = malloc(input_token.length); if(spnegoToken == NULL) return CURLE_OUT_OF_MEMORY; spnegoTokenLength = input_token.length; object = OBJ_txt2obj ("1.2.840.113554.1.2.2", 1); if(!parseSpnegoTargetToken(spnegoToken, spnegoTokenLength, NULL, NULL, &mechToken, &mechTokenLength, NULL, NULL)) { free(spnegoToken); spnegoToken = NULL; infof(conn->data, "Parse SPNEGO Target Token failed\n"); } else { free(input_token.value); input_token.value = malloc(mechTokenLength); if(input_token.value == NULL) return CURLE_OUT_OF_MEMORY; memcpy(input_token.value, mechToken,mechTokenLength); input_token.length = mechTokenLength; free(mechToken); mechToken = NULL; infof(conn->data, "Parse SPNEGO Target Token succeeded\n"); } } #endif } major_status = Curl_gss_init_sec_context(&minor_status, &neg_ctx->context, neg_ctx->server_name, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, NULL); if(input_token.length > 0) gss_release_buffer(&minor_status2, &input_token); neg_ctx->status = major_status; if(GSS_ERROR(major_status)) { /* Curl_cleanup_negotiate(conn->data) ??? */ log_gss_error(conn, minor_status, "gss_init_sec_context() failed: "); return -1; } if(output_token.length == 0) { return -1; } neg_ctx->output_token = output_token; /* conn->bits.close = FALSE; */ return 0; }
struct gfarmGssInitiateSecurityContextState * gfarmGssInitiateSecurityContextRequest(struct gfarm_eventqueue *q, int fd, const gss_name_t acceptorName, gss_cred_id_t cred, OM_uint32 reqFlag, void (*continuation) (void *), void *closure, OM_uint32 *majStatPtr, OM_uint32 *minStatPtr) { OM_uint32 majStat; OM_uint32 minStat; struct gfarmGssInitiateSecurityContextState *state; /* * Implementation specification: * In gfarm, an initiator must reveal own identity to an acceptor. */ if ((reqFlag & GSS_C_ANON_FLAG) == GSS_C_ANON_FLAG) { /* It is a bit safer to deny the request than to silently ignore it */ gflog_auth_error(GFARM_MSG_1000625, "gfarmGssInitiateSecurityContextRequest(): " "GSS_C_ANON_FLAG is not allowed"); majStat = GSS_S_UNAVAILABLE; minStat = GFSL_DEFAULT_MINOR_ERROR; goto ReturnStat; } GFARM_MALLOC(state); if (state == NULL) { gflog_auth_error(GFARM_MSG_1000626, "gfarmGssInitiateSecurityContextRequest(): " "no memory"); majStat = GSS_S_FAILURE; minStat = GFSL_DEFAULT_MINOR_ERROR; goto ReturnStat; } state->completed = 0; state->majStat = GSS_S_COMPLETE; state->minStat = GFSL_DEFAULT_MINOR_ERROR; state->writable = gfarm_fd_event_alloc(GFARM_EVENT_WRITE, fd, gfarmGssInitiateSecurityContextSendToken, state); if (state->writable == NULL) { gflog_auth_error(GFARM_MSG_1000627, "gfarmGssInitiateSecurityContextRequest(): " "no memory"); state->majStat = GSS_S_FAILURE; goto FreeState; } /* * We cannot use two independent events (i.e. a fd_event with * GFARM_EVENT_READ flag and a timer_event) here, because * it's possible that both event handlers are called at once. */ state->readable = gfarm_fd_event_alloc(GFARM_EVENT_READ|GFARM_EVENT_TIMEOUT, fd, gfarmGssInitiateSecurityContextReceiveToken, state); if (state->readable == NULL) { gflog_auth_error(GFARM_MSG_1000628, "gfarmGssInitiateSecurityContextRequest(): " "no memory"); state->majStat = GSS_S_FAILURE; goto FreeWritable; } state->q = q; state->fd = fd; state->acceptorName = acceptorName; state->cred = cred; state->reqFlag = reqFlag; state->continuation = continuation; state->closure = closure; state->retFlag = 0; /* GSS_C_EMPTY_BUFFER */ state->inputToken.length = 0; state->inputToken.value = NULL; state->itPtr = &state->inputToken; /* GSS_C_EMPTY_BUFFER */ state->outputToken.length = 0; state->outputToken.value = NULL; state->otPtr = &state->outputToken; state->actualMechType = NULL; state->sc = GSS_C_NO_CONTEXT; gssInitiateSecurityContextNext(state); assert(!state->completed); if (!GSS_ERROR(state->majStat)) { if (majStatPtr != NULL) { *majStatPtr = GSS_S_COMPLETE; } if (minStatPtr != NULL) { *minStatPtr = GFSL_DEFAULT_MINOR_ERROR; } return (state); } gfarm_event_free(state->readable); FreeWritable: gfarm_event_free(state->writable); FreeState: majStat = state->majStat; minStat = state->minStat; free(state); ReturnStat: if (majStatPtr != NULL) *majStatPtr = majStat; if (minStatPtr != NULL) *minStatPtr = minStat; if (GSS_ERROR(majStat)) { gflog_debug(GFARM_MSG_1000801, "failed to request initiate security context (%u)(%u)", majStat, minStat); } return (NULL); }
OM_uint32 kg_compose_deleg_cred(OM_uint32 *minor_status, krb5_gss_cred_id_t impersonator_cred, krb5_creds *subject_creds, OM_uint32 time_req, krb5_gss_cred_id_t *output_cred, OM_uint32 *time_rec, krb5_context context) { OM_uint32 major_status; krb5_error_code code; krb5_gss_cred_id_t cred = NULL; *output_cred = NULL; k5_mutex_assert_locked(&impersonator_cred->lock); if (!kg_is_initiator_cred(impersonator_cred) || impersonator_cred->name == NULL || impersonator_cred->impersonator != NULL) { code = G_BAD_USAGE; goto cleanup; } assert(impersonator_cred->name->princ != NULL); assert(subject_creds != NULL); assert(subject_creds->client != NULL); cred = xmalloc(sizeof(*cred)); if (cred == NULL) { code = ENOMEM; goto cleanup; } memset(cred, 0, sizeof(*cred)); code = k5_mutex_init(&cred->lock); if (code != 0) goto cleanup; cred->usage = GSS_C_INITIATE; cred->expire = subject_creds->times.endtime; code = kg_init_name(context, subject_creds->client, NULL, NULL, NULL, 0, &cred->name); if (code != 0) goto cleanup; code = krb5_cc_new_unique(context, "MEMORY", NULL, &cred->ccache); if (code != 0) goto cleanup; cred->destroy_ccache = 1; code = krb5_cc_initialize(context, cred->ccache, subject_creds->client); if (code != 0) goto cleanup; /* * Only return a "proxy" credential for use with constrained * delegation if the subject credentials are forwardable. * Submitting non-forwardable credentials to the KDC for use * with constrained delegation will only return an error. */ if (subject_creds->ticket_flags & TKT_FLG_FORWARDABLE) { code = make_proxy_cred(context, cred, impersonator_cred); if (code != 0) goto cleanup; } code = krb5_cc_store_cred(context, cred->ccache, subject_creds); if (code != 0) goto cleanup; if (time_rec != NULL) { krb5_timestamp now; code = krb5_timeofday(context, &now); if (code != 0) goto cleanup; *time_rec = cred->expire - now; } major_status = GSS_S_COMPLETE; *minor_status = 0; *output_cred = cred; cleanup: if (code != 0) { *minor_status = code; major_status = GSS_S_FAILURE; } if (GSS_ERROR(major_status) && cred != NULL) { k5_mutex_destroy(&cred->lock); krb5_cc_destroy(context, cred->ccache); kg_release_name(context, &cred->name); xfree(cred); } return major_status; }
/* * Curl_auth_create_gssapi_security_message() * * This is used to generate an already encoded GSSAPI (Kerberos V5) security * token message ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * chlg64 [in] - Pointer to the optional base64 encoded challenge message. * krb5 [in/out] - The Kerberos 5 data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_security_message(struct SessionHandle *data, const char *chlg64, struct kerberos5data *krb5, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlglen = 0; size_t messagelen = 0; unsigned char *chlg = NULL; unsigned char *message = NULL; OM_uint32 gss_status; OM_uint32 gss_major_status; OM_uint32 gss_minor_status; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; unsigned int indata = 0; unsigned int outdata = 0; gss_qop_t qop = GSS_C_QOP_DEFAULT; unsigned int sec_layer = 0; unsigned int max_size = 0; gss_name_t username = GSS_C_NO_NAME; gss_buffer_desc username_token; /* Decode the base-64 encoded input message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "GSSAPI handshake failure (empty security message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Get the fully qualified username back from the context */ gss_major_status = gss_inquire_context(&gss_minor_status, krb5->context, &username, NULL, NULL, NULL, NULL, NULL, NULL); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, "gss_inquire_context() failed: ", gss_major_status, gss_minor_status); free(chlg); return CURLE_OUT_OF_MEMORY; } /* Convert the username from internal format to a displayable token */ gss_major_status = gss_display_name(&gss_minor_status, username, &username_token, NULL); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, "gss_display_name() failed: ", gss_major_status, gss_minor_status); free(chlg); return CURLE_OUT_OF_MEMORY; } /* Setup the challenge "input" security buffer */ input_token.value = chlg; input_token.length = chlglen; /* Decrypt the inbound challenge and obtain the qop */ gss_major_status = gss_unwrap(&gss_minor_status, krb5->context, &input_token, &output_token, NULL, &qop); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, "gss_unwrap() failed: ", gss_major_status, gss_minor_status); gss_release_buffer(&gss_status, &username_token); free(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ if(output_token.length != 4) { infof(data, "GSSAPI handshake failure (invalid security data)\n"); gss_release_buffer(&gss_status, &username_token); free(chlg); return CURLE_BAD_CONTENT_ENCODING; } /* Copy the data out and free the challenge as it is not required anymore */ memcpy(&indata, output_token.value, 4); gss_release_buffer(&gss_status, &output_token); free(chlg); /* Extract the security layer */ sec_layer = indata & 0x000000FF; if(!(sec_layer & GSSAUTH_P_NONE)) { infof(data, "GSSAPI handshake failure (invalid security layer)\n"); gss_release_buffer(&gss_status, &username_token); return CURLE_BAD_CONTENT_ENCODING; } /* Extract the maximum message size the server can receive */ max_size = ntohl(indata & 0xFFFFFF00); if(max_size > 0) { /* The server has told us it supports a maximum receive buffer, however, as we don't require one unless we are encrypting data, we tell the server our receive buffer is zero. */ max_size = 0; } /* Allocate our message */ messagelen = sizeof(outdata) + username_token.length + 1; message = malloc(messagelen); if(!message) { gss_release_buffer(&gss_status, &username_token); return CURLE_OUT_OF_MEMORY; } /* Populate the message with the security layer, client supported receive message size and authorization identity including the 0x00 based terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization identity is not terminated with the zero-valued (%x00) octet." it seems necessary to include it. */ outdata = htonl(max_size) | sec_layer; memcpy(message, &outdata, sizeof(outdata)); memcpy(message + sizeof(outdata), username_token.value, username_token.length); message[messagelen - 1] = '\0'; /* Free the username token as it is not required anymore */ gss_release_buffer(&gss_status, &username_token); /* Setup the "authentication data" security buffer */ input_token.value = message; input_token.length = messagelen; /* Encrypt the data */ gss_major_status = gss_wrap(&gss_minor_status, krb5->context, 0, GSS_C_QOP_DEFAULT, &input_token, NULL, &output_token); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, "gss_wrap() failed: ", gss_major_status, gss_minor_status); free(message); return CURLE_OUT_OF_MEMORY; } /* Base64 encode the response */ result = Curl_base64_encode(data, (char *) output_token.value, output_token.length, outptr, outlen); /* Free the output buffer */ gss_release_buffer(&gss_status, &output_token); /* Free the message buffer */ free(message); return result; }
int gsscon_passive_authenticate (int inSocket, gss_buffer_desc inNameBuffer, gss_ctx_id_t *outGSSContext, client_cb_fn clientCb, void *clientCbData) { int err = 0; OM_uint32 majorStatus; OM_uint32 minorStatus = 0, minorStatusToo = 0; gss_ctx_id_t gssContext = GSS_C_NO_CONTEXT; gss_name_t clientName = GSS_C_NO_NAME, serviceName = GSS_C_NO_NAME; gss_cred_id_t acceptorCredentials = NULL; gss_buffer_desc clientDisplayName = {0, NULL}; char *inputTokenBuffer = NULL; size_t inputTokenBufferLength = 0; gss_buffer_desc inputToken; /* buffer received from the server */ printf("In gsscon_passive_authenticate(), inNameBuffer = %s\n", inNameBuffer.value); if (inSocket < 0 ) { err = EINVAL; } if (!outGSSContext) { err = EINVAL; } if (!err) majorStatus = gss_import_name (&minorStatus, &inNameBuffer, (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &serviceName); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_import_name(serviceName)", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } if (!err) { majorStatus = gss_acquire_cred ( &minorStatus, serviceName, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_ACCEPT, &acceptorCredentials, NULL /*mechs out*/, NULL /*time out*/); if (majorStatus != GSS_S_COMPLETE) { gsscon_print_gss_errors ("gss_acquire_cred", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } /* * The main authentication loop: * * GSS is a multimechanism API. The number of packet exchanges required to * authenticatevaries between mechanisms. As a result, we need to loop reading * input tokens from the client, calling gss_accept_sec_context on the input * tokens and send the resulting output tokens back to the client until we * get GSS_S_COMPLETE or an error. * * When we are done, save the client principal so we can make authorization * checks. */ majorStatus = GSS_S_CONTINUE_NEEDED; while (!err && (majorStatus != GSS_S_COMPLETE)) { /* Clean up old input buffer */ if (inputTokenBuffer != NULL) { free (inputTokenBuffer); inputTokenBuffer = NULL; /* don't double-free */ } err = gsscon_read_token (inSocket, &inputTokenBuffer, &inputTokenBufferLength); if (!err) { /* Set up input buffers for the next run through the loop */ inputToken.value = inputTokenBuffer; inputToken.length = inputTokenBufferLength; } if (!err) { /* buffer to send to the server */ gss_buffer_desc outputToken = { 0, NULL }; /* * accept_sec_context does the actual work of taking the client's * request and generating an appropriate reply. */ majorStatus = gss_accept_sec_context (&minorStatus, &gssContext, acceptorCredentials, &inputToken, GSS_C_NO_CHANNEL_BINDINGS, &clientName, NULL /* actual_mech_type */, &outputToken, NULL /* req_flags */, NULL /* time_rec */, NULL /* delegated_cred_handle */); if ((outputToken.length > 0) && (outputToken.value != NULL)) { /* Send the output token to the client (even on error) */ err = gsscon_write_token (inSocket, outputToken.value, outputToken.length); /* free the output token */ gss_release_buffer (&minorStatusToo, &outputToken); } } if ((majorStatus != GSS_S_COMPLETE) && (majorStatus != GSS_S_CONTINUE_NEEDED)) { gsscon_print_gss_errors ("gss_accept_sec_context", majorStatus, minorStatus); err = minorStatus ? minorStatus : majorStatus; } } if (!err) { majorStatus = gss_display_name(&minorStatus, clientName, &clientDisplayName, NULL); if (GSS_ERROR(majorStatus)) { gsscon_print_gss_errors("gss_display_name", majorStatus, minorStatus); err = EINVAL; } if (!err) err = clientCb(clientName, &clientDisplayName, clientCbData); } if (!err) { *outGSSContext = gssContext; gssContext = NULL; } else { gsscon_print_error (err, "Authenticate failed"); } if (inputTokenBuffer) { free (inputTokenBuffer); } if (gssContext != GSS_C_NO_CONTEXT) { gss_delete_sec_context (&minorStatus, &gssContext, GSS_C_NO_BUFFER); } if (clientName != GSS_C_NO_NAME) gss_release_name(&minorStatus, &clientName); if (clientDisplayName.value != NULL) gss_release_buffer(&minorStatus, &clientDisplayName); gss_release_name( &minorStatus, &serviceName); gss_release_cred( &minorStatus, &acceptorCredentials); return err; }
/* The mechglue always passes null desired_mechs and actual_mechs. */ OM_uint32 KRB5_CALLCONV krb5_gss_acquire_cred_impersonate_name(OM_uint32 *minor_status, const gss_cred_id_t impersonator_cred_handle, 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) { OM_uint32 major_status; krb5_error_code code; krb5_gss_cred_id_t cred; krb5_context context; if (impersonator_cred_handle == GSS_C_NO_CREDENTIAL) return GSS_S_CALL_INACCESSIBLE_READ; if (desired_name == GSS_C_NO_NAME) return GSS_S_CALL_INACCESSIBLE_READ; if (output_cred_handle == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; if (cred_usage != GSS_C_INITIATE) { *minor_status = (OM_uint32)G_BAD_USAGE; return GSS_S_FAILURE; } *output_cred_handle = GSS_C_NO_CREDENTIAL; if (time_rec != NULL) *time_rec = 0; code = krb5_gss_init_context(&context); if (code != 0) { *minor_status = code; return GSS_S_FAILURE; } major_status = kg_cred_resolve(minor_status, context, impersonator_cred_handle, NULL); if (GSS_ERROR(major_status)) { krb5_free_context(context); return major_status; } major_status = kg_impersonate_name(minor_status, (krb5_gss_cred_id_t)impersonator_cred_handle, (krb5_gss_name_t)desired_name, time_req, &cred, time_rec, context); if (!GSS_ERROR(major_status)) *output_cred_handle = (gss_cred_id_t)cred; k5_mutex_unlock(&((krb5_gss_cred_id_t)impersonator_cred_handle)->lock); krb5_free_context(context); return major_status; }
static OM_uint32 inquireNegoExKey(OM_uint32 *minor, const gss_ctx_id_t ctx, const gss_OID desired_object, gss_buffer_set_t *dataSet) { OM_uint32 major, tmpMinor; int bInitiatorKey; gss_buffer_desc salt; gss_buffer_desc key = GSS_C_EMPTY_BUFFER; size_t keySize; bInitiatorKey = CTX_IS_INITIATOR(ctx); if (ctx->encryptionType == ENCTYPE_NULL) { major = GSS_S_UNAVAILABLE; *minor = GSSEAP_KEY_UNAVAILABLE; goto cleanup; } /* * If the caller supplied the verify key OID, then we need the acceptor * key if we are the initiator, and vice versa. */ if (desired_object->length == 11 && memcmp(desired_object->elements, "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02\x05\x07", 11) == 0) bInitiatorKey ^= 1; if (bInitiatorKey) { salt.length = NEGOEX_INITIATOR_SALT_LEN; salt.value = NEGOEX_INITIATOR_SALT; } else { salt.length = NEGOEX_ACCEPTOR_SALT_LEN; salt.value = NEGOEX_ACCEPTOR_SALT; } keySize = KRB_KEY_LENGTH(&ctx->rfc3961Key); major = gssEapPseudoRandom(minor, ctx, GSS_C_PRF_KEY_FULL, &salt, keySize, &key); if (GSS_ERROR(major)) goto cleanup; major = gss_add_buffer_set_member(minor, &key, dataSet); if (GSS_ERROR(major)) goto cleanup; major = addEnctypeOidToBufferSet(minor, ctx->encryptionType, dataSet); if (GSS_ERROR(major)) goto cleanup; major = GSS_S_COMPLETE; *minor = 0; cleanup: if (key.value != NULL) { memset(key.value, 0, key.length); gss_release_buffer(&tmpMinor, &key); } if (GSS_ERROR(major)) zeroAndReleaseBufferSet(dataSet); return major; }
/* * Export GSI credentials to disk. */ static void ssh_gssapi_gsi_storecreds(ssh_gssapi_client *client) { OM_uint32 major_status; OM_uint32 minor_status; gss_buffer_desc export_cred = GSS_C_EMPTY_BUFFER; char * p; if (!client || !client->creds) { return; } major_status = gss_export_cred(&minor_status, client->creds, GSS_C_NO_OID, 1, &export_cred); if (GSS_ERROR(major_status) && major_status != GSS_S_UNAVAILABLE) { Gssctxt *ctx; ssh_gssapi_build_ctx(&ctx); ctx->major = major_status; ctx->minor = minor_status; ssh_gssapi_set_oid(ctx, &gssapi_gsi_mech.oid); ssh_gssapi_error(ctx); ssh_gssapi_delete_ctx(&ctx); return; } p = strchr((char *) export_cred.value, '='); if (p == NULL) { logit("Failed to parse exported credentials string '%.100s'", (char *)export_cred.value); gss_release_buffer(&minor_status, &export_cred); return; } *p++ = '\0'; if (strcmp((char *)export_cred.value,"X509_USER_DELEG_PROXY") == 0) { client->store.envvar = strdup("X509_USER_PROXY"); } else { client->store.envvar = strdup((char *)export_cred.value); } if (access(p, R_OK) == 0) { if (client->store.filename) { if (rename(p, client->store.filename) < 0) { logit("Failed to rename %s to %s: %s", p, client->store.filename, strerror(errno)); free(client->store.filename); client->store.filename = strdup(p); } else { p = client->store.filename; } } else { client->store.filename = strdup(p); } } client->store.envval = strdup(p); #ifdef USE_PAM if (options.use_pam) do_pam_putenv(client->store.envvar, client->store.envval); #endif gss_release_buffer(&minor_status, &export_cred); }
/* * We only support those mechanisms that we know about (ie ones that we know * how to check local user kuserok and the like) */ static int userauth_gssapi(struct ssh *ssh) { Authctxt *authctxt = ssh->authctxt; gss_OID_desc goid = {0, NULL}; Gssctxt *ctxt = NULL; int r, mechs, present; gss_OID_set supported; OM_uint32 ms; size_t len; u_char *doid = NULL; if (!authctxt->valid || authctxt->user == NULL) return (0); if ((r = sshpkt_get_u32(ssh, &mechs)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (mechs == 0) { debug("Mechanism negotiation is not supported"); return (0); } ssh_gssapi_supported_oids(&supported); do { mechs--; if (doid) xfree(doid); present = 0; if ((r = sshpkt_get_string(ssh, &doid, &len)) != 0) fatal("%s: %s", __func__, ssh_err(r)); if (len > 2 && doid[0] == SSH_GSS_OIDTYPE && doid[1] == len - 2) { goid.elements = doid + 2; goid.length = len - 2; gss_test_oid_set_member(&ms, &goid, supported, &present); } else { logit("Badly formed OID received"); } } while (mechs > 0 && !present); gss_release_oid_set(&ms, &supported); if (!present) { xfree(doid); authctxt->server_caused_failure = 1; return (0); } if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, &goid)))) { if (ctxt != NULL) ssh_gssapi_delete_ctx(&ctxt); xfree(doid); authctxt->server_caused_failure = 1; return (0); } authctxt->methoddata = (void *)ctxt; /* Return the OID that we received */ if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_GSSAPI_RESPONSE)) != 0 || (r = sshpkt_put_string(ssh, doid, len)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); xfree(doid); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); authctxt->postponed = 1; return (0); }
static OM_uint32 gssEapDuplicateCred(OM_uint32 *minor, const gss_cred_id_t src, gss_cred_id_t *pDst) { OM_uint32 major, tmpMinor; gss_cred_id_t dst = GSS_C_NO_CREDENTIAL; *pDst = GSS_C_NO_CREDENTIAL; major = gssEapAllocCred(minor, &dst); if (GSS_ERROR(major)) goto cleanup; dst->flags = src->flags; if (src->name != GSS_C_NO_NAME) { major = gssEapDuplicateName(minor, src->name, &dst->name); if (GSS_ERROR(major)) goto cleanup; } if (src->target != GSS_C_NO_NAME) { major = gssEapDuplicateName(minor, src->target, &dst->target); if (GSS_ERROR(major)) goto cleanup; } if (src->password.value != NULL) { major = duplicateBuffer(minor, &src->password, &dst->password); if (GSS_ERROR(major)) goto cleanup; } #ifndef MECH_EAP if (src->deleg_assertions.value != NULL) { major = duplicateBuffer(minor, &src->deleg_assertions, &dst->deleg_assertions); if (GSS_ERROR(major)) goto cleanup; } #endif major = duplicateOidSet(minor, src->mechanisms, &dst->mechanisms); if (GSS_ERROR(major)) goto cleanup; dst->expiryTime = src->expiryTime; if (src->radiusConfigFile.value != NULL) duplicateBufferOrCleanup(&src->radiusConfigFile, &dst->radiusConfigFile); if (src->radiusConfigStanza.value != NULL) duplicateBufferOrCleanup(&src->radiusConfigStanza, &dst->radiusConfigStanza); if (src->caCertificate.value != NULL) duplicateBufferOrCleanup(&src->caCertificate, &dst->caCertificate); if (src->subjectNameConstraint.value != NULL) duplicateBufferOrCleanup(&src->subjectNameConstraint, &dst->subjectNameConstraint); if (src->subjectAltNameConstraint.value != NULL) duplicateBufferOrCleanup(&src->subjectAltNameConstraint, &dst->subjectAltNameConstraint); *pDst = dst; dst = GSS_C_NO_CREDENTIAL; major = GSS_S_COMPLETE; *minor = 0; cleanup: gssEapReleaseCred(&tmpMinor, &dst); return major; }
/* * Curl_auth_create_gssapi_user_message() * * This is used to generate an already encoded GSSAPI (Kerberos V5) user token * message ready for sending to the recipient. * * Parameters: * * data [in] - The session handle. * userp [in] - The user name. * passdwp [in] - The user's password. * service [in] - The service type such as www, smtp, pop or imap. * mutual_auth [in] - Flag specifing whether or not mutual authentication * is enabled. * chlg64 [in] - Pointer to the optional base64 encoded challenge * message. * krb5 [in/out] - The Kerberos 5 data struct being used and modified. * outptr [in/out] - The address where a pointer to newly allocated memory * holding the result will be stored upon completion. * outlen [out] - The length of the output message. * * Returns CURLE_OK on success. */ CURLcode Curl_auth_create_gssapi_user_message(struct SessionHandle *data, const char *userp, const char *passwdp, const char *service, const bool mutual_auth, const char *chlg64, struct kerberos5data *krb5, char **outptr, size_t *outlen) { CURLcode result = CURLE_OK; size_t chlglen = 0; unsigned char *chlg = NULL; OM_uint32 gss_status; OM_uint32 gss_major_status; OM_uint32 gss_minor_status; gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; (void) userp; (void) passwdp; if(krb5->context == GSS_C_NO_CONTEXT) { /* Generate our SPN */ char *spn = Curl_auth_build_gssapi_spn(service, data->easy_conn->host.name); if(!spn) return CURLE_OUT_OF_MEMORY; /* Populate the SPN structure */ spn_token.value = spn; spn_token.length = strlen(spn); /* Import the SPN */ gss_major_status = gss_import_name(&gss_minor_status, &spn_token, GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn); if(GSS_ERROR(gss_major_status)) { Curl_gss_log_error(data, "gss_import_name() failed: ", gss_major_status, gss_minor_status); free(spn); return CURLE_OUT_OF_MEMORY; } free(spn); } else { /* Decode the base-64 encoded challenge message */ if(strlen(chlg64) && *chlg64 != '=') { result = Curl_base64_decode(chlg64, &chlg, &chlglen); if(result) return result; } /* Ensure we have a valid challenge message */ if(!chlg) { infof(data, "GSSAPI handshake failure (empty challenge message)\n"); return CURLE_BAD_CONTENT_ENCODING; } /* Setup the challenge "input" security buffer */ input_token.value = chlg; input_token.length = chlglen; } gss_major_status = Curl_gss_init_sec_context(data, &gss_minor_status, &krb5->context, krb5->spn, &Curl_krb5_mech_oid, GSS_C_NO_CHANNEL_BINDINGS, &input_token, &output_token, mutual_auth, NULL); free(input_token.value); if(GSS_ERROR(gss_major_status)) { if(output_token.value) gss_release_buffer(&gss_status, &output_token); Curl_gss_log_error(data, "gss_init_sec_context() failed: ", gss_major_status, gss_minor_status); return CURLE_RECV_ERROR; } if(output_token.value && output_token.length) { /* Base64 encode the response */ result = Curl_base64_encode(data, (char *) output_token.value, output_token.length, outptr, outlen); gss_release_buffer(&gss_status, &output_token); } return result; }
OM_uint32 gssEapResolveInitiatorCred(OM_uint32 *minor, const gss_cred_id_t cred, const gss_name_t targetName #ifndef HAVE_MOONSHOT_GET_IDENTITY GSSEAP_UNUSED #endif , gss_cred_id_t *pResolvedCred) { OM_uint32 major, tmpMinor; gss_cred_id_t resolvedCred = GSS_C_NO_CREDENTIAL; if (cred == GSS_C_NO_CREDENTIAL) { major = gssEapAcquireCred(minor, GSS_C_NO_NAME, GSS_C_INDEFINITE, GSS_C_NO_OID_SET, GSS_C_INITIATE, &resolvedCred, NULL, NULL); if (GSS_ERROR(major)) goto cleanup; } else { if ((cred->flags & CRED_FLAG_INITIATE) == 0) { major = GSS_S_NO_CRED; *minor = GSSEAP_CRED_USAGE_MISMATCH; goto cleanup; } major = gssEapDuplicateCred(minor, cred, &resolvedCred); if (GSS_ERROR(major)) goto cleanup; } if ((resolvedCred->flags & CRED_FLAG_RESOLVED) == 0) { #ifdef HAVE_MOONSHOT_GET_IDENTITY major = libMoonshotResolveInitiatorCred(minor, resolvedCred, targetName); if (major == GSS_S_CRED_UNAVAIL) #endif major = staticIdentityFileResolveInitiatorCred(minor, resolvedCred); if (GSS_ERROR(major) && major != GSS_S_CRED_UNAVAIL) goto cleanup; /* If we have a caller-supplied password, the credential is resolved. */ if ((resolvedCred->flags & CRED_FLAG_PASSWORD) == 0) { major = GSS_S_CRED_UNAVAIL; *minor = GSSEAP_NO_DEFAULT_CRED; goto cleanup; } resolvedCred->flags |= CRED_FLAG_RESOLVED; } *pResolvedCred = resolvedCred; resolvedCred = GSS_C_NO_CREDENTIAL; major = GSS_S_COMPLETE; *minor = 0; cleanup: gssEapReleaseCred(&tmpMinor, &resolvedCred); return major; }
/* * Establish a secure context using the GSS-API. A handshake is attempted in * order to detect a non-authenticating server. The server IP address is obtained * from the socket and is used to perform an fqdn lookup in case a DNS alias is * used as a host spec, this ensures we authenticate against the correct server. * We attempt to extract the server principal name, a service, from the * environment, if it is not specified we use "host/" as a default. * * @pram to_net_sd. Socket to write to. * * @pram from_net_sd. Socket to read from. * * @param req_flags. A representation of the security services to * be requested. * * @param ret_flags. A representation of the security services * provided/supported by the underlying mechanism * to be returned to the invoking function. * * Returns 0 on success, otherwise error. */ static int dcc_gssapi_establish_secure_context(int to_net_sd, int from_net_sd, OM_uint32 req_flags, OM_uint32 *ret_flags) { char *ext_princ_name = NULL; char *full_name = NULL; char *princ_env_val = NULL; gss_buffer_desc input_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc name_buffer = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_tok = GSS_C_EMPTY_BUFFER; gss_name_t int_serv_name; gss_OID name_type; int ret; OM_uint32 major_status, minor_status, return_status; socklen_t addr_len; struct hostent *hp; struct sockaddr_in addr; addr_len = sizeof(addr); if ((ret = getpeername(to_net_sd, &addr, &addr_len)) != 0) { rs_log_error("Failed to look up peer address using socket \"%d\": %s.", to_net_sd, hstrerror(h_errno)); return EXIT_CONNECT_FAILED; } rs_log_info("Successfully looked up IP address %s using socket %d.", inet_ntoa(addr.sin_addr), to_net_sd); if ((hp = gethostbyaddr((char *) &addr.sin_addr, sizeof(addr.sin_addr), AF_INET)) == NULL) { rs_log_error("Failed to look up host by address \"%s\": %s.", inet_ntoa(addr.sin_addr), hstrerror(h_errno)); return EXIT_CONNECT_FAILED; } rs_log_info("Successfully looked up host %s using IP address %s.", hp->h_name, inet_ntoa(addr.sin_addr)); if ((full_name = malloc(strlen(hp->h_name) + 1)) == NULL) { rs_log_error("malloc failed : %ld bytes: out of memory.", (long) (strlen(hp->h_name) + 1)); return EXIT_OUT_OF_MEMORY; } strcpy(full_name, hp->h_name); if ((princ_env_val = getenv("DISTCC_PRINCIPAL"))) { if (asprintf(&ext_princ_name, "%s@%s", princ_env_val, full_name) < 0) { rs_log_error("Failed to allocate memory for asprintf."); return EXIT_OUT_OF_MEMORY; } name_type = GSS_C_NT_HOSTBASED_SERVICE; } else { if (asprintf(&ext_princ_name, "host/%s", full_name) < 0) { rs_log_error("Failed to allocate memory for asprintf."); return EXIT_OUT_OF_MEMORY; } name_type = GSS_C_NT_USER_NAME; } free(full_name); name_buffer.value = ext_princ_name; name_buffer.length = strlen(ext_princ_name); if ((major_status = gss_import_name(&minor_status, &name_buffer, name_type, &int_serv_name)) != GSS_S_COMPLETE) { rs_log_error("Failed to import service name to internal GSS-API format."); return EXIT_GSSAPI_FAILED; } input_tok.value = NULL; input_tok.length = 0; output_tok.value = NULL; output_tok.length = 0; if ((ret = dcc_gssapi_send_handshake(to_net_sd, from_net_sd)) != 0) { return ret; } do { major_status = gss_init_sec_context(&minor_status, GSS_C_NO_CREDENTIAL, &distcc_ctx_handle, int_serv_name, GSS_C_NO_OID, req_flags, 0, GSS_C_NO_CHANNEL_BINDINGS, &input_tok, NULL, &output_tok, ret_flags, NULL); if (GSS_ERROR(major_status)) { rs_log_crit("Failed to initiate a secure context."); dcc_gssapi_status_to_log(major_status, GSS_C_GSS_CODE); dcc_gssapi_status_to_log(minor_status, GSS_C_MECH_CODE); return EXIT_GSSAPI_FAILED; } if (output_tok.length > 0) { if ((ret = send_token(to_net_sd, &output_tok)) != 0) { dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name); return ret; } else { if ((return_status = gss_release_buffer(&minor_status, &output_tok)) != GSS_S_COMPLETE) { rs_log_error("Failed to release buffer."); } } } if (input_tok.length > 0) { if ((return_status = gss_release_buffer(&minor_status, &input_tok)) != GSS_S_COMPLETE) { rs_log_error("Failed to release buffer."); } } if (major_status == GSS_S_CONTINUE_NEEDED) { if ((ret = recv_token(from_net_sd, &input_tok)) != 0) { dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name); return ret; } } } while (major_status != GSS_S_COMPLETE); rs_log_info("Successfully authenticated %s.", ext_princ_name); dcc_gssapi_cleanup(&input_tok, &output_tok, &int_serv_name); if ((major_status = gss_release_buffer(&minor_status, &name_buffer)) != GSS_S_COMPLETE) { rs_log_error("Failed to release buffer."); } return 0; }
/** * * GSSAPI routine to initiate the sending of a security context * See: <draft-ietf-cat-gssv2-cbind-04.txt> * * @param minor_status * @param context_handle * @param option * @param value * * @return */ OM_uint32 GSS_CALLCONV gss_set_sec_context_option( OM_uint32 * minor_status, gss_ctx_id_t * context_handle, const gss_OID option, const gss_buffer_t value) { gss_ctx_id_desc * context = NULL; OM_uint32 major_status = GSS_S_COMPLETE; OM_uint32 local_minor_status; globus_result_t local_result = GLOBUS_SUCCESS; int index; static char * _function_name_ = "gss_set_sec_context_option"; GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER; if(minor_status == NULL) { major_status = GSS_S_FAILURE; goto exit; } *minor_status = (OM_uint32) GLOBUS_SUCCESS; if(context_handle == NULL) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid context_handle passed to function - cannot be NULL"))); major_status = GSS_S_FAILURE; goto exit; } context = *context_handle; if(option == GSS_C_NO_OID) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid option passed to function with value: GSS_C_NO_OID"))); major_status = GSS_S_FAILURE; goto exit; } if ((*context_handle == (gss_ctx_id_t) GSS_C_NO_CONTEXT)) { /* for now just malloc and zero the context */ context = (gss_ctx_id_desc *) malloc(sizeof(gss_ctx_id_desc)); if (context == NULL) { GLOBUS_GSI_GSSAPI_MALLOC_ERROR(minor_status); major_status = GSS_S_FAILURE; goto exit; } *context_handle = context; memset(context, 0, sizeof(gss_ctx_id_desc)); context->ctx_flags = 0; major_status = gss_create_empty_oid_set( &local_minor_status, (gss_OID_set *) &context->extension_oids); /* initialize the callback_data in the context. This needs * to be done so the verify_callback func can be set later. */ local_result = globus_gsi_callback_data_init( &context->callback_data); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT, (_GGSL("The callback data in the context " "could not be initialized."))); major_status = GSS_S_FAILURE; goto exit; } } else if(context->ctx_flags & GSS_I_CTX_INITIALIZED) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSS_CONTEXT, (_GGSL("The context has already been initialized! %s should be " "called on a context before initialization"), _function_name_)); major_status = GSS_S_FAILURE; goto exit; } if(g_OID_equal(option, GSS_DISALLOW_ENCRYPTION)) { context->ctx_flags |= GSS_I_DISALLOW_ENCRYPTION; } else if(g_OID_equal(option, GSS_PROTECTION_FAIL_ON_CONTEXT_EXPIRATION)) { context->ctx_flags |= GSS_I_PROTECTION_FAIL_ON_CONTEXT_EXPIRATION; } else if(g_OID_equal(option, GSS_APPLICATION_WILL_HANDLE_EXTENSIONS)) { if(value == GSS_C_NO_BUFFER) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid buffer passed to function"))); major_status = GSS_S_FAILURE; goto exit; } for(index = 0; index < ((gss_OID_set_desc *) value->value)->count; index++) { major_status = gss_add_oid_set_member( &local_minor_status, (gss_OID) &((gss_OID_set_desc *) value->value)->elements[index], (gss_OID_set *) &context->extension_oids); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_OID); goto exit; } } local_result = globus_gsi_callback_set_extension_cb( context->callback_data, globus_i_gsi_gss_verify_extensions_callback); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA); major_status = GSS_S_FAILURE; goto exit; } /* if the extension_oids are set, * then we set them in the callback data */ local_result = globus_gsi_callback_set_extension_oids( context->callback_data, (void *) context->extension_oids); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_CALLBACK_DATA); major_status = GSS_S_FAILURE; goto exit; } context->ctx_flags |= GSS_I_APPLICATION_WILL_HANDLE_EXTENSIONS; } else { /* unknown option */ GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_UNKNOWN_OPTION, (NULL)); major_status = GSS_S_FAILURE; goto exit; } *context_handle = context; exit: GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT; return major_status; }
static int test_libntlm_v1(int flags) { const char *user = "******", *domain = "mydomain", *password = "******"; OM_uint32 maj_stat, min_stat; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_buffer_desc input, output; struct ntlm_type1 type1; struct ntlm_type2 type2; struct ntlm_type3 type3; struct ntlm_buf data; krb5_error_code ret; gss_name_t src_name = GSS_C_NO_NAME; memset(&type1, 0, sizeof(type1)); memset(&type2, 0, sizeof(type2)); memset(&type3, 0, sizeof(type3)); type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_TARGET|NTLM_NEG_NTLM|flags; type1.domain = strdup(domain); type1.hostname = NULL; type1.os[0] = 0; type1.os[1] = 0; ret = heim_ntlm_encode_type1(&type1, &data); if (ret) errx(1, "heim_ntlm_encode_type1"); input.value = data.data; input.length = data.length; output.length = 0; output.value = NULL; maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &output, NULL, NULL, NULL); free(data.data); if (GSS_ERROR(maj_stat)) errx(1, "accept_sec_context v1: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); if (output.length == 0) errx(1, "output.length == 0"); data.data = output.value; data.length = output.length; ret = heim_ntlm_decode_type2(&data, &type2); if (ret) errx(1, "heim_ntlm_decode_type2"); gss_release_buffer(&min_stat, &output); type3.flags = type2.flags; type3.username = rk_UNCONST(user); type3.targetname = type2.targetname; type3.ws = rk_UNCONST("workstation"); { struct ntlm_buf key; heim_ntlm_nt_key(password, &key); heim_ntlm_calculate_ntlm1(key.data, key.length, type2.challenge, &type3.ntlm); if (flags & NTLM_NEG_KEYEX) { struct ntlm_buf sessionkey; heim_ntlm_build_ntlm1_master(key.data, key.length, &sessionkey, &type3.sessionkey); free(sessionkey.data); } free(key.data); } ret = heim_ntlm_encode_type3(&type3, &data); if (ret) errx(1, "heim_ntlm_encode_type3"); input.length = data.length; input.value = data.data; maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, &src_name, NULL, &output, NULL, NULL, NULL); free(input.value); if (maj_stat != GSS_S_COMPLETE) errx(1, "accept_sec_context v1 2 %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); gss_release_buffer(&min_stat, &output); gss_delete_sec_context(&min_stat, &ctx, NULL); if (src_name == GSS_C_NO_NAME) errx(1, "no source name!"); gss_display_name(&min_stat, src_name, &output, NULL); printf("src_name: %.*s\n", (int)output.length, (char*)output.value); gss_release_name(&min_stat, &src_name); gss_release_buffer(&min_stat, &output); return 0; }
int gfarmGssAcceptSecurityContext(int fd, gss_cred_id_t cred, gss_ctx_id_t *scPtr, OM_uint32 *majStatPtr, OM_uint32 *minStatPtr, gss_name_t *remoteNamePtr, gss_cred_id_t *remoteCredPtr) { OM_uint32 majStat; OM_uint32 minStat, minStat2; OM_uint32 retFlag = GFARM_GSS_DEFAULT_SECURITY_ACCEPT_FLAG; gss_name_t initiatorName = GSS_C_NO_NAME; gss_cred_id_t remCred = GSS_C_NO_CREDENTIAL; gss_buffer_desc inputToken = GSS_C_EMPTY_BUFFER; gss_buffer_t itPtr = &inputToken; gss_buffer_desc outputToken = GSS_C_EMPTY_BUFFER; gss_buffer_t otPtr = &outputToken; gss_OID mechType = GSS_C_NO_OID; OM_uint32 timeRet; int tknStat; static const char diag[] = "gfarmGssAcceptSecurityContext()"; *scPtr = GSS_C_NO_CONTEXT; do { tknStat = gfarmGssReceiveToken(fd, itPtr, GFARM_GSS_TIMEOUT_INFINITE); if (tknStat <= 0) { gflog_auth_info(GFARM_MSG_1000616, "gfarmGssAcceptSecurityContext(): failed to receive response"); majStat = GSS_S_DEFECTIVE_TOKEN|GSS_S_CALL_INACCESSIBLE_READ; minStat = GFSL_DEFAULT_MINOR_ERROR; break; } gfarm_mutex_lock(&gss_mutex, diag, gssDiag); majStat = gss_accept_sec_context(&minStat, scPtr, cred, itPtr, GSS_C_NO_CHANNEL_BINDINGS, &initiatorName, &mechType, otPtr, &retFlag, &timeRet, &remCred); gfarm_mutex_unlock(&gss_mutex, diag, gssDiag); if (itPtr->length > 0) gss_release_buffer(&minStat2, itPtr); if (otPtr->length > 0) { tknStat = gfarmGssSendToken(fd, otPtr); gss_release_buffer(&minStat2, otPtr); if (tknStat <= 0) { gflog_auth_info(GFARM_MSG_1000617, "gfarmGssAcceptSecurityContext(): failed to send response"); majStat = GSS_S_DEFECTIVE_TOKEN|GSS_S_CALL_INACCESSIBLE_WRITE; minStat = GFSL_DEFAULT_MINOR_ERROR; } } if (GSS_ERROR(majStat)) { break; } } while (majStat & GSS_S_CONTINUE_NEEDED); if (majStatPtr != NULL) *majStatPtr = majStat; if (minStatPtr != NULL) *minStatPtr = minStat; if (majStat == GSS_S_COMPLETE && remoteNamePtr != NULL) *remoteNamePtr = initiatorName; else if (initiatorName != GSS_C_NO_NAME) gss_release_name(&minStat2, &initiatorName); if (majStat == GSS_S_COMPLETE && remoteCredPtr != NULL) *remoteCredPtr = remCred; else if (remCred != GSS_C_NO_CREDENTIAL) gss_release_cred(&minStat2, &remCred); if (majStat != GSS_S_COMPLETE && *scPtr != GSS_C_NO_CONTEXT) gss_delete_sec_context(&minStat2, scPtr, GSS_C_NO_BUFFER); return majStat == GSS_S_COMPLETE ? 1 : -1; }
/* Derived from util_cred.c:readStaticIdentityFile() */ OM_uint32 readChannelBindingsType(OM_uint32 *minor, char **cb_type) { OM_uint32 major, tmpMinor; FILE *fp = NULL; char buf[BUFSIZ]; char *ccacheName; int i = 0; #ifndef WIN32 struct passwd *pw = NULL, pwd; char pwbuf[BUFSIZ]; #endif *cb_type = NULL; ccacheName = getenv("GSS_SAML_EC_CB_TYPE_FILE"); if (ccacheName == NULL) { #ifdef WIN32 TCHAR szPath[MAX_PATH]; if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, /* |CSIDL_FLAG_CREATE */ NULL, /* User access token */ 0, /* SHGFP_TYPE_CURRENT */ szPath))) { major = GSS_S_CRED_UNAVAIL; *minor = GSSEAP_GET_LAST_ERROR(); /* XXX */ goto cleanup; } snprintf(buf, sizeof(buf), "%s/.gss_saml_ec_cb_type", szPath); #else if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 || pw == NULL || pw->pw_dir == NULL) { major = GSS_S_CRED_UNAVAIL; *minor = GSSEAP_GET_LAST_ERROR(); goto cleanup; } snprintf(buf, sizeof(buf), "%s/.gss_saml_ec_cb_type", pw->pw_dir); #endif /* WIN32 */ ccacheName = buf; } if (MECH_SAML_EC_DEBUG) printf("Looking for Channel Bindings Type in (%s)\n", ccacheName); fp = fopen(ccacheName, "r"); if (fp == NULL) { fprintf(stderr, "ERROR: Channel Bindings Type not specified in (%s) nor" " in a file pointed to by the environment variable: " " GSS_SAML_EC_CB_TYPE_FILE\n", ccacheName); major = GSS_S_BAD_BINDINGS; *minor = GSSEAP_SAML_BINDING_FAILURE; goto cleanup; } if (fgets(buf, sizeof(buf), fp) != NULL) { if (strlen(buf) && buf[strlen(buf) - 1] == '\n') { buf[strlen(buf) - 1] = '\0'; } *cb_type = strdup(buf); } if (*cb_type == NULL || strlen(*cb_type) == 0) { fprintf(stderr, "ERROR: Channel Bindings Type not specified\n"); major = GSS_S_BAD_BINDINGS; *minor = GSSEAP_SAML_BINDING_FAILURE; goto cleanup; } major = GSS_S_COMPLETE; *minor = 0; cleanup: if (fp != NULL) fclose(fp); if (GSS_ERROR(major)) { if (*cb_type) free(*cb_type); *cb_type = NULL; } memset(buf, 0, sizeof(buf)); return major; }
gfarmExportedCredential * gfarmGssExportCredential(gss_cred_id_t cred, OM_uint32 *statPtr) { gfarmExportedCredential *exportedCred = NULL; OM_uint32 majStat = 0; OM_uint32 minStat = 0; gss_buffer_desc buf = GSS_C_EMPTY_BUFFER; char *exported, *filename, *env; static char exported_name[] = "X509_USER_DELEG_PROXY="; static char env_name[] = "X509_USER_PROXY="; static char file_prefix[] = "FILE:"; majStat = gss_export_cred(&minStat, cred, GSS_C_NO_OID, 1, &buf); if (GSS_ERROR(majStat)) goto Done; exported = buf.value; for (filename = exported; *filename != '\0'; filename++) if (!isalnum(*(unsigned char *)filename) && *filename != '_') break; if (*filename != '=') { /* not an environment variable */ majStat = GSS_S_UNAVAILABLE; goto Done; } filename++; if (memcmp(exported, exported_name, sizeof(exported_name) - 1) == 0) { GFARM_MALLOC_ARRAY(env, sizeof(env_name) + strlen(filename)); if (env == NULL) { majStat = GSS_S_FAILURE; goto Done; } memcpy(env, env_name, sizeof(env_name) - 1); strcpy(env + sizeof(env_name) - 1, filename); filename = env + sizeof(env_name) - 1; } else { env = strdup(exported); if (env == NULL) { majStat = GSS_S_FAILURE; goto Done; } filename = env + (filename - exported); } if (memcmp(filename, file_prefix, sizeof(file_prefix) - 1) == 0) filename += sizeof(file_prefix) - 1; GFARM_MALLOC(exportedCred); if (exportedCred == NULL) { free(env); majStat = GSS_S_FAILURE; goto Done; } exportedCred->env = env; exportedCred->filename = access(filename, R_OK) == 0 ? filename : NULL; Done: gss_release_buffer(&minStat, &buf); if (statPtr != NULL) *statPtr = majStat; if (GSS_ERROR(majStat)) { gflog_debug(GFARM_MSG_1000800, "failed to export credential (%u)(%u)", majStat, minStat); } return exportedCred; }
static OM_uint32 readStaticIdentityFile(OM_uint32 *minor, gss_buffer_t defaultIdentity, gss_buffer_t defaultPassword) { OM_uint32 major, tmpMinor; FILE *fp = NULL; char buf[BUFSIZ]; char *ccacheName; int i = 0; #ifndef WIN32 struct passwd *pw = NULL, pwd; char pwbuf[BUFSIZ]; #endif defaultIdentity->length = 0; defaultIdentity->value = NULL; if (defaultPassword != GSS_C_NO_BUFFER) { defaultPassword->length = 0; defaultPassword->value = NULL; } ccacheName = getenv("GSSEAP_IDENTITY"); if (ccacheName == NULL) { #ifdef WIN32 TCHAR szPath[MAX_PATH]; if (!SUCCEEDED(SHGetFolderPath(NULL, CSIDL_APPDATA, /* |CSIDL_FLAG_CREATE */ NULL, /* User access token */ 0, /* SHGFP_TYPE_CURRENT */ szPath))) { major = GSS_S_CRED_UNAVAIL; *minor = GSSEAP_GET_LAST_ERROR(); /* XXX */ goto cleanup; } snprintf(buf, sizeof(buf), "%s/.gss_eap_id", szPath); #else if (getpwuid_r(getuid(), &pwd, pwbuf, sizeof(pwbuf), &pw) != 0 || pw == NULL || pw->pw_dir == NULL) { major = GSS_S_CRED_UNAVAIL; *minor = GSSEAP_GET_LAST_ERROR(); goto cleanup; } snprintf(buf, sizeof(buf), "%s/.gss_eap_id", pw->pw_dir); #endif /* WIN32 */ ccacheName = buf; } fp = fopen(ccacheName, "r"); if (fp == NULL) { major = GSS_S_CRED_UNAVAIL; *minor = GSSEAP_NO_DEFAULT_CRED; goto cleanup; } while (fgets(buf, sizeof(buf), fp) != NULL) { gss_buffer_desc src, *dst; src.length = strlen(buf); src.value = buf; if (src.length == 0) break; if (buf[src.length - 1] == '\n') { buf[src.length - 1] = '\0'; if (--src.length == 0) break; } if (i == 0) dst = defaultIdentity; else if (i == 1) dst = defaultPassword; else break; if (dst != GSS_C_NO_BUFFER) { major = duplicateBuffer(minor, &src, dst); if (GSS_ERROR(major)) goto cleanup; } i++; } if (defaultIdentity->length == 0) { major = GSS_S_CRED_UNAVAIL; *minor = GSSEAP_NO_DEFAULT_CRED; goto cleanup; } major = GSS_S_COMPLETE; *minor = 0; cleanup: if (fp != NULL) fclose(fp); if (GSS_ERROR(major)) { gss_release_buffer(&tmpMinor, defaultIdentity); zeroAndReleasePassword(defaultPassword); } memset(buf, 0, sizeof(buf)); return major; }
static char * gss_mk_err(OM_uint32 maj_stat, OM_uint32 min_stat, const char *preamble) { gss_buffer_desc status; OM_uint32 new_stat; OM_uint32 cur_stat; OM_uint32 msg_ctx = 0; OM_uint32 ret; int type; size_t newlen; char *str = NULL; char *tmp = NULL; cur_stat = maj_stat; type = GSS_C_GSS_CODE; for (;;) { /* * GSS_S_FAILURE produces a rather unhelpful message, so * we skip straight to the mech specific error in this case. */ if (type == GSS_C_GSS_CODE && cur_stat == GSS_S_FAILURE) { type = GSS_C_MECH_CODE; cur_stat = min_stat; } ret = gss_display_status(&new_stat, cur_stat, type, GSS_C_NO_OID, &msg_ctx, &status); if (GSS_ERROR(ret)) return str; /* XXXrcd: hmmm, not quite?? */ if (str) newlen = strlen(str); else newlen = strlen(preamble); newlen += status.length + 3; tmp = str; str = malloc(newlen); if (!str) { gss_release_buffer(&new_stat, &status); return tmp; /* XXXrcd: hmmm, not quite?? */ } snprintf(str, newlen, "%s%s%.*s", tmp?tmp:preamble, tmp?", ":": ", (int)status.length, (char *)status.value); gss_release_buffer(&new_stat, &status); free(tmp); /* * If we are finished processing for maj_stat, then * move onto min_stat. */ if (msg_ctx == 0 && type == GSS_C_GSS_CODE && min_stat != 0) { type = GSS_C_MECH_CODE; cur_stat = min_stat; continue; } if (msg_ctx == 0) break; } return str; }
OM_uint32 gssEapAcquireCred(OM_uint32 *minor, const gss_name_t desiredName, OM_uint32 timeReq GSSEAP_UNUSED, const gss_OID_set desiredMechs, int credUsage, gss_cred_id_t *pCred, gss_OID_set *pActualMechs, OM_uint32 *timeRec) { OM_uint32 major, tmpMinor; gss_cred_id_t cred; /* XXX TODO validate with changed set_cred_option API */ *pCred = GSS_C_NO_CREDENTIAL; major = gssEapAllocCred(minor, &cred); if (GSS_ERROR(major)) goto cleanup; switch (credUsage) { case GSS_C_BOTH: cred->flags |= CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT; break; case GSS_C_INITIATE: cred->flags |= CRED_FLAG_INITIATE; break; case GSS_C_ACCEPT: cred->flags |= CRED_FLAG_ACCEPT; break; default: major = GSS_S_FAILURE; *minor = GSSEAP_BAD_USAGE; goto cleanup; break; } major = gssEapValidateMechs(minor, desiredMechs); if (GSS_ERROR(major)) goto cleanup; major = duplicateOidSet(minor, desiredMechs, &cred->mechanisms); if (GSS_ERROR(major)) goto cleanup; if (desiredName != GSS_C_NO_NAME) { GSSEAP_MUTEX_LOCK(&desiredName->mutex); major = gssEapDuplicateName(minor, desiredName, &cred->name); if (GSS_ERROR(major)) { GSSEAP_MUTEX_UNLOCK(&desiredName->mutex); goto cleanup; } GSSEAP_MUTEX_UNLOCK(&desiredName->mutex); } #ifdef GSSEAP_ENABLE_ACCEPTOR if (cred->flags & CRED_FLAG_ACCEPT) { #ifdef MECH_EAP struct rs_context *radContext; major = gssEapCreateRadiusContext(minor, cred, &radContext); if (GSS_ERROR(major)) goto cleanup; rs_context_destroy(radContext); #endif } #endif if (pActualMechs != NULL) { major = duplicateOidSet(minor, cred->mechanisms, pActualMechs); if (GSS_ERROR(major)) goto cleanup; } if (timeRec != NULL) *timeRec = GSS_C_INDEFINITE; *pCred = cred; major = GSS_S_COMPLETE; *minor = 0; cleanup: if (GSS_ERROR(major)) gssEapReleaseCred(&tmpMinor, &cred); return major; }
static OM_uint32 spnego_reply (OM_uint32 * minor_status, const gssspnego_cred cred, 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 ) { OM_uint32 ret, minor; NegTokenResp resp; u_char oidbuf[17]; size_t oidlen; size_t len, taglen; gss_OID_desc mech; int require_mic; size_t buf_len; gss_buffer_desc mic_buf, mech_buf; gss_buffer_desc mech_output_token; gssspnego_ctx ctx; *minor_status = 0; ctx = (gssspnego_ctx)*context_handle; output_token->length = 0; output_token->value = NULL; mech_output_token.length = 0; mech_output_token.value = NULL; mech_buf.value = NULL; mech_buf.length = 0; ret = der_match_tag_and_length(input_token->value, input_token->length, ASN1_C_CONTEXT, CONS, 1, &len, &taglen); if (ret) return ret; if (len > input_token->length - taglen) return ASN1_OVERRUN; ret = decode_NegTokenResp((const unsigned char *)input_token->value+taglen, len, &resp, NULL); if (ret) { *minor_status = ENOMEM; return GSS_S_FAILURE; } if (resp.negResult == NULL || *(resp.negResult) == reject || resp.supportedMech == NULL) { free_NegTokenResp(&resp); return GSS_S_BAD_MECH; } ret = der_put_oid(oidbuf + sizeof(oidbuf) - 1, sizeof(oidbuf), resp.supportedMech, &oidlen); if (ret || (oidlen == GSS_SPNEGO_MECHANISM->length && memcmp(oidbuf + sizeof(oidbuf) - oidlen, GSS_SPNEGO_MECHANISM->elements, oidlen) == 0)) { /* Avoid recursively embedded SPNEGO */ free_NegTokenResp(&resp); return GSS_S_BAD_MECH; } HEIMDAL_MUTEX_lock(&ctx->ctx_id_mutex); if (resp.responseToken != NULL) { gss_buffer_desc mech_input_token; mech_input_token.length = resp.responseToken->length; mech_input_token.value = resp.responseToken->data; mech.length = oidlen; mech.elements = oidbuf + sizeof(oidbuf) - oidlen; /* Fall through as if the negotiated mechanism was requested explicitly */ ret = gss_init_sec_context(&minor, (cred != NULL) ? cred->negotiated_cred_id : GSS_C_NO_CREDENTIAL, &ctx->negotiated_ctx_id, target_name, &mech, req_flags, time_req, input_chan_bindings, &mech_input_token, &ctx->negotiated_mech_type, &mech_output_token, &ctx->mech_flags, &ctx->mech_time_rec); if (GSS_ERROR(ret)) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegTokenResp(&resp); *minor_status = minor; return ret; } if (ret == GSS_S_COMPLETE) { ctx->open = 1; } } if (*(resp.negResult) == request_mic) { ctx->require_mic = 1; } if (ctx->open) { /* * Verify the mechListMIC if one was provided or CFX was * used and a non-preferred mechanism was selected */ if (resp.mechListMIC != NULL) { require_mic = 1; } else { ret = _gss_spnego_require_mechlist_mic(minor_status, ctx, &require_mic); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegTokenResp(&resp); gss_release_buffer(&minor, &mech_output_token); return ret; } } } else { require_mic = 0; } if (require_mic) { ASN1_MALLOC_ENCODE(MechTypeList, mech_buf.value, mech_buf.length, &ctx->initiator_mech_types, &buf_len, ret); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free_NegTokenResp(&resp); gss_release_buffer(&minor, &mech_output_token); *minor_status = ret; return GSS_S_FAILURE; } if (mech_buf.length != buf_len) abort(); if (resp.mechListMIC == NULL) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); free_NegTokenResp(&resp); *minor_status = 0; return GSS_S_DEFECTIVE_TOKEN; } mic_buf.length = resp.mechListMIC->length; mic_buf.value = resp.mechListMIC->data; if (mech_output_token.length == 0) { ret = gss_verify_mic(minor_status, ctx->negotiated_ctx_id, &mech_buf, &mic_buf, NULL); if (ret) { HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); free(mech_buf.value); gss_release_buffer(&minor, &mech_output_token); free_NegTokenResp(&resp); return GSS_S_DEFECTIVE_TOKEN; } ctx->verified_mic = 1; } } ret = spnego_reply_internal(minor_status, ctx, require_mic ? &mech_buf : NULL, &mech_output_token, output_token); if (mech_buf.value != NULL) free(mech_buf.value); free_NegTokenResp(&resp); gss_release_buffer(&minor, &mech_output_token); if (actual_mech_type) *actual_mech_type = ctx->negotiated_mech_type; if (ret_flags) *ret_flags = ctx->mech_flags; if (time_rec) *time_rec = ctx->mech_time_rec; HEIMDAL_MUTEX_unlock(&ctx->ctx_id_mutex); return ret; }
OM_uint32 gssEapInquireCred(OM_uint32 *minor, gss_cred_id_t cred, gss_name_t *name, OM_uint32 *pLifetime, gss_cred_usage_t *cred_usage, gss_OID_set *mechanisms) { OM_uint32 major; time_t now, lifetime; if (name != NULL) { major = gssEapResolveCredIdentity(minor, cred); if (GSS_ERROR(major)) goto cleanup; if (cred->name != GSS_C_NO_NAME) { major = gssEapDuplicateName(minor, cred->name, name); if (GSS_ERROR(major)) goto cleanup; } else *name = GSS_C_NO_NAME; } if (cred_usage != NULL) { OM_uint32 flags = (cred->flags & (CRED_FLAG_INITIATE | CRED_FLAG_ACCEPT)); switch (flags) { case CRED_FLAG_INITIATE: *cred_usage = GSS_C_INITIATE; break; case CRED_FLAG_ACCEPT: *cred_usage = GSS_C_ACCEPT; break; default: *cred_usage = GSS_C_BOTH; break; } } if (mechanisms != NULL) { if (cred->mechanisms != GSS_C_NO_OID_SET) major = duplicateOidSet(minor, cred->mechanisms, mechanisms); else major = gssEapIndicateMechs(minor, mechanisms); if (GSS_ERROR(major)) goto cleanup; } if (cred->expiryTime == 0) { lifetime = GSS_C_INDEFINITE; } else { now = time(NULL); lifetime = now - cred->expiryTime; if (lifetime < 0) lifetime = 0; } if (pLifetime != NULL) { *pLifetime = lifetime; } if (lifetime == 0) { major = GSS_S_CREDENTIALS_EXPIRED; *minor = GSSEAP_CRED_EXPIRED; goto cleanup; } major = GSS_S_COMPLETE; *minor = 0; cleanup: return major; }
int _gsasl_gs2_server_step (Gsasl_session * sctx, void *mech_data, const char *input, size_t input_len, char **output, size_t * output_len) { _Gsasl_gs2_server_state *state = mech_data; gss_buffer_desc bufdesc1, bufdesc2; OM_uint32 maj_stat, min_stat; gss_buffer_desc client_name; gss_OID mech_type; char tmp[4]; int res; OM_uint32 ret_flags; struct gs2_token tok; *output = NULL; *output_len = 0; switch (state->step) { case 0: if (input_len == 0) { res = GSASL_NEEDS_MORE; break; } state->step++; /* fall through */ case 1: res = gs2_parser (input, input_len, &tok); if (res < 0) return GSASL_MECHANISM_PARSE_ERROR; bufdesc1.value = tok.context_token; bufdesc1.length = tok.context_length; if (state->client) { gss_release_name (&min_stat, &state->client); state->client = GSS_C_NO_NAME; } maj_stat = gss_accept_sec_context (&min_stat, &state->context, state->cred, &bufdesc1, GSS_C_NO_CHANNEL_BINDINGS, &state->client, &mech_type, &bufdesc2, &ret_flags, NULL, NULL); if (maj_stat != GSS_S_COMPLETE && maj_stat != GSS_S_CONTINUE_NEEDED) return GSASL_GSSAPI_ACCEPT_SEC_CONTEXT_ERROR; /* XXX check wrap token */ if (ret_flags & GSS_C_PROT_READY_FLAG) { puts ("prot_ready"); /* XXX gss_wrap token */ } res = gs2_encode (bufdesc2.value, bufdesc2.length, NULL, 0, output, output_len); if (res < 0) return GSASL_GSSAPI_INIT_SEC_CONTEXT_ERROR; maj_stat = gss_release_buffer (&min_stat, &bufdesc2); if (GSS_ERROR (maj_stat)) return GSASL_GSSAPI_RELEASE_BUFFER_ERROR; if (maj_stat == GSS_S_COMPLETE) state->step++; res = GSASL_NEEDS_MORE; break; case 2: memset (tmp, 0xFF, 4); tmp[0] = GSASL_QOP_AUTH; bufdesc1.length = 4; bufdesc1.value = tmp; maj_stat = gss_wrap (&min_stat, state->context, 0, GSS_C_QOP_DEFAULT, &bufdesc1, NULL, &bufdesc2); if (GSS_ERROR (maj_stat)) return GSASL_GSSAPI_WRAP_ERROR; *output = malloc (bufdesc2.length); if (!*output) return GSASL_MALLOC_ERROR; memcpy (*output, bufdesc2.value, bufdesc2.length); *output_len = bufdesc2.length; maj_stat = gss_release_buffer (&min_stat, &bufdesc2); if (GSS_ERROR (maj_stat)) return GSASL_GSSAPI_RELEASE_BUFFER_ERROR; state->step++; res = GSASL_NEEDS_MORE; break; case 3: bufdesc1.value = (void *) input; bufdesc1.length = input_len; maj_stat = gss_unwrap (&min_stat, state->context, &bufdesc1, &bufdesc2, NULL, NULL); if (GSS_ERROR (maj_stat)) return GSASL_GSSAPI_UNWRAP_ERROR; /* [RFC 2222 section 7.2.1]: The client passes this token to GSS_Unwrap and interprets the first octet of resulting cleartext as a bit-mask specifying the security layers supported by the server and the second through fourth octets as the maximum size output_message to send to the server. The client then constructs data, with the first octet containing the bit-mask specifying the selected security layer, the second through fourth octets containing in network byte order the maximum size output_message the client is able to receive, and the remaining octets containing the authorization identity. The client passes the data to GSS_Wrap with conf_flag set to FALSE, and responds with the generated output_message. The client can then consider the server authenticated. */ if ((((char *) bufdesc2.value)[0] & GSASL_QOP_AUTH) == 0) { /* Integrity or privacy unsupported */ maj_stat = gss_release_buffer (&min_stat, &bufdesc2); return GSASL_GSSAPI_UNSUPPORTED_PROTECTION_ERROR; } gsasl_property_set_raw (sctx, GSASL_AUTHZID, (char *) bufdesc2.value + 4, bufdesc2.length - 4); maj_stat = gss_display_name (&min_stat, state->client, &client_name, &mech_type); if (GSS_ERROR (maj_stat)) return GSASL_GSSAPI_DISPLAY_NAME_ERROR; gsasl_property_set_raw (sctx, GSASL_GSSAPI_DISPLAY_NAME, client_name.value, client_name.length); maj_stat = gss_release_buffer (&min_stat, &bufdesc2); if (GSS_ERROR (maj_stat)) return GSASL_GSSAPI_RELEASE_BUFFER_ERROR; res = gsasl_callback (NULL, sctx, GSASL_VALIDATE_GSSAPI); state->step++; break; default: res = GSASL_MECHANISM_CALLED_TOO_MANY_TIMES; break; } return res; }
gss_client_response *authenticate_gss_server_step(gss_server_state *state, const char *auth_data) { OM_uint32 maj_stat; OM_uint32 min_stat; gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; int ret = AUTH_GSS_CONTINUE; gss_client_response *response = NULL; // Always clear out the old response if (state->response != NULL) { free(state->response); state->response = NULL; } // we don't need to check the authentication token if S4U2Self protocol // transition was done, because we already have the client credentials. if (state->client_creds == GSS_C_NO_CREDENTIAL) { if (auth_data && *auth_data) { int len; input_token.value = base64_decode(auth_data, &len); input_token.length = len; } else { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->message = strdup("No auth_data value in request from client"); response->return_code = AUTH_GSS_ERROR; goto end; } maj_stat = gss_accept_sec_context(&min_stat, &state->context, state->server_creds, &input_token, GSS_C_NO_CHANNEL_BINDINGS, &state->client_name, NULL, &output_token, NULL, NULL, &state->client_creds); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_accept_sec_context", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } // Grab the server response to send back to the client if (output_token.length) { state->response = base64_encode((const unsigned char *)output_token.value, output_token.length); maj_stat = gss_release_buffer(&min_stat, &output_token); } } // Get the user name maj_stat = gss_display_name(&min_stat, state->client_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_display_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } state->username = (char *)malloc(output_token.length + 1); strncpy(state->username, (char*) output_token.value, output_token.length); state->username[output_token.length] = 0; // Get the target name if no server creds were supplied if (state->server_creds == GSS_C_NO_CREDENTIAL) { gss_name_t target_name = GSS_C_NO_NAME; maj_stat = gss_inquire_context(&min_stat, state->context, NULL, &target_name, NULL, NULL, NULL, NULL, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_inquire_context", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } maj_stat = gss_display_name(&min_stat, target_name, &output_token, NULL); if (GSS_ERROR(maj_stat)) { response = gss_error(__func__, "gss_display_name", maj_stat, min_stat); response->return_code = AUTH_GSS_ERROR; goto end; } state->targetname = (char *)malloc(output_token.length + 1); strncpy(state->targetname, (char*) output_token.value, output_token.length); state->targetname[output_token.length] = 0; } if (state->constrained_delegation && state->client_creds != GSS_C_NO_CREDENTIAL) { if ((response = store_gss_creds(state)) != NULL) { goto end; } } ret = AUTH_GSS_COMPLETE; end: if (output_token.length) gss_release_buffer(&min_stat, &output_token); if (input_token.value) free(input_token.value); if(response == NULL) { response = calloc(1, sizeof(gss_client_response)); if(response == NULL) die1("Memory allocation failed"); response->return_code = ret; } // Return the response return response; }