mirror of
https://github.com/curl/curl.git
synced 2026-06-16 19:05:39 +03:00
spnego/gss-api: block NTLM via gss_set_neg_mechs
Add credential-based NTLM filtering for GSS-API SPNEGO. Acquire explicit credentials, enumerate available mechanisms, filter out the NTLMSSP OID, and apply via gss_set_neg_mechs(). Also verify the negotiated mechanism after context establishment and reject NTLM if disallowed. Pass a cred_handle through Curl_gss_init_sec_context so SPNEGO can use the restricted credentials. Probe for gss_set_neg_mechs() availability (HAVE_GSS_SET_NEG_MECHS) in configure and CMake. Signed-off-by: Matthew John Cheetham <mjcheetham@outlook.com>
This commit is contained in:
parent
25a742e6e4
commit
e16ac344de
8 changed files with 95 additions and 7 deletions
|
|
@ -1308,6 +1308,11 @@ if(CURL_USE_GSSAPI)
|
|||
elseif(GSS_VERSION) # MIT
|
||||
set(CURL_KRB5_VERSION "\"${GSS_VERSION}\"")
|
||||
endif()
|
||||
|
||||
cmake_push_check_state()
|
||||
list(APPEND CMAKE_REQUIRED_LIBRARIES CURL::gss)
|
||||
check_function_exists("gss_set_neg_mechs" HAVE_GSS_SET_NEG_MECHS)
|
||||
cmake_pop_check_state()
|
||||
else()
|
||||
message(WARNING "GSSAPI has been requested, but no supporting libraries found. Skipping.")
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -1997,6 +1997,7 @@ if test "$want_gss" = "yes"; then
|
|||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR([--with-gssapi was specified, but a GSS-API library was not found.])
|
||||
])
|
||||
AC_CHECK_FUNCS([gss_set_neg_mechs])
|
||||
fi
|
||||
|
||||
build_libstubgss=no
|
||||
|
|
|
|||
|
|
@ -319,7 +319,8 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
|
|||
gss_buffer_t input_token,
|
||||
gss_buffer_t output_token,
|
||||
const bool mutual_auth,
|
||||
OM_uint32 *ret_flags)
|
||||
OM_uint32 *ret_flags,
|
||||
gss_cred_id_t cred_handle)
|
||||
{
|
||||
OM_uint32 req_flags = GSS_C_REPLAY_FLAG;
|
||||
|
||||
|
|
@ -341,7 +342,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
|
|||
#ifdef CURL_GSS_STUB
|
||||
if(getenv("CURL_STUB_GSS_CREDS"))
|
||||
return stub_gss_init_sec_context(minor_status,
|
||||
GSS_C_NO_CREDENTIAL, /* cred_handle */
|
||||
cred_handle,
|
||||
(struct stub_gss_ctx_id_t_desc **)context,
|
||||
target_name,
|
||||
mech_type,
|
||||
|
|
@ -356,7 +357,7 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
|
|||
#endif /* CURL_GSS_STUB */
|
||||
|
||||
return gss_init_sec_context(minor_status,
|
||||
GSS_C_NO_CREDENTIAL, /* cred_handle */
|
||||
cred_handle,
|
||||
context,
|
||||
target_name,
|
||||
mech_type,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,8 @@ OM_uint32 Curl_gss_init_sec_context(struct Curl_easy *data,
|
|||
gss_buffer_t input_token,
|
||||
gss_buffer_t output_token,
|
||||
const bool mutual_auth,
|
||||
OM_uint32 *ret_flags);
|
||||
OM_uint32 *ret_flags,
|
||||
gss_cred_id_t cred_handle);
|
||||
|
||||
OM_uint32 Curl_gss_delete_sec_context(OM_uint32 *min,
|
||||
gss_ctx_id_t *context_handle,
|
||||
|
|
|
|||
|
|
@ -180,7 +180,8 @@ CURLcode Curl_SOCKS5_gssapi_negotiate(struct Curl_cfilter *cf,
|
|||
gss_token,
|
||||
&gss_send_token,
|
||||
TRUE,
|
||||
&gss_ret_flags);
|
||||
&gss_ret_flags,
|
||||
GSS_C_NO_CREDENTIAL);
|
||||
|
||||
if(gss_token != GSS_C_NO_BUFFER) {
|
||||
Curl_safefree(gss_recv_token.value);
|
||||
|
|
|
|||
|
|
@ -138,7 +138,8 @@ CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
|
|||
&input_token,
|
||||
&output_token,
|
||||
mutual_auth,
|
||||
NULL);
|
||||
NULL,
|
||||
GSS_C_NO_CREDENTIAL);
|
||||
|
||||
if(GSS_ERROR(major_status)) {
|
||||
if(output_token.value)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@
|
|||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* Curl_auth_is_spnego_supported()
|
||||
*
|
||||
|
|
@ -158,6 +159,52 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GSS_SET_NEG_MECHS
|
||||
/* Acquire explicit credentials and restrict SPNEGO sub-mechanisms to
|
||||
* exclude NTLM. We enumerate all available mechanisms and filter out
|
||||
* the NTLMSSP OID, matching SSPI's "!ntlm". */
|
||||
if(nego->cred == GSS_C_NO_CREDENTIAL) {
|
||||
/* OID 1.3.6.1.4.1.311.2.2.10 (NTLMSSP) */
|
||||
static const gss_OID_desc ntlmssp_oid = {
|
||||
10, CURL_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")
|
||||
};
|
||||
gss_OID_set available_mechs = GSS_C_NO_OID_SET;
|
||||
gss_OID_set filtered_mechs = GSS_C_NO_OID_SET;
|
||||
|
||||
/* Acquire default credentials for SPNEGO */
|
||||
major_status = gss_acquire_cred(&minor_status, GSS_C_NO_NAME,
|
||||
GSS_C_INDEFINITE, GSS_C_NO_OID_SET,
|
||||
GSS_C_INITIATE, &nego->cred, NULL, NULL);
|
||||
if(GSS_ERROR(major_status)) {
|
||||
Curl_gss_log_error(data, "gss_acquire_cred() failed: ",
|
||||
major_status, minor_status);
|
||||
Curl_safefree(input_token.value);
|
||||
return CURLE_AUTH_ERROR;
|
||||
}
|
||||
|
||||
/* Get all available mechanisms */
|
||||
major_status = gss_indicate_mechs(&minor_status, &available_mechs);
|
||||
if(!GSS_ERROR(major_status)) {
|
||||
/* Build a set excluding NTLMSSP */
|
||||
major_status = gss_create_empty_oid_set(&minor_status, &filtered_mechs);
|
||||
if(!GSS_ERROR(major_status)) {
|
||||
size_t i;
|
||||
for(i = 0; i < available_mechs->count; i++) {
|
||||
gss_OID oid = &available_mechs->elements[i];
|
||||
if(oid->length != ntlmssp_oid.length ||
|
||||
memcmp(oid->elements, ntlmssp_oid.elements, oid->length)) {
|
||||
gss_add_oid_set_member(&minor_status, oid, &filtered_mechs);
|
||||
}
|
||||
}
|
||||
/* Restrict SPNEGO to only use non-NTLM mechanisms */
|
||||
gss_set_neg_mechs(&minor_status, nego->cred, filtered_mechs);
|
||||
gss_release_oid_set(&minor_status, &filtered_mechs);
|
||||
}
|
||||
gss_release_oid_set(&minor_status, &available_mechs);
|
||||
}
|
||||
}
|
||||
#endif /* HAVE_GSS_SET_NEG_MECHS */
|
||||
|
||||
/* Generate our challenge-response message */
|
||||
major_status = Curl_gss_init_sec_context(data,
|
||||
&minor_status,
|
||||
|
|
@ -168,7 +215,8 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
|
|||
&input_token,
|
||||
&output_token,
|
||||
TRUE,
|
||||
NULL);
|
||||
NULL,
|
||||
nego->cred);
|
||||
|
||||
/* Free the decoded challenge as it is not required anymore */
|
||||
Curl_safefree(input_token.value);
|
||||
|
|
@ -191,6 +239,29 @@ CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
|
|||
return CURLE_AUTH_ERROR;
|
||||
}
|
||||
|
||||
/* Check if NTLM was selected and is disallowed */
|
||||
if(nego->context != GSS_C_NO_CONTEXT) {
|
||||
/* OID 1.3.6.1.4.1.311.2.2.10 (NTLMSSP) */
|
||||
static const gss_OID_desc ntlmssp_oid = {
|
||||
10, CURL_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")
|
||||
};
|
||||
OM_uint32 inquire_major, inquire_minor;
|
||||
gss_OID mech_type = GSS_C_NO_OID;
|
||||
|
||||
inquire_major = gss_inquire_context(&inquire_minor, nego->context,
|
||||
NULL, NULL, NULL, &mech_type,
|
||||
NULL, NULL, NULL);
|
||||
if(!GSS_ERROR(inquire_major) && mech_type &&
|
||||
mech_type->length == ntlmssp_oid.length &&
|
||||
!memcmp(mech_type->elements, ntlmssp_oid.elements,
|
||||
ntlmssp_oid.length)) {
|
||||
infof(data, "SPNEGO chose NTLM, but NTLM is not allowed");
|
||||
gss_release_buffer(&unused_status, &output_token);
|
||||
Curl_auth_cleanup_spnego(nego);
|
||||
return CURLE_AUTH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free previous token */
|
||||
if(nego->output_token.length && nego->output_token.value)
|
||||
gss_release_buffer(&unused_status, &nego->output_token);
|
||||
|
|
@ -280,6 +351,12 @@ void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
|
|||
nego->spn = GSS_C_NO_NAME;
|
||||
}
|
||||
|
||||
/* Free our credentials */
|
||||
if(nego->cred != GSS_C_NO_CREDENTIAL) {
|
||||
gss_release_cred(&minor_status, &nego->cred);
|
||||
nego->cred = GSS_C_NO_CREDENTIAL;
|
||||
}
|
||||
|
||||
/* Reset any variables */
|
||||
nego->status = 0;
|
||||
nego->noauthpersist = FALSE;
|
||||
|
|
|
|||
|
|
@ -297,6 +297,7 @@ struct negotiatedata {
|
|||
OM_uint32 status;
|
||||
gss_ctx_id_t context;
|
||||
gss_name_t spn;
|
||||
gss_cred_id_t cred;
|
||||
gss_buffer_desc output_token;
|
||||
#ifdef GSS_C_CHANNEL_BOUND_FLAG
|
||||
struct dynbuf channel_binding_data;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue