http_negotiate.c (7443B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 25 #include "curl_setup.h" 26 27 #if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) 28 29 #include "urldata.h" 30 #include "cfilters.h" 31 #include "sendf.h" 32 #include "http_negotiate.h" 33 #include "vauth/vauth.h" 34 #include "vtls/vtls.h" 35 #include "curlx/strparse.h" 36 37 /* The last 3 #include files should be in this order */ 38 #include "curl_printf.h" 39 #include "curl_memory.h" 40 #include "memdebug.h" 41 42 43 static void http_auth_nego_reset(struct connectdata *conn, 44 struct negotiatedata *neg_ctx, 45 bool proxy) 46 { 47 if(proxy) 48 conn->proxy_negotiate_state = GSS_AUTHNONE; 49 else 50 conn->http_negotiate_state = GSS_AUTHNONE; 51 if(neg_ctx) 52 Curl_auth_cleanup_spnego(neg_ctx); 53 } 54 55 56 CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, 57 bool proxy, const char *header) 58 { 59 CURLcode result; 60 size_t len; 61 62 /* Point to the username, password, service and host */ 63 const char *userp; 64 const char *passwdp; 65 const char *service; 66 const char *host; 67 68 /* Point to the correct struct with this */ 69 struct negotiatedata *neg_ctx; 70 curlnegotiate state; 71 72 if(proxy) { 73 #ifndef CURL_DISABLE_PROXY 74 userp = conn->http_proxy.user; 75 passwdp = conn->http_proxy.passwd; 76 service = data->set.str[STRING_PROXY_SERVICE_NAME] ? 77 data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; 78 host = conn->http_proxy.host.name; 79 state = conn->proxy_negotiate_state; 80 #else 81 return CURLE_NOT_BUILT_IN; 82 #endif 83 } 84 else { 85 userp = conn->user; 86 passwdp = conn->passwd; 87 service = data->set.str[STRING_SERVICE_NAME] ? 88 data->set.str[STRING_SERVICE_NAME] : "HTTP"; 89 host = conn->host.name; 90 state = conn->http_negotiate_state; 91 } 92 93 neg_ctx = Curl_auth_nego_get(conn, proxy); 94 if(!neg_ctx) 95 return CURLE_OUT_OF_MEMORY; 96 97 /* Not set means empty */ 98 if(!userp) 99 userp = ""; 100 101 if(!passwdp) 102 passwdp = ""; 103 104 /* Obtain the input token, if any */ 105 header += strlen("Negotiate"); 106 curlx_str_passblanks(&header); 107 108 len = strlen(header); 109 neg_ctx->havenegdata = len != 0; 110 if(!len) { 111 if(state == GSS_AUTHSUCC) { 112 infof(data, "Negotiate auth restarted"); 113 http_auth_nego_reset(conn, neg_ctx, proxy); 114 } 115 else if(state != GSS_AUTHNONE) { 116 /* The server rejected our authentication and has not supplied any more 117 negotiation mechanisms */ 118 http_auth_nego_reset(conn, neg_ctx, proxy); 119 return CURLE_LOGIN_DENIED; 120 } 121 } 122 123 /* Supports SSL channel binding for Windows ISS extended protection */ 124 #if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) 125 neg_ctx->sslContext = conn->sslContext; 126 #endif 127 /* Check if the connection is using SSL and get the channel binding data */ 128 #ifdef HAVE_GSSAPI 129 #ifdef USE_SSL 130 curlx_dyn_init(&neg_ctx->channel_binding_data, SSL_CB_MAX_SIZE + 1); 131 if(Curl_conn_is_ssl(conn, FIRSTSOCKET)) { 132 result = Curl_ssl_get_channel_binding( 133 data, FIRSTSOCKET, &neg_ctx->channel_binding_data); 134 if(result) { 135 http_auth_nego_reset(conn, neg_ctx, proxy); 136 return result; 137 } 138 } 139 #else 140 curlx_dyn_init(&neg_ctx->channel_binding_data, 1); 141 #endif /* USE_SSL */ 142 #endif /* HAVE_GSSAPI */ 143 144 /* Initialize the security context and decode our challenge */ 145 result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, 146 host, header, neg_ctx); 147 148 #ifdef HAVE_GSSAPI 149 curlx_dyn_free(&neg_ctx->channel_binding_data); 150 #endif 151 152 if(result) 153 http_auth_nego_reset(conn, neg_ctx, proxy); 154 155 return result; 156 } 157 158 CURLcode Curl_output_negotiate(struct Curl_easy *data, 159 struct connectdata *conn, bool proxy) 160 { 161 struct negotiatedata *neg_ctx; 162 struct auth *authp; 163 curlnegotiate *state; 164 char *base64 = NULL; 165 size_t len = 0; 166 char *userp; 167 CURLcode result; 168 169 if(proxy) { 170 #ifndef CURL_DISABLE_PROXY 171 authp = &data->state.authproxy; 172 state = &conn->proxy_negotiate_state; 173 #else 174 return CURLE_NOT_BUILT_IN; 175 #endif 176 } 177 else { 178 authp = &data->state.authhost; 179 state = &conn->http_negotiate_state; 180 } 181 neg_ctx = Curl_auth_nego_get(conn, proxy); 182 if(!neg_ctx) 183 return CURLE_OUT_OF_MEMORY; 184 185 authp->done = FALSE; 186 187 if(*state == GSS_AUTHRECV) { 188 if(neg_ctx->havenegdata) { 189 neg_ctx->havemultiplerequests = TRUE; 190 } 191 } 192 else if(*state == GSS_AUTHSUCC) { 193 if(!neg_ctx->havenoauthpersist) { 194 neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests; 195 } 196 } 197 198 if(neg_ctx->noauthpersist || 199 (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) { 200 201 if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) { 202 infof(data, "Curl_output_negotiate, " 203 "no persistent authentication: cleanup existing context"); 204 http_auth_nego_reset(conn, neg_ctx, proxy); 205 } 206 if(!neg_ctx->context) { 207 result = Curl_input_negotiate(data, conn, proxy, "Negotiate"); 208 if(result == CURLE_AUTH_ERROR) { 209 /* negotiate auth failed, let's continue unauthenticated to stay 210 * compatible with the behavior before curl-7_64_0-158-g6c6035532 */ 211 authp->done = TRUE; 212 return CURLE_OK; 213 } 214 else if(result) 215 return result; 216 } 217 218 result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len); 219 if(result) 220 return result; 221 222 userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", 223 base64); 224 225 if(proxy) { 226 #ifndef CURL_DISABLE_PROXY 227 free(data->state.aptr.proxyuserpwd); 228 data->state.aptr.proxyuserpwd = userp; 229 #endif 230 } 231 else { 232 free(data->state.aptr.userpwd); 233 data->state.aptr.userpwd = userp; 234 } 235 236 free(base64); 237 238 if(!userp) { 239 return CURLE_OUT_OF_MEMORY; 240 } 241 242 *state = GSS_AUTHSENT; 243 #ifdef HAVE_GSSAPI 244 if(neg_ctx->status == GSS_S_COMPLETE || 245 neg_ctx->status == GSS_S_CONTINUE_NEEDED) { 246 *state = GSS_AUTHDONE; 247 } 248 #else 249 #ifdef USE_WINDOWS_SSPI 250 if(neg_ctx->status == SEC_E_OK || 251 neg_ctx->status == SEC_I_CONTINUE_NEEDED) { 252 *state = GSS_AUTHDONE; 253 } 254 #endif 255 #endif 256 } 257 258 if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) { 259 /* connection is already authenticated, 260 * do not send a header in future requests */ 261 authp->done = TRUE; 262 } 263 264 neg_ctx->havenegdata = FALSE; 265 266 return CURLE_OK; 267 } 268 269 #endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */