OM_uint32 gss_krb5_copy_ccache(OM_uint32 *minor_status, gss_cred_id_t cred, krb5_ccache out) { gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; krb5_context context; krb5_error_code kret; krb5_ccache id; OM_uint32 ret; char *str; ret = gss_inquire_cred_by_oid(minor_status, cred, GSS_KRB5_COPY_CCACHE_X, &data_set); if (ret) return ret; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } kret = krb5_init_context(&context); if (kret) { *minor_status = kret; gss_release_buffer_set(minor_status, &data_set); return GSS_S_FAILURE; } kret = asprintf(&str, "%.*s", (int)data_set->elements[0].length, (char *)data_set->elements[0].value); gss_release_buffer_set(minor_status, &data_set); if (kret == -1) { *minor_status = ENOMEM; return GSS_S_FAILURE; } kret = krb5_cc_resolve(context, str, &id); free(str); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } kret = krb5_cc_copy_cache(context, id, out); krb5_cc_close(context, id); krb5_free_context(context); if (kret) { *minor_status = kret; return GSS_S_FAILURE; } return ret; }
/* Owns data on success */ static krb5_error_code data_list_to_buffer_set(krb5_context context, krb5_data *data, gss_buffer_set_t *buffer_set) { gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; OM_uint32 minor_status; int i; krb5_error_code code = 0; if (data == NULL) goto cleanup; if (buffer_set == NULL) goto cleanup; if (GSS_ERROR(gss_create_empty_buffer_set(&minor_status, &set))) { assert(minor_status != 0); code = minor_status; goto cleanup; } for (i = 0; data[i].data != NULL; i++) ; set->count = i; set->elements = gssalloc_calloc(i, sizeof(gss_buffer_desc)); if (set->elements == NULL) { gss_release_buffer_set(&minor_status, &set); code = ENOMEM; goto cleanup; } /* * Copy last element first so data remains properly * NULL-terminated in case of allocation failure * in data_to_gss() on windows. */ for (i = set->count-1; i >= 0; i--) { if (data_to_gss(&data[i], &set->elements[i])) { gss_release_buffer_set(&minor_status, &set); code = ENOMEM; goto cleanup; } } cleanup: krb5int_free_data_list(context, data); if (buffer_set != NULL) *buffer_set = set; return code; }
OM_uint32 gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, time_t *authtime) { gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; OM_uint32 maj_stat; if (context_handle == GSS_C_NO_CONTEXT) { *minor_status = EINVAL; return GSS_S_FAILURE; } maj_stat = gss_inquire_sec_context_by_oid (minor_status, context_handle, GSS_KRB5_GET_AUTHTIME_X, &data_set); if (maj_stat) return maj_stat; if (data_set == GSS_C_NO_BUFFER_SET) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } if (data_set->count != 1) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } if (data_set->elements[0].length != 4) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } { unsigned char *buf = data_set->elements[0].value; *authtime = (buf[3] <<24) | (buf[2] << 16) | (buf[1] << 8) | (buf[0] << 0); } gss_release_buffer_set(minor_status, &data_set); *minor_status = 0; return GSS_S_COMPLETE; }
static NTSTATUS SMBGssGetSessionKey( gss_ctx_id_t Context, PBYTE* ppSessionKey, PDWORD pdwSessionKeyLength ) { NTSTATUS status = STATUS_SUCCESS; PBYTE pSessionKey = NULL; DWORD dwSessionKeyLength = 0; OM_uint32 gssMajor = GSS_S_COMPLETE; OM_uint32 gssMinor = 0; gss_buffer_set_t sessionKey = NULL; gssMajor = gss_inquire_sec_context_by_oid( &gssMinor, Context, GSS_C_INQ_SSPI_SESSION_KEY, &sessionKey); if (gssMajor != GSS_S_COMPLETE) { smb_display_status("gss_inquire_sec_context_by_oid", gssMajor, gssMinor); // TODO - error code conversion status = gssMajor; BAIL_ON_LWIO_ERROR(status); } // The key is in element 0 and the key type OID is in element 1 if (!sessionKey || (sessionKey->count < 1) || !sessionKey->elements[0].value || (0 == sessionKey->elements[0].length)) { LWIO_ASSERT_MSG(FALSE, "Invalid session key"); status = STATUS_ASSERTION_FAILURE; BAIL_ON_LWIO_ERROR(status); } status = LW_RTL_ALLOCATE(&pSessionKey, BYTE, sessionKey->elements[0].length); BAIL_ON_LWIO_ERROR(status); memcpy(pSessionKey, sessionKey->elements[0].value, sessionKey->elements[0].length); dwSessionKeyLength = sessionKey->elements[0].length; cleanup: gss_release_buffer_set(&gssMinor, &sessionKey); *ppSessionKey = pSessionKey; *pdwSessionKeyLength = dwSessionKeyLength; return status; error: LWIO_SAFE_FREE_MEMORY(pSessionKey); dwSessionKeyLength = 0; goto cleanup; }
void _gss_spnego_fixup_ntlm(gssspnego_ctx ctx) { if (gss_oid_equal(ctx->negotiated_mech_type, GSS_NTLM_MECHANISM)) { gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET; OM_uint32 junk; gss_inquire_sec_context_by_oid(&junk, ctx->negotiated_ctx_id, GSS_C_NTLM_RESET_KEYS, &buffer_set); gss_release_buffer_set(&junk, &buffer_set); } }
OM_uint32 gss_krb5_get_tkt_flags(OM_uint32 *minor_status, gss_ctx_id_t context_handle, OM_uint32 *tkt_flags) { OM_uint32 major_status; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; if (context_handle == GSS_C_NO_CONTEXT) { *minor_status = EINVAL; return GSS_S_FAILURE; } major_status = gss_inquire_sec_context_by_oid (minor_status, context_handle, GSS_KRB5_GET_TKT_FLAGS_X, &data_set); if (major_status) return major_status; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1 || data_set->elements[0].length < 4) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } { const u_char *p = data_set->elements[0].value; *tkt_flags = (p[0] << 0) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24); } gss_release_buffer_set(minor_status, &data_set); return GSS_S_COMPLETE; }
/* * This API should go away and be replaced with an accessor * into a gss_name_t. */ OM_uint32 KRB5_CALLCONV gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int ad_type, gss_buffer_t ad_data) { gss_OID_desc req_oid; unsigned char oid_buf[GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH + 6]; OM_uint32 major_status; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; if (ad_data == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; req_oid.elements = oid_buf; req_oid.length = sizeof(oid_buf); major_status = generic_gss_oid_compose(minor_status, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH, ad_type, &req_oid); if (GSS_ERROR(major_status)) return major_status; major_status = gss_inquire_sec_context_by_oid(minor_status, context_handle, (gss_OID)&req_oid, &data_set); if (major_status != GSS_S_COMPLETE) { return major_status; } if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) { return GSS_S_FAILURE; } ad_data->length = data_set->elements[0].length; ad_data->value = data_set->elements[0].value; data_set->elements[0].length = 0; data_set->elements[0].value = NULL; data_set->count = 0; gss_release_buffer_set(minor_status, &data_set); return GSS_S_COMPLETE; }
static void zeroAndReleaseBufferSet(gss_buffer_set_t *dataSet) { OM_uint32 tmpMinor; gss_buffer_set_t set = *dataSet; size_t i; if (set == GSS_C_NO_BUFFER_SET) return; for (i = 0; i <set->count; i++) memset(set->elements[i].value, 0, set->elements[i].length); gss_release_buffer_set(&tmpMinor, dataSet); }
int _gss_spnego_require_mechlist_mic(gssspnego_ctx ctx) { gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET; int require_mic; OM_uint32 minor; require_mic = 1; /* Acceptor requested it: mandatory to honour */ if (ctx->flags.peer_require_mic) return 1; /* Protocol can't handle mechListMIC (HTTP Negotiate) */ if (ctx->flags.protocol_require_no_mic) return 0; /* * Check whether peer indicated implicit support for updated SPNEGO * (eg. in the Kerberos case by using CFX) */ if (gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id, GSS_C_PEER_HAS_UPDATED_SPNEGO, &buffer_set) == GSS_S_COMPLETE) { require_mic = 1; gss_release_buffer_set(&minor, &buffer_set); } /* * Don't require mic for NTLM because * - Windows servers to have negTokenResp.negResult set for the acceptor to send the mic. * - SnowLeopard smb server can't handle it * So if we are the initiator and using NTLM, don't send the acceptor status. */ if (ctx->flags.local && gss_oid_equal(ctx->negotiated_mech_type, GSS_NTLM_MECHANISM)) require_mic = 0; /* Safe-to-omit MIC rules follow */ if (gss_oid_equal(ctx->negotiated_mech_type, ctx->preferred_mech_type)) { ctx->flags.safe_omit = 1; } else if (gss_oid_equal(ctx->negotiated_mech_type, &_gss_spnego_krb5_mechanism_oid_desc) && gss_oid_equal(ctx->preferred_mech_type, &_gss_spnego_mskrb_mechanism_oid_desc)) { ctx->flags.safe_omit = 1; } return require_mic; }
/* Owns data on success */ static krb5_error_code kg_data_list_to_buffer_set_nocopy(krb5_data **pdata, gss_buffer_set_t *buffer_set) { gss_buffer_set_t set; OM_uint32 minor_status; unsigned int i; krb5_data *data; data = *pdata; if (data == NULL) { if (buffer_set != NULL) *buffer_set = GSS_C_NO_BUFFER_SET; return 0; } else if (buffer_set == NULL) return EINVAL; if (GSS_ERROR(gss_create_empty_buffer_set(&minor_status, &set))) { assert(minor_status != 0); return minor_status; } for (i = 0; data[i].data != NULL; i++) ; set->count = i; set->elements = calloc(i, sizeof(gss_buffer_desc)); if (set->elements == NULL) { gss_release_buffer_set(&minor_status, &set); return ENOMEM; } for (i = 0; i < set->count; i++) { set->elements[i].length = data[i].length; set->elements[i].value = data[i].data; } free(data); *pdata = NULL; *buffer_set = set; return 0; }
void enumerate_attributes(gss_name_t name, int noisy) { OM_uint32 major, minor; int is_mechname; gss_buffer_set_t attrs = GSS_C_NO_BUFFER_SET; size_t i; major = gss_inquire_name(&minor, name, &is_mechname, NULL, &attrs); check_gsserr("gss_inquire_name", major, minor); if (attrs != GSS_C_NO_BUFFER_SET) { for (i = 0; i < attrs->count; i++) dump_attribute(name, &attrs->elements[i], noisy); } (void)gss_release_buffer_set(&minor, &attrs); }
OM_uint32 GSSAPI_CALLCONV _gss_spnego_require_mechlist_mic(OM_uint32 *minor_status, gssspnego_ctx ctx, int *require_mic) { gss_buffer_set_t buffer_set = GSS_C_NO_BUFFER_SET; OM_uint32 minor; *minor_status = 0; *require_mic = 0; if (ctx == NULL) { return GSS_S_COMPLETE; } if (ctx->require_mic) { /* Acceptor requested it: mandatory to honour */ *require_mic = 1; return GSS_S_COMPLETE; } /* * Check whether peer indicated implicit support for updated SPNEGO * (eg. in the Kerberos case by using CFX) */ if (gss_inquire_sec_context_by_oid(&minor, ctx->negotiated_ctx_id, GSS_C_PEER_HAS_UPDATED_SPNEGO, &buffer_set) == GSS_S_COMPLETE) { *require_mic = 1; gss_release_buffer_set(&minor, &buffer_set); } /* Safe-to-omit MIC rules follow */ if (*require_mic) { if (gss_oid_equal(ctx->negotiated_mech_type, ctx->preferred_mech_type)) { *require_mic = 0; } else if (gss_oid_equal(ctx->negotiated_mech_type, &_gss_spnego_krb5_mechanism_oid_desc) && gss_oid_equal(ctx->preferred_mech_type, &_gss_spnego_mskrb_mechanism_oid_desc)) { *require_mic = 0; } } return GSS_S_COMPLETE; }
OM_uint32 KRB5_CALLCONV gss_krb5_get_tkt_flags(OM_uint32 *minor_status, gss_ctx_id_t context_handle, krb5_flags *ticket_flags) { static const gss_OID_desc req_oid = { GSS_KRB5_GET_TKT_FLAGS_OID_LENGTH, GSS_KRB5_GET_TKT_FLAGS_OID }; OM_uint32 major_status; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; if (ticket_flags == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; major_status = gss_inquire_sec_context_by_oid(minor_status, context_handle, (gss_OID)&req_oid, &data_set); if (major_status != GSS_S_COMPLETE) return major_status; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1 || data_set->elements[0].length != sizeof(*ticket_flags)) { *minor_status = EINVAL; return GSS_S_FAILURE; } *ticket_flags = *((krb5_flags *)data_set->elements[0].value); gss_release_buffer_set(minor_status, &data_set); *minor_status = 0; return GSS_S_COMPLETE; }
OM_uint32 KRB5_CALLCONV gsskrb5_extract_authtime_from_sec_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, krb5_timestamp *authtime) { static const gss_OID_desc req_oid = { GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID_LENGTH, GSS_KRB5_EXTRACT_AUTHTIME_FROM_SEC_CONTEXT_OID }; OM_uint32 major_status; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; if (authtime == NULL) return GSS_S_CALL_INACCESSIBLE_WRITE; major_status = gss_inquire_sec_context_by_oid(minor_status, context_handle, (gss_OID)&req_oid, &data_set); if (major_status != GSS_S_COMPLETE) return major_status; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1 || data_set->elements[0].length != sizeof(*authtime)) { *minor_status = EINVAL; return GSS_S_FAILURE; } *authtime = *((krb5_timestamp *)data_set->elements[0].value); gss_release_buffer_set(minor_status, &data_set); *minor_status = 0; return GSS_S_COMPLETE; }
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 */ }
NTSTATUS gssapi_obtain_pac_blob(TALLOC_CTX *mem_ctx, gss_ctx_id_t gssapi_context, gss_name_t gss_client_name, DATA_BLOB *pac_blob) { NTSTATUS status; OM_uint32 gss_maj, gss_min; #ifdef HAVE_GSS_GET_NAME_ATTRIBUTE /* * gss_get_name_attribute() in MIT krb5 1.10.0 can return unintialized pac_display_buffer * and later gss_release_buffer() will crash on attempting to release it. * * So always initialize the buffer descriptors. * * See following links for more details: * http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=658514 * http://krbdev.mit.edu/rt/Ticket/Display.html?user=guest&pass=guest&id=7087 */ gss_buffer_desc pac_buffer = { .value = NULL, .length = 0 }; gss_buffer_desc pac_display_buffer = { .value = NULL, .length = 0 }; gss_buffer_desc pac_name = { .value = discard_const("urn:mspac:"), .length = sizeof("urn:mspac:")-1 }; int more = -1; int authenticated = false; int complete = false; gss_maj = gss_get_name_attribute( &gss_min, gss_client_name, &pac_name, &authenticated, &complete, &pac_buffer, &pac_display_buffer, &more); if (gss_maj != 0) { gss_OID oid = discard_const(gss_mech_krb5); DBG_NOTICE("obtaining PAC via GSSAPI gss_get_name_attribute " "failed: %s\n", gssapi_error_string(mem_ctx, gss_maj, gss_min, oid)); return NT_STATUS_ACCESS_DENIED; } else if (authenticated && complete) { /* The PAC blob is returned directly */ *pac_blob = data_blob_talloc(mem_ctx, pac_buffer.value, pac_buffer.length); if (!pac_blob->data) { status = NT_STATUS_NO_MEMORY; } else { status = NT_STATUS_OK; } gss_maj = gss_release_buffer(&gss_min, &pac_buffer); gss_maj = gss_release_buffer(&gss_min, &pac_display_buffer); return status; } else { DEBUG(0, ("obtaining PAC via GSSAPI failed: authenticated: %s, complete: %s, more: %s\n", authenticated ? "true" : "false", complete ? "true" : "false", more ? "true" : "false")); return NT_STATUS_ACCESS_DENIED; } #elif defined(HAVE_GSS_INQUIRE_SEC_CONTEXT_BY_OID) gss_OID_desc pac_data_oid = { .elements = discard_const(EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID), .length = EXTRACT_PAC_AUTHZ_DATA_FROM_SEC_CONTEXT_OID_LENGTH }; gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; /* If we didn't have the routine to get a verified, validated * PAC (supplied only by MIT at the time of writing), then try * with the Heimdal OID (fetches the PAC directly and always * validates) */ gss_maj = gss_inquire_sec_context_by_oid( &gss_min, gssapi_context, &pac_data_oid, &set); /* First check for the error MIT gives for an unknown OID */ if (gss_maj == GSS_S_UNAVAILABLE) { DEBUG(1, ("unable to obtain a PAC against this GSSAPI library. " "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n")); } else if (gss_maj != 0) { DEBUG(2, ("obtaining PAC via GSSAPI gss_inqiure_sec_context_by_oid (Heimdal OID) failed: %s\n", gssapi_error_string(mem_ctx, gss_maj, gss_min, gss_mech_krb5))); } else { if (set == GSS_C_NO_BUFFER_SET) { DEBUG(0, ("gss_inquire_sec_context_by_oid returned unknown " "data in results.\n")); return NT_STATUS_INTERNAL_ERROR; } /* The PAC blob is returned directly */ *pac_blob = data_blob_talloc(mem_ctx, set->elements[0].value, set->elements[0].length); if (!pac_blob->data) { status = NT_STATUS_NO_MEMORY; } else { status = NT_STATUS_OK; } gss_maj = gss_release_buffer_set(&gss_min, &set); return status; } #else DEBUG(1, ("unable to obtain a PAC against this GSSAPI library. " "GSSAPI secured connections are available only with Heimdal or MIT Kerberos >= 1.8\n")); #endif return NT_STATUS_ACCESS_DENIED; } NTSTATUS gssapi_get_session_key(TALLOC_CTX *mem_ctx, gss_ctx_id_t gssapi_context, DATA_BLOB *session_key, uint32_t *keytype) { OM_uint32 gss_min, gss_maj; gss_buffer_set_t set = GSS_C_NO_BUFFER_SET; gss_maj = gss_inquire_sec_context_by_oid( &gss_min, gssapi_context, &gse_sesskey_inq_oid, &set); if (gss_maj) { DEBUG(0, ("gss_inquire_sec_context_by_oid failed [%s]\n", gssapi_error_string(mem_ctx, gss_maj, gss_min, discard_const_p(struct gss_OID_desc_struct, gss_mech_krb5)))); return NT_STATUS_NO_USER_SESSION_KEY; } if ((set == GSS_C_NO_BUFFER_SET) || (set->count == 0)) { #ifdef HAVE_GSSKRB5_GET_SUBKEY krb5_keyblock *subkey; gss_maj = gsskrb5_get_subkey(&gss_min, gssapi_context, &subkey); if (gss_maj != 0) { DEBUG(1, ("NO session key for this mech\n")); return NT_STATUS_NO_USER_SESSION_KEY; } if (session_key) { *session_key = data_blob_talloc(mem_ctx, KRB5_KEY_DATA(subkey), KRB5_KEY_LENGTH(subkey)); } if (keytype) { *keytype = KRB5_KEY_TYPE(subkey); } krb5_free_keyblock(NULL /* should be krb5_context */, subkey); return NT_STATUS_OK; #else DEBUG(0, ("gss_inquire_sec_context_by_oid didn't return any session key (and no alternative method available)\n")); return NT_STATUS_NO_USER_SESSION_KEY; #endif } if (session_key) { *session_key = data_blob_talloc(mem_ctx, set->elements[0].value, set->elements[0].length); } if (keytype) { int diflen, i; const uint8_t *p; *keytype = 0; if (set->count < 2) { #ifdef HAVE_GSSKRB5_GET_SUBKEY krb5_keyblock *subkey; gss_maj = gsskrb5_get_subkey(&gss_min, gssapi_context, &subkey); if (gss_maj == 0) { *keytype = KRB5_KEY_TYPE(subkey); krb5_free_keyblock(NULL /* should be krb5_context */, subkey); } #endif gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_OK; } else if (memcmp(set->elements[1].value, gse_sesskeytype_oid.elements, gse_sesskeytype_oid.length) != 0) { /* Perhaps a non-krb5 session key */ gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_OK; } p = (const uint8_t *)set->elements[1].value + gse_sesskeytype_oid.length; diflen = set->elements[1].length - gse_sesskeytype_oid.length; if (diflen <= 0) { gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_INVALID_PARAMETER; } for (i = 0; i < diflen; i++) { *keytype = (*keytype << 7) | (p[i] & 0x7f); if (i + 1 != diflen && (p[i] & 0x80) == 0) { gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_INVALID_PARAMETER; } } } gss_maj = gss_release_buffer_set(&gss_min, &set); return NT_STATUS_OK; } char *gssapi_error_string(TALLOC_CTX *mem_ctx, OM_uint32 maj_stat, OM_uint32 min_stat, const gss_OID mech) { OM_uint32 disp_min_stat, disp_maj_stat; gss_buffer_desc maj_error_message; gss_buffer_desc min_error_message; char *maj_error_string, *min_error_string; OM_uint32 msg_ctx = 0; char *ret; maj_error_message.value = NULL; min_error_message.value = NULL; maj_error_message.length = 0; min_error_message.length = 0; disp_maj_stat = gss_display_status(&disp_min_stat, maj_stat, GSS_C_GSS_CODE, mech, &msg_ctx, &maj_error_message); if (disp_maj_stat != 0) { maj_error_message.value = NULL; maj_error_message.length = 0; } disp_maj_stat = gss_display_status(&disp_min_stat, min_stat, GSS_C_MECH_CODE, mech, &msg_ctx, &min_error_message); if (disp_maj_stat != 0) { min_error_message.value = NULL; min_error_message.length = 0; } maj_error_string = talloc_strndup(mem_ctx, (char *)maj_error_message.value, maj_error_message.length); min_error_string = talloc_strndup(mem_ctx, (char *)min_error_message.value, min_error_message.length); ret = talloc_asprintf(mem_ctx, "%s: %s", maj_error_string, min_error_string); talloc_free(maj_error_string); talloc_free(min_error_string); gss_release_buffer(&disp_min_stat, &maj_error_message); gss_release_buffer(&disp_min_stat, &min_error_message); return ret; }
OM_uint32 gss_krb5_export_lucid_sec_context(OM_uint32 *minor_status, gss_ctx_id_t *context_handle, OM_uint32 version, void **rctx) { krb5_context context = NULL; krb5_error_code ret; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; OM_uint32 major_status; gss_krb5_lucid_context_v1_t *ctx = NULL; krb5_storage *sp = NULL; uint32_t num; if (context_handle == NULL || *context_handle == GSS_C_NO_CONTEXT || version != 1) { ret = EINVAL; return GSS_S_FAILURE; } major_status = gss_inquire_sec_context_by_oid (minor_status, *context_handle, GSS_KRB5_EXPORT_LUCID_CONTEXT_V1_X, &data_set); if (major_status) return major_status; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = krb5_init_context(&context); if (ret) goto out; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) { ret = ENOMEM; goto out; } sp = krb5_storage_from_mem(data_set->elements[0].value, data_set->elements[0].length); if (sp == NULL) { ret = ENOMEM; goto out; } ret = krb5_ret_uint32(sp, &num); if (ret) goto out; if (num != 1) { ret = EINVAL; goto out; } ctx->version = 1; /* initiator */ ret = krb5_ret_uint32(sp, &ctx->initiate); if (ret) goto out; /* endtime */ ret = krb5_ret_uint32(sp, &ctx->endtime); if (ret) goto out; /* send_seq */ ret = krb5_ret_uint32(sp, &num); if (ret) goto out; ctx->send_seq = ((uint64_t)num) << 32; ret = krb5_ret_uint32(sp, &num); if (ret) goto out; ctx->send_seq |= num; /* recv_seq */ ret = krb5_ret_uint32(sp, &num); if (ret) goto out; ctx->recv_seq = ((uint64_t)num) << 32; ret = krb5_ret_uint32(sp, &num); if (ret) goto out; ctx->recv_seq |= num; /* protocol */ ret = krb5_ret_uint32(sp, &ctx->protocol); if (ret) goto out; if (ctx->protocol == 0) { krb5_keyblock key; /* sign_alg */ ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.sign_alg); if (ret) goto out; /* seal_alg */ ret = krb5_ret_uint32(sp, &ctx->rfc1964_kd.seal_alg); if (ret) goto out; /* ctx_key */ ret = krb5_ret_keyblock(sp, &key); if (ret) goto out; ret = set_key(&key, &ctx->rfc1964_kd.ctx_key); krb5_free_keyblock_contents(context, &key); if (ret) goto out; } else if (ctx->protocol == 1) { krb5_keyblock key; /* acceptor_subkey */ ret = krb5_ret_uint32(sp, &ctx->cfx_kd.have_acceptor_subkey); if (ret) goto out; /* ctx_key */ ret = krb5_ret_keyblock(sp, &key); if (ret) goto out; ret = set_key(&key, &ctx->cfx_kd.ctx_key); krb5_free_keyblock_contents(context, &key); if (ret) goto out; /* acceptor_subkey */ if (ctx->cfx_kd.have_acceptor_subkey) { ret = krb5_ret_keyblock(sp, &key); if (ret) goto out; ret = set_key(&key, &ctx->cfx_kd.acceptor_subkey); krb5_free_keyblock_contents(context, &key); if (ret) goto out; } } else { ret = EINVAL; goto out; } *rctx = ctx; out: gss_release_buffer_set(minor_status, &data_set); if (sp) krb5_storage_free(sp); if (context) krb5_free_context(context); if (ret) { if (ctx) gss_krb5_free_lucid_sec_context(NULL, ctx); *minor_status = ret; return GSS_S_FAILURE; } *minor_status = 0; return GSS_S_COMPLETE; }
/** * Get the proxy group from a GSS name. * * This function will get the proxy group from a GSS name structure. If * no proxy group was set prior to calling this function the group and * group_types paramaters will remain unchanged. * * @param minor_status * The minor status returned by this function. This paramter * will be 0 upon success. * @param name * The GSS name from which the group information is extracted. * @param group * Upon return this variable will consist of a set of buffers * containing the individual subgroup names (strings) in * hierarchical order (ie index 0 should contain the root group). * @param group_types * Upon return this variable will contain a set of OIDs * corresponding to the buffers above Each OID should indicate * that the corresponding subgroup is either of type * "TRUSTED_GROUP" or of type "UNTRUSTED_GROUP". * * @return * GSS_S_COMPLETE upon success * GSS_S_BAD_NAME if the name was found to be faulty * GSS_S_FAILURE upon general failure */ OM_uint32 GSS_CALLCONV gss_get_group( OM_uint32 * minor_status, const gss_name_t name, gss_buffer_set_t * group, gss_OID_set * group_types) { OM_uint32 major_status = GSS_S_COMPLETE; OM_uint32 tmp_minor_status; int i; int num_subgroups; gss_name_desc * internal_name; char * subgroup; gss_buffer_desc buffer; static char * _function_name_ = "gss_get_group"; GLOBUS_I_GSI_GSSAPI_DEBUG_ENTER; internal_name = (gss_name_desc *) name; if(minor_status == NULL) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, major_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("NULL parameter minor_status passed to function: %s"), _function_name_)); goto exit; } *minor_status = (OM_uint32) GLOBUS_SUCCESS; if(name == GSS_C_NO_NAME) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, major_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid group name passed to function: %s"), _function_name_)); goto exit; } if(group == NULL) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, major_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid group passed to function: %s"), _function_name_)); goto exit; } if(group_types == NULL) { major_status = GSS_S_FAILURE; GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, major_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_ARGUMENT, (_GGSL("Invalid group types passed to function: %s"), _function_name_)); goto exit; } num_subgroups = sk_num(internal_name->group); if(internal_name->group == NULL || num_subgroups == 0) { goto exit; } if(internal_name->group_types == NULL) { GLOBUS_GSI_GSSAPI_ERROR_RESULT( minor_status, GLOBUS_GSI_GSSAPI_ERROR_BAD_NAME); major_status = GSS_S_BAD_NAME; goto exit; } major_status = gss_create_empty_buffer_set(local_minor_status, group); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP); goto exit; } major_status = gss_create_empty_oid_set(local_minor_status, group_types); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP); goto release_buffer; } for(++index = 0; ++index < num_subgroups; ++index) { subgroup = sk_value(internal_name->group, ++index); buffer.value = (void *) subgroup; buffer.length = strlen(subgroup) + 1; major_status = gss_add_buffer_set_member(&local_minor_status, &buffer, group); if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP); goto release_oid; } if(ASN1_BIT_STRING_get_bit(internal_name->group_types, index)) { major_status = gss_add_oid_set_member( &local_minor_status, (gss_OID) gss_untrusted_group, group_types); } else { major_status = gss_add_oid_set_member( &local_minor_status, (gss_OID) gss_trusted_group, group_types); } if(GSS_ERROR(major_status)) { GLOBUS_GSI_GSSAPI_ERROR_CHAIN_RESULT( minor_status, local_minor_status, GLOBUS_GSI_GSSAPI_ERROR_WITH_GROUP); goto release_oid; } } goto exit; release_oid: gss_release_oid_set(&local_minor_status, group_types); release_buffer: gss_release_buffer_set(&local_minor_status, group); exit: GLOBUS_I_GSI_GSSAPI_DEBUG_EXIT; return major_status; }
OM_uint32 gsskrb5_extract_authz_data_from_sec_context(OM_uint32 *minor_status, gss_ctx_id_t context_handle, int ad_type, gss_buffer_t ad_data) { gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; OM_uint32 maj_stat; gss_OID_desc oid_flat; heim_oid baseoid, oid; size_t size; if (context_handle == GSS_C_NO_CONTEXT) { *minor_status = EINVAL; return GSS_S_FAILURE; } /* All this to append an integer to an oid... */ if (der_get_oid(GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->elements, GSS_KRB5_EXTRACT_AUTHZ_DATA_FROM_SEC_CONTEXT_X->length, &baseoid, NULL) != 0) { *minor_status = EINVAL; return GSS_S_FAILURE; } oid.length = baseoid.length + 1; oid.components = calloc(oid.length, sizeof(*oid.components)); if (oid.components == NULL) { der_free_oid(&baseoid); *minor_status = ENOMEM; return GSS_S_FAILURE; } memcpy(oid.components, baseoid.components, baseoid.length * sizeof(*baseoid.components)); der_free_oid(&baseoid); oid.components[oid.length - 1] = ad_type; oid_flat.length = der_length_oid(&oid); oid_flat.elements = malloc(oid_flat.length); if (oid_flat.elements == NULL) { free(oid.components); *minor_status = ENOMEM; return GSS_S_FAILURE; } if (der_put_oid((unsigned char *)oid_flat.elements + oid_flat.length - 1, oid_flat.length, &oid, &size) != 0) { free(oid.components); free(oid_flat.elements); *minor_status = EINVAL; return GSS_S_FAILURE; } if (oid_flat.length != size) abort(); free(oid.components); /* FINALLY, we have the OID */ maj_stat = gss_inquire_sec_context_by_oid (minor_status, context_handle, &oid_flat, &data_set); free(oid_flat.elements); if (maj_stat) return maj_stat; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } ad_data->value = malloc(data_set->elements[0].length); if (ad_data->value == NULL) { gss_release_buffer_set(minor_status, &data_set); *minor_status = ENOMEM; return GSS_S_FAILURE; } ad_data->length = data_set->elements[0].length; memcpy(ad_data->value, data_set->elements[0].value, ad_data->length); gss_release_buffer_set(minor_status, &data_set); *minor_status = 0; return GSS_S_COMPLETE; }
static OM_uint32 gsskrb5_extract_key(OM_uint32 *minor_status, gss_ctx_id_t context_handle, const gss_OID oid, krb5_keyblock **keyblock) { krb5_error_code ret; gss_buffer_set_t data_set = GSS_C_NO_BUFFER_SET; OM_uint32 major_status; krb5_context context = NULL; krb5_storage *sp = NULL; if (context_handle == GSS_C_NO_CONTEXT) { ret = EINVAL; return GSS_S_FAILURE; } ret = krb5_init_context(&context); if(ret) { *minor_status = ret; return GSS_S_FAILURE; } major_status = gss_inquire_sec_context_by_oid (minor_status, context_handle, oid, &data_set); if (major_status) return major_status; if (data_set == GSS_C_NO_BUFFER_SET || data_set->count != 1) { gss_release_buffer_set(minor_status, &data_set); *minor_status = EINVAL; return GSS_S_FAILURE; } sp = krb5_storage_from_mem(data_set->elements[0].value, data_set->elements[0].length); if (sp == NULL) { ret = ENOMEM; goto out; } *keyblock = calloc(1, sizeof(**keyblock)); if (keyblock == NULL) { ret = ENOMEM; goto out; } ret = krb5_ret_keyblock(sp, *keyblock); out: gss_release_buffer_set(minor_status, &data_set); if (sp) krb5_storage_free(sp); if (ret && keyblock) { krb5_free_keyblock(context, *keyblock); *keyblock = NULL; } if (context) krb5_free_context(context); *minor_status = ret; if (ret) return GSS_S_FAILURE; return GSS_S_COMPLETE; }