/** * @brief Initiate Delegation * @ingroup globus_gsi_gssapi_extensions_delegation * @details * This functions drives the initiating side of the credential * delegation process. It is expected to be called in tandem with the * gss_accept_delegation function. * * @param minor_status * The minor status returned by this function. This parameter * will be 0 upon success. * @param context_handle * The security context over which the credential is * delegated. * @param cred_handle * The credential to be delegated. May be GSS_C_NO_CREDENTIAL * in which case the credential associated with the security * context is used. * @param desired_mech * The desired security mechanism. Currently not used. May be * GSS_C_NO_OID. * @param extension_oids * A set of extension OIDs corresponding to buffers in the * extension_buffers parameter below. The extensions specified * will be added to the delegated credential. May be * GSS_C_NO_BUFFER_SET. * @param extension_buffers * A set of extension buffers corresponding to OIDs in the * extension_oids parameter above. May be * GSS_C_NO_BUFFER_SET. * @param input_token * The token that was produced by a prior call to * gss_accept_delegation. This parameter will be ignored the * first time this function is called. * @param req_flags * Flags that modify the behavior of the function. Currently * only GSS_C_GLOBUS_SSL_COMPATIBLE and * GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG are checked for. The * GSS_C_GLOBUS_SSL_COMPATIBLE flag results in tokens that * aren't wrapped and GSS_C_GLOBUS_LIMITED_DELEG_PROXY_FLAG * causes the delegated proxy to be limited (requires that no * extensions are specified. * * @param time_req * The requested period of validity (seconds) of the delegated * credential. Passing a time_req of 0 cause the delegated credential to * have the same lifetime as the credential that issued it. * @param output_token * A token that should be passed to gss_accept_delegation if the * return value is GSS_S_CONTINUE_NEEDED. * @retval GSS_S_COMPLETE Success * @retval GSS_S_CONTINUE_NEEDED This function needs to be called again. * @retval GSS_S_FAILURE upon failure */ OM_uint32 GSS_CALLCONV gss_init_delegation( OM_uint32 * minor_status, const gss_ctx_id_t context_handle, const gss_cred_id_t cred_handle, const gss_OID desired_mech, const gss_OID_set extension_oids, const gss_buffer_set_t extension_buffers, const gss_buffer_t input_token, OM_uint32 req_flags, OM_uint32 time_req, gss_buffer_t output_token) { BIO * bio = NULL; BIO * read_bio = NULL; BIO * write_bio = NULL; OM_uint32 major_status = GSS_S_COMPLETE; OM_uint32 local_minor_status; gss_ctx_id_desc * context; gss_cred_id_desc * cred; X509 * cert = NULL; STACK_OF(X509) * cert_chain = NULL; PROXYCERTINFO * pci; globus_gsi_cert_utils_cert_type_t cert_type; int index; globus_result_t local_result = GLOBUS_SUCCESS; static char * _function_name_ = "init_delegation"; 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 == GSS_C_NO_CONTEXT) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid context_handle passed to function"))); major_status = GSS_S_FAILURE; goto exit; } context = (gss_ctx_id_desc *) context_handle; cred = (gss_cred_id_desc *) cred_handle; if (cred_handle == GSS_C_NO_CREDENTIAL) { cred = (gss_cred_id_desc *) context->cred_handle; } if(cred == GSS_C_NO_CREDENTIAL) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Couldn't initialize delegation credential handle"))); major_status = GSS_S_FAILURE; goto exit; } if(desired_mech != GSS_C_NO_OID && desired_mech != (gss_OID) gss_mech_globus_gssapi_openssl) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid desired_mech passed to function"))); major_status = GSS_S_FAILURE; goto exit; } if(extension_oids != GSS_C_NO_OID_SET && (extension_buffers == GSS_C_NO_BUFFER_SET || extension_oids->count != extension_buffers->count)) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid extension parameters passed to function"))); major_status = GSS_S_FAILURE; goto exit; } if(output_token == GSS_C_NO_BUFFER) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid output_token passed to function"))); major_status = GSS_S_FAILURE; goto exit; } output_token->length = 0; if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE) { bio = BIO_new(BIO_s_mem()); read_bio = bio; write_bio = bio; } else { bio = context->gss_sslbio; } /* lock the context mutex */ globus_mutex_lock(&context->mutex); /* pass the input to the BIO */ if(context->delegation_state != GSS_DELEGATION_START) { /* * first time there is no input token, but after that * there will always be one */ if(input_token == GSS_C_NO_BUFFER) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid input_token passed to function: " "delegation is not at initial state"))); major_status = GSS_S_FAILURE; goto mutex_unlock; } major_status = globus_i_gsi_gss_put_token(&local_minor_status, context, read_bio, input_token); if (GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL); goto mutex_unlock; } } /* delegation state machine */ switch (context->delegation_state) { case GSS_DELEGATION_START: /* start delegation by sending a "D" */ BIO_write(bio, "D", 1); context->delegation_state = GSS_DELEGATION_SIGN_CERT; break; case GSS_DELEGATION_SIGN_CERT: /* get the returned cert from the ssl BIO, make sure it is * correct and then sign it and place it in the output_token */ local_result = globus_gsi_proxy_inquire_req(context->proxy_handle, bio); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; goto mutex_unlock; } local_result = globus_gsi_cred_get_cert_type( context->cred_handle->cred_handle, &cert_type); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } /* set the requested time */ if(time_req != 0) { if(time_req%60) { /* round up */ time_req += 60; } local_result = globus_gsi_proxy_handle_set_time_valid( context->proxy_handle, time_req/60); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } } /* clear the proxycertinfo */ local_result = globus_gsi_proxy_handle_clear_cert_info(context->proxy_handle); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } if(cert_type == GLOBUS_GSI_CERT_UTILS_TYPE_CA) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } local_result = globus_gsi_proxy_handle_set_type( context->proxy_handle, (req_flags & GSS_C_GLOBUS_DELEGATE_LIMITED_PROXY_FLAG) ? GLOBUS_GSI_CERT_UTILS_TYPE_LIMITED_PROXY : GLOBUS_GSI_CERT_UTILS_TYPE_IMPERSONATION_PROXY); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } /* set the proxycertinfo here */ if(extension_oids != GSS_C_NO_OID_SET) { if(GLOBUS_GSI_CERT_UTILS_IS_GSI_2_PROXY(cert_type)) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("A restricted globus proxy may not be created " "from a legacy globus proxy"))); context->delegation_state = GSS_DELEGATION_DONE; major_status = GSS_S_FAILURE; goto mutex_unlock; } for(index = 0; index < extension_oids->count; index++) { if(g_OID_equal((gss_OID) &extension_oids->elements[index], gss_proxycertinfo_extension)) { pci = extension_buffers->elements[index].value; local_result = globus_gsi_proxy_handle_set_proxy_cert_info( context->proxy_handle, pci); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } } } } local_result = globus_gsi_proxy_sign_req( context->proxy_handle, cred->cred_handle, bio); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_PROXY); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } local_result = globus_gsi_cred_get_cert(cred->cred_handle, &cert); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } /* push the cert used to sign the proxy */ i2d_X509_bio(bio, cert); X509_free(cert); /* push the number of certs in the cert chain */ local_result = globus_gsi_cred_get_cert_chain(cred->cred_handle, &cert_chain); if(local_result != GLOBUS_SUCCESS) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_result, GLOBUS_GSI_GSSAPI_ERROR_WITH_GSI_CREDENTIAL); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } for(index = 0; index < sk_X509_num(cert_chain); index++) { cert = sk_X509_value(cert_chain, index); if(!cert) { GLOBUS_GSI_GSSAPI_OPENSSL_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_OPENSSL, (_GGSL("Couldn't get cert from cert chain"))); major_status = GSS_S_FAILURE; context->delegation_state = GSS_DELEGATION_DONE; goto mutex_unlock; } i2d_X509_bio(bio, cert); } sk_X509_pop_free(cert_chain, X509_free); /* reset state machine */ context->delegation_state = GSS_DELEGATION_START; break; case GSS_DELEGATION_COMPLETE_CRED: case GSS_DELEGATION_DONE: break; } major_status = globus_i_gsi_gss_get_token(&local_minor_status, context, write_bio, output_token); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_TOKEN_FAIL); goto mutex_unlock; } if (context->delegation_state != GSS_DELEGATION_START) { major_status |= GSS_S_CONTINUE_NEEDED; } mutex_unlock: globus_mutex_unlock(&context->mutex); exit: if(req_flags & GSS_C_GLOBUS_SSL_COMPATIBLE) { BIO_free(bio); } GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT; return major_status; }
int globus_i_gram_get_tg_gateway_user( gss_ctx_id_t context, globus_gsi_cred_handle_t peer_cred, char ** gateway_user) { #if HAVE_LIBXML2 OM_uint32 maj_stat, min_stat; gss_buffer_set_t data_set; ASN1_UTF8STRING * asn1_str; char * assertion_string; unsigned char * p; long pl; xmlDocPtr doc; xmlXPathContextPtr xpath_ctx; xmlXPathObjectPtr xresult; int rc; ASN1_OBJECT * asn1_desired_object = NULL; int cert_count; int found_index; int chain_index; X509 *cert; X509_EXTENSION * extension; ASN1_OCTET_STRING *asn1_oct_string; STACK_OF(X509) *chain = NULL; *gateway_user = NULL; if (context == GSS_C_NO_CONTEXT && peer_cred != NULL) { globus_result_t result; /* This basically duplicates the gss_inquire_sec_context_by_oid(), but * instead uses a gsi credential object */ rc = GLOBUS_SUCCESS; asn1_desired_object = ASN1_OBJECT_new(); if (asn1_desired_object == NULL) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_MALLOC_FAILED; goto no_extension_in_cred_chain; } asn1_desired_object->length = globus_l_saml_oid_desc.length; asn1_desired_object->data = globus_l_saml_oid_desc.elements; result = globus_gsi_cred_get_cert_chain(peer_cred, &chain); if (result != GLOBUS_SUCCESS) { char * msg; msg = globus_error_print_friendly( globus_error_peek(result)); globus_gram_protocol_error_7_hack_replace_message( msg); free(msg); rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; goto no_extension_in_cred_chain; } cert_count = sk_X509_num(chain); found_index = -1; for (chain_index = 0; chain_index < cert_count; chain_index++) { cert = sk_X509_value(chain, chain_index); found_index = X509_get_ext_by_OBJ(cert, asn1_desired_object, found_index); if (found_index >= 0) { extension = X509_get_ext(cert, found_index); if (extension == NULL) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; globus_gram_protocol_error_7_hack_replace_message( "Unable to extract SAML assertion extension from certificate chain"); goto no_extension_in_cred_chain; } asn1_oct_string = X509_EXTENSION_get_data(extension); if (asn1_oct_string == NULL) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; globus_gram_protocol_error_7_hack_replace_message( "Unable to extract SAML assertion extension from certificate chain"); goto no_extension_in_cred_chain; } p = asn1_oct_string->data; asn1_str = d2i_ASN1_UTF8STRING(NULL, (void *)&p, asn1_oct_string->length); if (asn1_str == NULL) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; globus_gram_protocol_error_7_hack_replace_message( "Unable to convert SAML assertion text from DER to UTF8"); goto no_extension_in_cred_chain; } assertion_string = malloc(asn1_str->length + 1); if (assertion_string == NULL) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_MALLOC_FAILED; goto no_extension_in_cred_chain; } memcpy(assertion_string, asn1_str->data, asn1_str->length); assertion_string[asn1_str->length] = 0; break; } } if (chain_index == cert_count) { goto no_extension_in_cred_chain; } } else if (context == GSS_C_NO_CONTEXT) { rc = GLOBUS_SUCCESS; goto no_context; } else { maj_stat = gss_inquire_sec_context_by_oid( &min_stat, context, globus_saml_oid, &data_set); if (GSS_ERROR(maj_stat)) { globus_gram_protocol_error_7_hack_replace_message( "Error extracting SAML assertion"); rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; goto inquire_failed; } /* We'll process only the first SAML assertion bound in the X.509 chain */ if (data_set->count < 1) { rc = GLOBUS_SUCCESS; goto empty_data_set; } p = data_set->elements[0].value; pl = data_set->elements[0].length; /* Convert DER-Encoded string to UTF8 */ asn1_str = d2i_ASN1_UTF8STRING(NULL, (void *) &p, pl); if (!asn1_str) { globus_gram_protocol_error_7_hack_replace_message( "Error decoding SAML assertion"); rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; goto utfstring_failed; } assertion_string = malloc(asn1_str->length + 1); if (assertion_string == NULL) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_MALLOC_FAILED; goto assertion_string_malloc_failed; } memcpy(assertion_string, asn1_str->data, asn1_str->length); assertion_string[asn1_str->length] = 0; } /* Parse SAML assertion */ doc = xmlParseDoc(BAD_CAST assertion_string); if (doc == NULL) { globus_gram_protocol_error_7_hack_replace_message( "Error parsing SAML assertion"); rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; goto parse_assertion_failed; } xmlXPathInit(); /* Use XPATH to extract Issuer */ xpath_ctx = xmlXPathNewContext(doc); if (xpath_ctx == NULL) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_MALLOC_FAILED; goto xpath_ctx_init_failed; } rc = xmlXPathRegisterNs( xpath_ctx, (xmlChar *) "s", (xmlChar *) "urn:oasis:names:tc:SAML:1.0:assertion"); if (rc != 0) { rc = GLOBUS_GRAM_PROTOCOL_ERROR_MALLOC_FAILED; goto xpath_register_ns_failed; } xresult = xmlXPathEvalExpression( (const xmlChar *) "string(/s:Assertion/@Issuer)", xpath_ctx); if (xresult == NULL) { globus_gram_protocol_error_7_hack_replace_message( "Error processing SAML assertion: no \"Issuer\" attribute"); rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; goto xpath_eval_issuer_failed; } if (! globus_l_tg_saml_assertion_is_self_issued( context, (const char *) xresult->stringval)) { /* Ignore non-self issued assertions */ rc = GLOBUS_SUCCESS; goto non_self_issued; } xmlXPathFreeObject(xresult); /* Use XPATH to extract the sender-vouches, self-issued, TG principal name * Subject attribute from the Assertion's AuthenticationStatement */ xresult = xmlXPathEvalExpression( (const xmlChar *) "string(/s:Assertion/s:AuthenticationStatement/s:Subject[string(s:SubjectConfirmation/s:ConfirmationMethod) = 'urn:oasis:names:tc:SAML:1.0:cm:sender-vouches' and s:NameIdentifier/@Format = 'http://teragrid.org/names/nameid-format/principalname']/s:NameIdentifier[1])", xpath_ctx); if (xresult == NULL) { globus_gram_protocol_error_7_hack_replace_message( "Error processing SAML assertion: no teragrid principal"); rc = GLOBUS_GRAM_PROTOCOL_ERROR_AUTHORIZATION; goto get_gateway_name_failed; } if (xresult != NULL && xresult->stringval != NULL && *(xresult->stringval) != 0) { *gateway_user = strdup((char *) xresult->stringval); } get_gateway_name_failed: non_self_issued: if (xresult != NULL) { xmlXPathFreeObject(xresult); } xpath_eval_issuer_failed: xpath_register_ns_failed: xmlXPathFreeContext(xpath_ctx); xpath_ctx_init_failed: xmlFreeDoc(doc); parse_assertion_failed: free(assertion_string); assertion_string_malloc_failed: ASN1_UTF8STRING_free(asn1_str); utfstring_failed: empty_data_set: gss_release_buffer_set(&min_stat, &data_set); inquire_failed: no_extension_in_cred_chain: no_context: if (asn1_desired_object != NULL) { ASN1_OBJECT_free(asn1_desired_object); } if (chain != NULL) { sk_X509_free(chain); } return rc; #else *gateway_user = NULL; return GLOBUS_SUCCESS; #endif /* HAVE_LIBXML2 */ }