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 "sendf.h" 31#include "http_negotiate.h" 32#include "vauth/vauth.h" 33 34/* The last 3 #include files should be in this order */ 35#include "curl_printf.h" 36#include "curl_memory.h" 37#include "memdebug.h" 38 39CURLcode Curl_input_negotiate(struct Curl_easy *data, struct connectdata *conn, 40 bool proxy, const char *header) 41{ 42 CURLcode result; 43 size_t len; 44 45 /* Point to the username, password, service and host */ 46 const char *userp; 47 const char *passwdp; 48 const char *service; 49 const char *host; 50 51 /* Point to the correct struct with this */ 52 struct negotiatedata *neg_ctx; 53 curlnegotiate state; 54 55 if(proxy) { 56#ifndef CURL_DISABLE_PROXY 57 userp = conn->http_proxy.user; 58 passwdp = conn->http_proxy.passwd; 59 service = data->set.str[STRING_PROXY_SERVICE_NAME] ? 60 data->set.str[STRING_PROXY_SERVICE_NAME] : "HTTP"; 61 host = conn->http_proxy.host.name; 62 neg_ctx = &conn->proxyneg; 63 state = conn->proxy_negotiate_state; 64#else 65 return CURLE_NOT_BUILT_IN; 66#endif 67 } 68 else { 69 userp = conn->user; 70 passwdp = conn->passwd; 71 service = data->set.str[STRING_SERVICE_NAME] ? 72 data->set.str[STRING_SERVICE_NAME] : "HTTP"; 73 host = conn->host.name; 74 neg_ctx = &conn->negotiate; 75 state = conn->http_negotiate_state; 76 } 77 78 /* Not set means empty */ 79 if(!userp) 80 userp = ""; 81 82 if(!passwdp) 83 passwdp = ""; 84 85 /* Obtain the input token, if any */ 86 header += strlen("Negotiate"); 87 while(*header && ISBLANK(*header)) 88 header++; 89 90 len = strlen(header); 91 neg_ctx->havenegdata = len != 0; 92 if(!len) { 93 if(state == GSS_AUTHSUCC) { 94 infof(data, "Negotiate auth restarted"); 95 Curl_http_auth_cleanup_negotiate(conn); 96 } 97 else if(state != GSS_AUTHNONE) { 98 /* The server rejected our authentication and hasn't supplied any more 99 negotiation mechanisms */ 100 Curl_http_auth_cleanup_negotiate(conn); 101 return CURLE_LOGIN_DENIED; 102 } 103 } 104 105 /* Supports SSL channel binding for Windows ISS extended protection */ 106#if defined(USE_WINDOWS_SSPI) && defined(SECPKG_ATTR_ENDPOINT_BINDINGS) 107 neg_ctx->sslContext = conn->sslContext; 108#endif 109 110 /* Initialize the security context and decode our challenge */ 111 result = Curl_auth_decode_spnego_message(data, userp, passwdp, service, 112 host, header, neg_ctx); 113 114 if(result) 115 Curl_http_auth_cleanup_negotiate(conn); 116 117 return result; 118} 119 120CURLcode Curl_output_negotiate(struct Curl_easy *data, 121 struct connectdata *conn, bool proxy) 122{ 123 struct negotiatedata *neg_ctx = proxy ? &conn->proxyneg : 124 &conn->negotiate; 125 struct auth *authp = proxy ? &data->state.authproxy : &data->state.authhost; 126 curlnegotiate *state = proxy ? &conn->proxy_negotiate_state : 127 &conn->http_negotiate_state; 128 char *base64 = NULL; 129 size_t len = 0; 130 char *userp; 131 CURLcode result; 132 133 authp->done = FALSE; 134 135 if(*state == GSS_AUTHRECV) { 136 if(neg_ctx->havenegdata) { 137 neg_ctx->havemultiplerequests = TRUE; 138 } 139 } 140 else if(*state == GSS_AUTHSUCC) { 141 if(!neg_ctx->havenoauthpersist) { 142 neg_ctx->noauthpersist = !neg_ctx->havemultiplerequests; 143 } 144 } 145 146 if(neg_ctx->noauthpersist || 147 (*state != GSS_AUTHDONE && *state != GSS_AUTHSUCC)) { 148 149 if(neg_ctx->noauthpersist && *state == GSS_AUTHSUCC) { 150 infof(data, "Curl_output_negotiate, " 151 "no persistent authentication: cleanup existing context"); 152 Curl_http_auth_cleanup_negotiate(conn); 153 } 154 if(!neg_ctx->context) { 155 result = Curl_input_negotiate(data, conn, proxy, "Negotiate"); 156 if(result == CURLE_AUTH_ERROR) { 157 /* negotiate auth failed, let's continue unauthenticated to stay 158 * compatible with the behavior before curl-7_64_0-158-g6c6035532 */ 159 authp->done = TRUE; 160 return CURLE_OK; 161 } 162 else if(result) 163 return result; 164 } 165 166 result = Curl_auth_create_spnego_message(neg_ctx, &base64, &len); 167 if(result) 168 return result; 169 170 userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", 171 base64); 172 173 if(proxy) { 174 Curl_safefree(data->state.aptr.proxyuserpwd); 175 data->state.aptr.proxyuserpwd = userp; 176 } 177 else { 178 Curl_safefree(data->state.aptr.userpwd); 179 data->state.aptr.userpwd = userp; 180 } 181 182 free(base64); 183 184 if(!userp) { 185 return CURLE_OUT_OF_MEMORY; 186 } 187 188 *state = GSS_AUTHSENT; 189 #ifdef HAVE_GSSAPI 190 if(neg_ctx->status == GSS_S_COMPLETE || 191 neg_ctx->status == GSS_S_CONTINUE_NEEDED) { 192 *state = GSS_AUTHDONE; 193 } 194 #else 195 #ifdef USE_WINDOWS_SSPI 196 if(neg_ctx->status == SEC_E_OK || 197 neg_ctx->status == SEC_I_CONTINUE_NEEDED) { 198 *state = GSS_AUTHDONE; 199 } 200 #endif 201 #endif 202 } 203 204 if(*state == GSS_AUTHDONE || *state == GSS_AUTHSUCC) { 205 /* connection is already authenticated, 206 * don't send a header in future requests */ 207 authp->done = TRUE; 208 } 209 210 neg_ctx->havenegdata = FALSE; 211 212 return CURLE_OK; 213} 214 215void Curl_http_auth_cleanup_negotiate(struct connectdata *conn) 216{ 217 conn->http_negotiate_state = GSS_AUTHNONE; 218 conn->proxy_negotiate_state = GSS_AUTHNONE; 219 220 Curl_auth_cleanup_spnego(&conn->negotiate); 221 Curl_auth_cleanup_spnego(&conn->proxyneg); 222} 223 224#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ 225