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()
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
Available variables:
- `HAVE_DES_ECB_ENCRYPT`: `DES_ecb_encrypt` present in OpenSSL (or fork).
+- `HAVE_GSS_SET_NEG_MECHS`: `gss_set_neg_mechs` present in GSS-API library.
- `HAVE_LDAP_INIT_FD`: `ldap_init_fd` present in LDAP library.
- `HAVE_LDAP_URL_PARSE`: `ldap_url_parse` present in LDAP library.
- `HAVE_MBEDTLS_DES_CRYPT_ECB`: `mbedtls_des_crypt_ecb` present in mbedTLS <4.
/* if you have the GNU gssapi libraries */
#cmakedefine HAVE_GSSGNU 1
+/* if you have gss_set_neg_mechs */
+#cmakedefine HAVE_GSS_SET_NEG_MECHS 1
+
/* MIT Kerberos version */
#cmakedefine CURL_KRB5_VERSION ${CURL_KRB5_VERSION}
/* libcurl is also passing this struct to these functions, which are not yet
* stubbed:
- * gss_inquire_context()
* gss_unwrap()
* gss_wrap()
*/
char creds[250];
};
+/* Stub credential: tracks which mechanisms are allowed */
+struct stub_gss_cred_id_t_desc {
+ int allow_krb5;
+ int allow_ntlm;
+};
+
static OM_uint32 stub_gss_init_sec_context(
OM_uint32 *min,
gss_cred_id_t initiator_cred_handle,
char *token = NULL;
const char *creds = NULL;
- (void)initiator_cred_handle;
(void)mech_type;
(void)time_req;
(void)input_chan_bindings;
if(strstr(creds, "NTLM"))
ctx->have_ntlm = 1;
+ /* If a credential restricts allowed mechs, honour it */
+ if(initiator_cred_handle != GSS_C_NO_CREDENTIAL) {
+ struct stub_gss_cred_id_t_desc *cred =
+ (struct stub_gss_cred_id_t_desc *)initiator_cred_handle;
+ if(!cred->allow_krb5)
+ ctx->have_krb5 = 0;
+ if(!cred->allow_ntlm)
+ ctx->have_ntlm = 0;
+ }
+
if(ctx->have_krb5)
ctx->sent = STUB_GSS_KRB5;
else if(ctx->have_ntlm)
return GSS_S_COMPLETE;
}
+
+/* NTLMSSP OID: 1.3.6.1.4.1.311.2.2.10 */
+static gss_OID_desc stub_ntlmssp_oid = {
+ 10, CURL_UNCONST("\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a")
+};
+
+static OM_uint32 stub_gss_inquire_context(
+ OM_uint32 *min,
+ struct stub_gss_ctx_id_t_desc *context,
+ gss_name_t *src_name,
+ gss_name_t *targ_name,
+ OM_uint32 *lifetime_rec,
+ gss_OID *mech_type,
+ OM_uint32 *ctx_flags,
+ int *locally_initiated,
+ int *open_context)
+{
+ (void)src_name;
+ (void)targ_name;
+ (void)lifetime_rec;
+ (void)ctx_flags;
+ (void)locally_initiated;
+ (void)open_context;
+
+ if(!min)
+ return GSS_S_FAILURE;
+
+ if(!context) {
+ *min = STUB_GSS_INVALID_CTX;
+ return GSS_S_FAILURE;
+ }
+
+ *min = 0;
+ if(mech_type) {
+ switch(context->sent) {
+ case STUB_GSS_NTLM1:
+ case STUB_GSS_NTLM3:
+ *mech_type = &stub_ntlmssp_oid;
+ break;
+ default:
+ *mech_type = (gss_OID)&Curl_krb5_mech_oid;
+ break;
+ }
+ }
+
+ return GSS_S_COMPLETE;
+}
+static OM_uint32 stub_gss_acquire_cred(
+ OM_uint32 *min,
+ gss_name_t desired_name,
+ OM_uint32 time_req,
+ 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)
+{
+ (void)desired_name;
+ (void)time_req;
+ (void)desired_mechs;
+ (void)cred_usage;
+ (void)actual_mechs;
+ (void)time_rec;
+
+ if(!min)
+ return GSS_S_FAILURE;
+
+ *min = 0;
+ /* Allocate a stub credential that initially allows all mechanisms */
+ if(output_cred_handle) {
+ struct stub_gss_cred_id_t_desc *cred =
+ curlx_calloc(1, sizeof(*cred));
+ if(!cred) {
+ *min = STUB_GSS_NO_MEMORY;
+ return GSS_S_FAILURE;
+ }
+ cred->allow_krb5 = 1;
+ cred->allow_ntlm = 1;
+ *output_cred_handle = (gss_cred_id_t)cred;
+ }
+ return GSS_S_COMPLETE;
+}
+
+static OM_uint32 stub_gss_indicate_mechs(
+ OM_uint32 *min,
+ gss_OID_set *mech_set)
+{
+ const char *creds;
+ OM_uint32 major;
+
+ if(!min)
+ return GSS_S_FAILURE;
+
+ *min = 0;
+ creds = getenv("CURL_STUB_GSS_CREDS");
+ if(!creds) {
+ *min = STUB_GSS_INVALID_CREDS;
+ return GSS_S_FAILURE;
+ }
+
+ major = gss_create_empty_oid_set(min, mech_set);
+ if(GSS_ERROR(major))
+ return major;
+
+ /* Always include Kerberos */
+ gss_add_oid_set_member(min, (gss_OID)&Curl_krb5_mech_oid, mech_set);
+
+ /* Include NTLM if the stub creds contain NTLM */
+ if(strstr(creds, "NTLM"))
+ gss_add_oid_set_member(min, &stub_ntlmssp_oid, mech_set);
+
+ return GSS_S_COMPLETE;
+}
+
+#ifdef HAVE_GSS_SET_NEG_MECHS
+static OM_uint32 stub_gss_set_neg_mechs(
+ OM_uint32 *min,
+ gss_cred_id_t cred_handle,
+ const gss_OID_set mech_set)
+{
+ struct stub_gss_cred_id_t_desc *cred;
+ size_t i;
+ int found_krb5 = 0;
+ int found_ntlm = 0;
+
+ if(!min)
+ return GSS_S_FAILURE;
+
+ *min = 0;
+ if(cred_handle == GSS_C_NO_CREDENTIAL)
+ return GSS_S_FAILURE;
+
+ cred = (struct stub_gss_cred_id_t_desc *)cred_handle;
+
+ /* Determine which mechs are in the allowed set */
+ if(mech_set) {
+ for(i = 0; i < mech_set->count; i++) {
+ gss_OID oid = &mech_set->elements[i];
+ if(oid->length == Curl_krb5_mech_oid.length &&
+ !memcmp(oid->elements, Curl_krb5_mech_oid.elements, oid->length))
+ found_krb5 = 1;
+ if(oid->length == stub_ntlmssp_oid.length &&
+ !memcmp(oid->elements, stub_ntlmssp_oid.elements, oid->length))
+ found_ntlm = 1;
+ }
+ }
+
+ cred->allow_krb5 = found_krb5;
+ cred->allow_ntlm = found_ntlm;
+ return GSS_S_COMPLETE;
+}
+#endif /* HAVE_GSS_SET_NEG_MECHS */
+
+static OM_uint32 stub_gss_release_cred(
+ OM_uint32 *min,
+ gss_cred_id_t *cred_handle)
+{
+ if(!min)
+ return GSS_S_FAILURE;
+
+ *min = 0;
+ if(cred_handle && *cred_handle != GSS_C_NO_CREDENTIAL) {
+ curlx_free(*cred_handle);
+ *cred_handle = GSS_C_NO_CREDENTIAL;
+ }
+ return GSS_S_COMPLETE;
+}
+
#endif /* CURL_GSS_STUB */
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;
#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,
#endif /* CURL_GSS_STUB */
return gss_init_sec_context(minor_status,
- GSS_C_NO_CREDENTIAL, /* cred_handle */
+ cred_handle,
context,
target_name,
mech_type,
return gss_delete_sec_context(min, context, output_token);
}
+OM_uint32 Curl_gss_inquire_context(OM_uint32 *minor_status,
+ gss_ctx_id_t context,
+ gss_OID *mech_type)
+{
+#ifdef CURL_GSS_STUB
+ if(getenv("CURL_STUB_GSS_CREDS"))
+ return stub_gss_inquire_context(minor_status,
+ (struct stub_gss_ctx_id_t_desc *)context,
+ NULL, NULL, NULL, mech_type,
+ NULL, NULL, NULL);
+#endif /* CURL_GSS_STUB */
+
+ return gss_inquire_context(minor_status, context,
+ NULL, NULL, NULL, mech_type,
+ NULL, NULL, NULL);
+}
+
+OM_uint32 Curl_gss_acquire_cred(OM_uint32 *minor_status,
+ gss_name_t desired_name,
+ OM_uint32 time_req,
+ 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)
+{
+#ifdef CURL_GSS_STUB
+ if(getenv("CURL_STUB_GSS_CREDS"))
+ return stub_gss_acquire_cred(minor_status, desired_name, time_req,
+ desired_mechs, cred_usage,
+ output_cred_handle, actual_mechs, time_rec);
+#endif /* CURL_GSS_STUB */
+
+ return gss_acquire_cred(minor_status, desired_name, time_req,
+ desired_mechs, cred_usage,
+ output_cred_handle, actual_mechs, time_rec);
+}
+
+OM_uint32 Curl_gss_indicate_mechs(OM_uint32 *minor_status,
+ gss_OID_set *mech_set)
+{
+#ifdef CURL_GSS_STUB
+ if(getenv("CURL_STUB_GSS_CREDS"))
+ return stub_gss_indicate_mechs(minor_status, mech_set);
+#endif /* CURL_GSS_STUB */
+
+ return gss_indicate_mechs(minor_status, mech_set);
+}
+
+#ifdef HAVE_GSS_SET_NEG_MECHS
+OM_uint32 Curl_gss_set_neg_mechs(OM_uint32 *minor_status,
+ gss_cred_id_t cred_handle,
+ const gss_OID_set mech_set)
+{
+#ifdef CURL_GSS_STUB
+ if(getenv("CURL_STUB_GSS_CREDS"))
+ return stub_gss_set_neg_mechs(minor_status, cred_handle, mech_set);
+#endif /* CURL_GSS_STUB */
+
+ return gss_set_neg_mechs(minor_status, cred_handle, mech_set);
+}
+#endif /* HAVE_GSS_SET_NEG_MECHS */
+
+OM_uint32 Curl_gss_release_cred(OM_uint32 *minor_status,
+ gss_cred_id_t *cred_handle)
+{
+#ifdef CURL_GSS_STUB
+ if(getenv("CURL_STUB_GSS_CREDS"))
+ return stub_gss_release_cred(minor_status, cred_handle);
+#endif /* CURL_GSS_STUB */
+
+ return gss_release_cred(minor_status, cred_handle);
+}
+
#ifdef CURLVERBOSE
#define GSS_LOG_BUFFER_LEN 1024
static size_t display_gss_error(OM_uint32 status, int type,
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,
gss_buffer_t output_token);
+OM_uint32 Curl_gss_inquire_context(OM_uint32 *minor_status,
+ gss_ctx_id_t context,
+ gss_OID *mech_type);
+
+OM_uint32 Curl_gss_acquire_cred(OM_uint32 *minor_status,
+ gss_name_t desired_name,
+ OM_uint32 time_req,
+ 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 Curl_gss_indicate_mechs(OM_uint32 *minor_status,
+ gss_OID_set *mech_set);
+
+#ifdef HAVE_GSS_SET_NEG_MECHS
+OM_uint32 Curl_gss_set_neg_mechs(OM_uint32 *minor_status,
+ gss_cred_id_t cred_handle,
+ const gss_OID_set mech_set);
+#endif
+
+OM_uint32 Curl_gss_release_cred(OM_uint32 *minor_status,
+ gss_cred_id_t *cred_handle);
+
#ifdef CURLVERBOSE
/* Helper to log a GSS-API error status */
void Curl_gss_log_error(struct Curl_easy *data, const char *prefix,
* Returns CURLE_OK on success.
*/
CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
- SEC_WINNT_AUTH_IDENTITY *identity)
+ SEC_WINNT_AUTH_IDENTITY_EX *identity)
{
xcharp_u useranddomain;
xcharp_u user, dup_user;
/* Initialize the identity */
memset(identity, 0, sizeof(*identity));
+ identity->Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
+ identity->Length = sizeof(*identity);
useranddomain.tchar_ptr = curlx_convert_UTF8_to_tchar(userp);
if(!useranddomain.tchar_ptr)
*
* identity [in/out] - The identity structure.
*/
-void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity)
+void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY_EX *identity)
{
if(identity) {
curlx_safefree(identity->User);
/* This is used to populate the domain in an SSPI identity structure */
CURLcode Curl_override_sspi_http_realm(const char *chlg,
- SEC_WINNT_AUTH_IDENTITY *identity);
+ SEC_WINNT_AUTH_IDENTITY_EX *identity);
/* This is used to generate an SSPI identity structure */
CURLcode Curl_create_sspi_identity(const char *userp, const char *passwdp,
- SEC_WINNT_AUTH_IDENTITY *identity);
+ SEC_WINNT_AUTH_IDENTITY_EX *identity);
/* This is used to free an SSPI identity structure */
-void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity);
+void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY_EX *identity);
/* Forward-declaration of global variables defined in curl_sspi.c */
extern PSecurityFunctionTable Curl_pSecFn;
const char *passwd, unsigned long authflags)
{
ULONG method = 0;
- SEC_WINNT_AUTH_IDENTITY cred;
+ SEC_WINNT_AUTH_IDENTITY_EX cred;
ULONG rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
memset(&cred, 0, sizeof(cred));
gss_token,
&gss_send_token,
TRUE,
- gss_ret_flags);
+ gss_ret_flags,
+ GSS_C_NO_CREDENTIAL);
if(gss_token != GSS_C_NO_BUFFER) {
curlx_safefree(gss_recv_token.value);
CredHandle credentials;
CtxtHandle context;
PSecPkgInfo SecurityPackage;
- SEC_WINNT_AUTH_IDENTITY identity;
- SEC_WINNT_AUTH_IDENTITY *p_identity;
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
SecBuffer chlg_buf;
SecBuffer resp_buf;
SecBufferDesc chlg_desc;
* Returns CURLE_OK on success.
*/
CURLcode Curl_override_sspi_http_realm(const char *chlg,
- SEC_WINNT_AUTH_IDENTITY *identity)
+ SEC_WINNT_AUTH_IDENTITY_EX *identity)
{
xcharp_u domain, dup_domain;
if(!digest->http_context) {
CredHandle credentials;
- SEC_WINNT_AUTH_IDENTITY identity;
- SEC_WINNT_AUTH_IDENTITY *p_identity;
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
SecBuffer resp_buf;
SecBufferDesc resp_desc;
unsigned long attrs;
&input_token,
&output_token,
mutual_auth,
- NULL);
+ NULL,
+ GSS_C_NO_CREDENTIAL);
if(GSS_ERROR(major_status)) {
if(output_token.value)
}
#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 = Curl_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);
+ curlx_safefree(input_token.value);
+ return CURLE_AUTH_ERROR;
+ }
+
+ /* Get all available mechanisms */
+ major_status = Curl_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 */
+ major_status = Curl_gss_set_neg_mechs(&minor_status, nego->cred,
+ filtered_mechs);
+ if(GSS_ERROR(major_status)) {
+ Curl_gss_log_error(data, "gss_set_neg_mechs() failed: ",
+ major_status, minor_status);
+ }
+ 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,
&input_token,
&output_token,
TRUE,
- NULL);
+ NULL,
+ nego->cred);
/* Free the decoded challenge as it is not required anymore */
curlx_safefree(input_token.value);
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 = Curl_gss_inquire_context(&inquire_minor,
+ nego->context,
+ &mech_type);
+ 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);
nego->spn = GSS_C_NO_NAME;
}
+ /* Free our credentials */
+ if(nego->cred != GSS_C_NO_CREDENTIAL) {
+ Curl_gss_release_cred(&minor_status, &nego->cred);
+ nego->cred = GSS_C_NO_CREDENTIAL;
+ }
+
/* Reset any variables */
nego->status = 0;
nego->noauthpersist = FALSE;
/* Use the current Windows user */
nego->p_identity = NULL;
+ /* Exclude NTLM from SPNEGO negotiation via the PackageList field */
+ if(!nego->p_identity) {
+ memset(&nego->identity, 0, sizeof(nego->identity));
+ nego->identity.Version = SEC_WINNT_AUTH_IDENTITY_VERSION;
+ nego->identity.Length = sizeof(nego->identity);
+ nego->identity.Flags =
+#ifdef UNICODE
+ SEC_WINNT_AUTH_IDENTITY_UNICODE;
+#else
+ SEC_WINNT_AUTH_IDENTITY_ANSI;
+#endif
+ nego->p_identity = &nego->identity;
+ }
+
+ /* Use the special name "!ntlm" to prevent NTLM from being used:
+ * https://learn.microsoft.com/en-us/windows/win32/api/sspi/ns-sspi-sec_winnt_auth_identity_exa
+ */
+#ifdef UNICODE
+ nego->identity.PackageList =
+ (unsigned short *)CURL_UNCONST(TEXT("!ntlm"));
+#else
+ nego->identity.PackageList =
+ (unsigned char *)CURL_UNCONST(TEXT("!ntlm"));
+#endif
+ nego->identity.PackageListLength = 5;
+
/* Allocate our credentials handle */
nego->credentials = curlx_calloc(1, sizeof(CredHandle));
if(!nego->credentials)
#endif
CredHandle *credentials;
CtxtHandle *context;
- SEC_WINNT_AUTH_IDENTITY identity;
- SEC_WINNT_AUTH_IDENTITY *p_identity;
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
size_t token_max;
BYTE *output_token;
BYTE *input_token;
CredHandle *credentials;
CtxtHandle *context;
TCHAR *spn;
- SEC_WINNT_AUTH_IDENTITY identity;
- SEC_WINNT_AUTH_IDENTITY *p_identity;
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
size_t token_max;
BYTE *output_token;
#else
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;
SECURITY_STATUS status;
CredHandle *credentials;
CtxtHandle *context;
- SEC_WINNT_AUTH_IDENTITY identity;
- SEC_WINNT_AUTH_IDENTITY *p_identity;
+ SEC_WINNT_AUTH_IDENTITY_EX identity;
+ SEC_WINNT_AUTH_IDENTITY_EX *p_identity;
TCHAR *spn;
size_t token_max;
BYTE *output_token;
test2064 test2065 test2066 test2067 test2068 test2069 test2070 test2071 \
test2072 test2073 test2074 test2075 test2076 test2077 test2078 test2079 \
test2080 test2081 test2082 test2083 test2084 test2085 test2086 test2087 \
-test2088 test2089 test2090 test2091 test2092 test2093 \
+test2088 test2089 test2090 test2091 test2092 test2093 test2094 \
test2100 test2101 test2102 test2103 test2104 test2105 test2106 test2107 \
test2108 test2109 test2110 test2113 test2114 test2115 test2116 test2117 \
\
HTTP
HTTP GET
HTTP Negotiate auth (stub ntlm)
+SPNEGO NTLM disallowed
</keywords>
</info>
# Server-side
<reply>
-<!-- First request, expect 401 (ntlm challenge) -->
-<data1>
-HTTP/1.1 401 Authorization Required
-Server: Microsoft-IIS/7.0
-Content-Type: text/html; charset=iso-8859-1
-WWW-Authenticate: Negotiate Qw==
-Content-Length: 19
+<data nocheck="yes" crlf="headers">
+HTTP/1.1 200 OK swsclose
+Content-Length: 23
-Still not yet sir!
-</data1>
-<!-- Second request, expect success -->
-<data2>
-HTTP/1.1 200 Things are fine in server land
-Server: Microsoft-IIS/7.0
-Content-Type: text/html; charset=iso-8859-1
-WWW-Authenticate: Negotiate RA==
-Content-Length: 15
-
-Nice auth sir!
-</data2>
-<datacheck>
-HTTP/1.1 401 Authorization Required
-Server: Microsoft-IIS/7.0
-Content-Type: text/html; charset=iso-8859-1
-WWW-Authenticate: Negotiate Qw==
-Content-Length: 19
-
-HTTP/1.1 200 Things are fine in server land
-Server: Microsoft-IIS/7.0
-Content-Type: text/html; charset=iso-8859-1
-WWW-Authenticate: Negotiate RA==
-Content-Length: 15
-
-Nice auth sir!
-</datacheck>
+This IS the real page!
+</data>
</reply>
# Client-side
http
</server>
<name>
-HTTP Negotiate authentication (stub NTLM)
+HTTP Negotiate authentication blocked for stub NTLM credentials
</name>
<features>
GSS-API
# Verify data after the test has been "shot"
<verify>
+<errorcode>
+0
+</errorcode>
+# NTLM is blocked within SPNEGO, so when only NTLM credentials are
+# available, negotiate auth silently fails and the request is sent
+# without any Authorization header.
<protocol crlf="headers">
GET /%TESTNUMBER HTTP/1.1
Host: %HOSTIP:%HTTPPORT
-Authorization: Negotiate %b64["NTLM_Alice":HTTP@127.0.0.1:2:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]b64%
-User-Agent: curl/%VERSION
-Accept: */*
-
-GET /%TESTNUMBER HTTP/1.1
-Host: %HOSTIP:%HTTPPORT
-Authorization: Negotiate %b64["NTLM_Alice":HTTP@127.0.0.1:3:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]b64%
User-Agent: curl/%VERSION
Accept: */*
--- /dev/null
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+HTTP Negotiate auth (stub krb5)
+SPNEGO NTLM disallowed
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data1>
+HTTP/1.1 200 Things are fine in server land
+Server: Microsoft-IIS/7.0
+Content-Type: text/html; charset=iso-8859-1
+WWW-Authenticate: Negotiate RA==
+Content-Length: 15
+
+Nice auth sir!
+</data1>
+<datacheck>
+HTTP/1.1 200 Things are fine in server land
+Server: Microsoft-IIS/7.0
+Content-Type: text/html; charset=iso-8859-1
+WWW-Authenticate: Negotiate RA==
+Content-Length: 15
+
+Nice auth sir!
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<name>
+SPNEGO with Kerberos still works when NTLM is blocked
+</name>
+<features>
+GSS-API
+Debug
+</features>
+<setenv>
+CURL_STUB_GSS_CREDS="KRB5_Alice"
+</setenv>
+<command>
+--negotiate http://%HOSTIP:%HTTPPORT/%TESTNUMBER
+</command>
+</client>
+
+# Verify data after the test has been "shot"
+<verify>
+<errorcode>
+0
+</errorcode>
+<protocol crlf="headers">
+GET /%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+Authorization: Negotiate %b64["KRB5_Alice":HTTP@127.0.0.1:1:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA]b64%
+User-Agent: curl/%VERSION
+Accept: */*
+
+</protocol>
+</verify>
+</testcase>
fun:inet_pton6
fun:ipv6_parse
}
+
+{
+ mit-krb5-gss_display_status-internal-leak
+ Memcheck:Leak
+ match-leak-kinds: definite
+ fun:realloc
+ ...
+ fun:gss_display_status
+ fun:display_gss_error
+ fun:Curl_gss_log_error
+}