From 6c6035532383e300c712e4c1cd9fdd749ed5cf59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dominik=20H=C3=B6lzl?= Date: Mon, 10 Sep 2018 09:18:01 +0200 Subject: Negotiate: fix for HTTP POST with Negotiate * Adjusted unit tests 2056, 2057 * do not generally close connections with CURLAUTH_NEGOTIATE after every request * moved negotiatedata from UrlState to connectdata * Added stream rewind logic for CURLAUTH_NEGOTIATE * introduced negotiatedata::GSS_AUTHDONE and negotiatedata::GSS_AUTHSUCC * Consider authproblem state for CURLAUTH_NEGOTIATE * Consider reuse_forbid for CURLAUTH_NEGOTIATE * moved and adjusted negotiate authentication state handling from output_auth_headers into Curl_output_negotiate * Curl_output_negotiate: ensure auth done is always set * Curl_output_negotiate: Set auth done also if result code is GSS_S_CONTINUE_NEEDED/SEC_I_CONTINUE_NEEDED as this result code may also indicate the last challenge request (only works with disabled Expect: 100-continue and CURLOPT_KEEP_SENDING_ON_ERROR -> 1) * Consider "Persistent-Auth" header, detect if not present; Reset/Cleanup negotiate after authentication if no persistent authentication * apply changes introduced with #2546 for negotiate rewind logic Fixes #1261 Closes #1975 --- lib/http_negotiate.c | 109 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 86 insertions(+), 23 deletions(-) (limited to 'lib/http_negotiate.c') diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 2a97707eb..9415236fb 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -56,7 +56,7 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, service = data->set.str[STRING_PROXY_SERVICE_NAME] ? data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; host = conn->http_proxy.host.name; - neg_ctx = &data->state.proxyneg; + neg_ctx = &conn->proxyneg; } else { userp = conn->user; @@ -64,7 +64,7 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, service = data->set.str[STRING_SERVICE_NAME] ? data->set.str[STRING_SERVICE_NAME] : "HTTP"; host = conn->host.name; - neg_ctx = &data->state.negotiate; + neg_ctx = &conn->negotiate; } /* Not set means empty */ @@ -80,11 +80,16 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, header++; len = strlen(header); + neg_ctx->havenegdata = len != 0; if(!len) { - /* Is this the first call in a new negotiation? */ - if(neg_ctx->context) { - /* The server rejected our authentication and hasn't suppled any more + if(neg_ctx->state == GSS_AUTHSUCC) { + infof(conn->data, "Negotiate auth restarted\n"); + Curl_cleanup_negotiate(conn); + } + else if(neg_ctx->state != GSS_AUTHNONE) { + /* The server rejected our authentication and hasn't supplied any more negotiation mechanisms */ + Curl_cleanup_negotiate(conn); return CURLE_LOGIN_DENIED; } } @@ -106,38 +111,96 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) { - struct negotiatedata *neg_ctx = proxy ? &conn->data->state.proxyneg : - &conn->data->state.negotiate; + struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg : + &conn->negotiate; + struct auth *authp = proxy ? &conn->data->state.authproxy : + &conn->data->state.authhost; char *base64 = NULL; size_t len = 0; char *userp; CURLcode result; - result = Curl_auth_create_spnego_message(conn->data, neg_ctx, &base64, &len); - if(result) - return result; + authp->done = FALSE; + + if(neg_ctx->state == GSS_AUTHRECV) { + if(neg_ctx->havenegdata) { + neg_ctx->havemultiplerequests = TRUE; + } + } + else if(neg_ctx->state == GSS_AUTHSUCC) { + if(!neg_ctx->havenoauthpersist) { + neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests; + } + } - userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", - base64); + if(neg_ctx->noauthpersist || + (neg_ctx->state != GSS_AUTHDONE && neg_ctx->state != GSS_AUTHSUCC)) { - if(proxy) { - Curl_safefree(conn->allocptr.proxyuserpwd); - conn->allocptr.proxyuserpwd = userp; + if(neg_ctx->noauthpersist && neg_ctx->state == GSS_AUTHSUCC) { + infof(conn->data, "Curl_output_negotiate, " + "no persistent authentication: cleanup existing context"); + Curl_auth_spnego_cleanup(neg_ctx); + } + if(!neg_ctx->context) { + result = Curl_input_negotiate(conn, proxy, "Negotiate"); + if(result) + return result; + } + + result = Curl_auth_create_spnego_message(conn->data, + neg_ctx, &base64, &len); + if(result) + return result; + + userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", + base64); + + if(proxy) { + Curl_safefree(conn->allocptr.proxyuserpwd); + conn->allocptr.proxyuserpwd = userp; + } + else { + Curl_safefree(conn->allocptr.userpwd); + conn->allocptr.userpwd = userp; + } + + free(base64); + + if(userp == NULL) { + return CURLE_OUT_OF_MEMORY; + } + + neg_ctx->state = GSS_AUTHSENT; + #ifdef HAVE_GSSAPI + if(neg_ctx->status == GSS_S_COMPLETE || + neg_ctx->status == GSS_S_CONTINUE_NEEDED) { + neg_ctx->state = GSS_AUTHDONE; + } + #else + #ifdef USE_WINDOWS_SSPI + if(neg_ctx->status == SEC_E_OK || + neg_ctx->status == SEC_I_CONTINUE_NEEDED) { + neg_ctx->state = GSS_AUTHDONE; + } + #endif + #endif } - else { - Curl_safefree(conn->allocptr.userpwd); - conn->allocptr.userpwd = userp; + + if(neg_ctx->state == GSS_AUTHDONE || neg_ctx->state == GSS_AUTHSUCC) { + /* connection is already authenticated, + * don't send a header in future requests */ + authp->done = TRUE; } - free(base64); + neg_ctx->havenegdata = FALSE; - return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; + return CURLE_OK; } -void Curl_cleanup_negotiate(struct Curl_easy *data) +void Curl_cleanup_negotiate(struct connectdata *conn) { - Curl_auth_spnego_cleanup(&data->state.negotiate); - Curl_auth_spnego_cleanup(&data->state.proxyneg); + Curl_auth_spnego_cleanup(&conn->negotiate); + Curl_auth_spnego_cleanup(&conn->proxyneg); } #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ -- cgit v1.2.1