1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2015, 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 http://curl.haxx.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 ***************************************************************************/ 22 23#include "curl_setup.h" 24 25#ifdef USE_WINDOWS_SSPI 26 27#if !defined(CURL_DISABLE_HTTP) && defined(USE_SPNEGO) 28 29#include "urldata.h" 30#include "sendf.h" 31#include "rawstr.h" 32#include "warnless.h" 33#include "curl_base64.h" 34#include "curl_sasl.h" 35#include "http_negotiate.h" 36#include "curl_multibyte.h" 37#include "curl_printf.h" 38 39/* The last #include files should be: */ 40#include "curl_memory.h" 41#include "memdebug.h" 42 43CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, 44 const char *header) 45{ 46 struct SessionHandle *data = conn->data; 47 BYTE *input_token = NULL; 48 SecBufferDesc out_buff_desc; 49 SecBuffer out_sec_buff; 50 SecBufferDesc in_buff_desc; 51 SecBuffer in_sec_buff; 52 SECURITY_STATUS status; 53 unsigned long attrs; 54 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ 55 size_t len = 0, input_token_len = 0; 56 CURLcode result; 57 58 /* Point to the username and password */ 59 const char *userp; 60 const char *passwdp; 61 62 /* Point to the correct struct with this */ 63 struct negotiatedata *neg_ctx; 64 65 if(proxy) { 66 userp = conn->proxyuser; 67 passwdp = conn->proxypasswd; 68 neg_ctx = &data->state.proxyneg; 69 } 70 else { 71 userp = conn->user; 72 passwdp = conn->passwd; 73 neg_ctx = &data->state.negotiate; 74 } 75 76 /* Not set means empty */ 77 if(!userp) 78 userp = ""; 79 80 if(!passwdp) 81 passwdp = ""; 82 83 if(neg_ctx->context && neg_ctx->status == SEC_E_OK) { 84 /* We finished successfully our part of authentication, but server 85 * rejected it (since we're again here). Exit with an error since we 86 * can't invent anything better */ 87 Curl_cleanup_negotiate(data); 88 return CURLE_LOGIN_DENIED; 89 } 90 91 if(!neg_ctx->server_name) { 92 /* Check proxy auth requested but no given proxy name */ 93 if(proxy && !conn->proxy.name) 94 return CURLE_BAD_FUNCTION_ARGUMENT; 95 96 /* Generate our SPN */ 97 neg_ctx->server_name = Curl_sasl_build_spn( 98 proxy ? data->set.str[STRING_PROXY_SERVICE_NAME] : 99 data->set.str[STRING_SERVICE_NAME], 100 proxy ? conn->proxy.name : conn->host.name); 101 if(!neg_ctx->server_name) 102 return CURLE_OUT_OF_MEMORY; 103 } 104 105 if(!neg_ctx->output_token) { 106 PSecPkgInfo SecurityPackage; 107 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) 108 TEXT(SP_NAME_NEGOTIATE), 109 &SecurityPackage); 110 if(status != SEC_E_OK) 111 return CURLE_NOT_BUILT_IN; 112 113 /* Allocate input and output buffers according to the max token size 114 as indicated by the security package */ 115 neg_ctx->token_max = SecurityPackage->cbMaxToken; 116 neg_ctx->output_token = malloc(neg_ctx->token_max); 117 s_pSecFn->FreeContextBuffer(SecurityPackage); 118 } 119 120 /* Obtain the input token, if any */ 121 header += strlen("Negotiate"); 122 while(*header && ISSPACE(*header)) 123 header++; 124 125 len = strlen(header); 126 if(!len) { 127 /* Is this the first call in a new negotiation? */ 128 if(neg_ctx->context) { 129 /* The server rejected our authentication and hasn't suppled any more 130 negotiation mechanisms */ 131 return CURLE_LOGIN_DENIED; 132 } 133 134 /* We have to acquire credentials and allocate memory for the context */ 135 neg_ctx->credentials = malloc(sizeof(CredHandle)); 136 neg_ctx->context = malloc(sizeof(CtxtHandle)); 137 138 if(!neg_ctx->credentials || !neg_ctx->context) 139 return CURLE_OUT_OF_MEMORY; 140 141 if(userp && *userp) { 142 /* Populate our identity structure */ 143 result = Curl_create_sspi_identity(userp, passwdp, &neg_ctx->identity); 144 if(result) 145 return result; 146 147 /* Allow proper cleanup of the identity structure */ 148 neg_ctx->p_identity = &neg_ctx->identity; 149 } 150 else 151 /* Use the current Windows user */ 152 neg_ctx->p_identity = NULL; 153 154 /* Acquire our credientials handle */ 155 neg_ctx->status = 156 s_pSecFn->AcquireCredentialsHandle(NULL, 157 (TCHAR *) TEXT(SP_NAME_NEGOTIATE), 158 SECPKG_CRED_OUTBOUND, NULL, 159 neg_ctx->p_identity, NULL, NULL, 160 neg_ctx->credentials, &expiry); 161 if(neg_ctx->status != SEC_E_OK) 162 return CURLE_LOGIN_DENIED; 163 } 164 else { 165 result = Curl_base64_decode(header, 166 (unsigned char **)&input_token, 167 &input_token_len); 168 if(result) 169 return result; 170 171 if(!input_token_len) { 172 infof(data, 173 "Negotiate handshake failure (empty challenge message)\n"); 174 175 return CURLE_BAD_CONTENT_ENCODING; 176 } 177 } 178 179 /* Setup the "output" security buffer */ 180 out_buff_desc.ulVersion = SECBUFFER_VERSION; 181 out_buff_desc.cBuffers = 1; 182 out_buff_desc.pBuffers = &out_sec_buff; 183 out_sec_buff.BufferType = SECBUFFER_TOKEN; 184 out_sec_buff.pvBuffer = neg_ctx->output_token; 185 out_sec_buff.cbBuffer = curlx_uztoul(neg_ctx->token_max); 186 187 /* Setup the "input" security buffer if present */ 188 if(input_token) { 189 in_buff_desc.ulVersion = SECBUFFER_VERSION; 190 in_buff_desc.cBuffers = 1; 191 in_buff_desc.pBuffers = &in_sec_buff; 192 in_sec_buff.BufferType = SECBUFFER_TOKEN; 193 in_sec_buff.pvBuffer = input_token; 194 in_sec_buff.cbBuffer = curlx_uztoul(input_token_len); 195 } 196 197 /* Generate our message */ 198 neg_ctx->status = s_pSecFn->InitializeSecurityContext( 199 neg_ctx->credentials, 200 input_token ? neg_ctx->context : NULL, 201 neg_ctx->server_name, 202 ISC_REQ_CONFIDENTIALITY, 203 0, 204 SECURITY_NATIVE_DREP, 205 input_token ? &in_buff_desc : NULL, 206 0, 207 neg_ctx->context, 208 &out_buff_desc, 209 &attrs, 210 &expiry); 211 212 free(input_token); 213 214 if(GSS_ERROR(neg_ctx->status)) 215 return CURLE_OUT_OF_MEMORY; 216 217 if(neg_ctx->status == SEC_I_COMPLETE_NEEDED || 218 neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE) { 219 neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context, 220 &out_buff_desc); 221 if(GSS_ERROR(neg_ctx->status)) 222 return CURLE_RECV_ERROR; 223 } 224 225 neg_ctx->output_token_length = out_sec_buff.cbBuffer; 226 227 return CURLE_OK; 228} 229 230CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) 231{ 232 struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: 233 &conn->data->state.negotiate; 234 char *encoded = NULL; 235 size_t len = 0; 236 char *userp; 237 CURLcode error; 238 239 error = Curl_base64_encode(conn->data, 240 (const char*)neg_ctx->output_token, 241 neg_ctx->output_token_length, 242 &encoded, &len); 243 if(error) 244 return error; 245 246 if(!len) 247 return CURLE_REMOTE_ACCESS_DENIED; 248 249 userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", 250 encoded); 251 252 if(proxy) { 253 Curl_safefree(conn->allocptr.proxyuserpwd); 254 conn->allocptr.proxyuserpwd = userp; 255 } 256 else { 257 Curl_safefree(conn->allocptr.userpwd); 258 conn->allocptr.userpwd = userp; 259 } 260 free(encoded); 261 return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; 262} 263 264static void cleanup(struct negotiatedata *neg_ctx) 265{ 266 /* Free our security context */ 267 if(neg_ctx->context) { 268 s_pSecFn->DeleteSecurityContext(neg_ctx->context); 269 free(neg_ctx->context); 270 neg_ctx->context = NULL; 271 } 272 273 /* Free our credentials handle */ 274 if(neg_ctx->credentials) { 275 s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials); 276 free(neg_ctx->credentials); 277 neg_ctx->credentials = NULL; 278 } 279 280 /* Free our identity */ 281 Curl_sspi_free_identity(neg_ctx->p_identity); 282 neg_ctx->p_identity = NULL; 283 284 /* Free the SPN and output token */ 285 Curl_safefree(neg_ctx->server_name); 286 Curl_safefree(neg_ctx->output_token); 287 288 /* Reset any variables */ 289 neg_ctx->token_max = 0; 290} 291 292void Curl_cleanup_negotiate(struct SessionHandle *data) 293{ 294 cleanup(&data->state.negotiate); 295 cleanup(&data->state.proxyneg); 296} 297 298#endif /* !CURL_DISABLE_HTTP && USE_SPNEGO */ 299 300#endif /* USE_WINDOWS_SSPI */ 301