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(USE_WINDOWS_SSPI) && defined(USE_NTLM) 28 29#include <curl/curl.h> 30 31#include "vauth/vauth.h" 32#include "urldata.h" 33#include "curl_ntlm_core.h" 34#include "warnless.h" 35#include "curl_multibyte.h" 36#include "sendf.h" 37#include "strdup.h" 38 39/* The last #include files should be: */ 40#include "curl_memory.h" 41#include "memdebug.h" 42 43/* 44 * Curl_auth_is_ntlm_supported() 45 * 46 * This is used to evaluate if NTLM is supported. 47 * 48 * Parameters: None 49 * 50 * Returns TRUE if NTLM is supported by Windows SSPI. 51 */ 52bool Curl_auth_is_ntlm_supported(void) 53{ 54 PSecPkgInfo SecurityPackage; 55 SECURITY_STATUS status; 56 57 /* Query the security package for NTLM */ 58 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), 59 &SecurityPackage); 60 61 /* Release the package buffer as it is not required anymore */ 62 if(status == SEC_E_OK) { 63 s_pSecFn->FreeContextBuffer(SecurityPackage); 64 } 65 66 return (status == SEC_E_OK ? TRUE : FALSE); 67} 68 69/* 70 * Curl_auth_create_ntlm_type1_message() 71 * 72 * This is used to generate an already encoded NTLM type-1 message ready for 73 * sending to the recipient. 74 * 75 * Parameters: 76 * 77 * data [in] - The session handle. 78 * userp [in] - The user name in the format User or Domain\User. 79 * passwdp [in] - The user's password. 80 * service [in] - The service type such as http, smtp, pop or imap. 81 * host [in] - The host name. 82 * ntlm [in/out] - The NTLM data struct being used and modified. 83 * out [out] - The result storage. 84 * 85 * Returns CURLE_OK on success. 86 */ 87CURLcode Curl_auth_create_ntlm_type1_message(struct Curl_easy *data, 88 const char *userp, 89 const char *passwdp, 90 const char *service, 91 const char *host, 92 struct ntlmdata *ntlm, 93 struct bufref *out) 94{ 95 PSecPkgInfo SecurityPackage; 96 SecBuffer type_1_buf; 97 SecBufferDesc type_1_desc; 98 SECURITY_STATUS status; 99 unsigned long attrs; 100 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ 101 102 /* Clean up any former leftovers and initialise to defaults */ 103 Curl_auth_cleanup_ntlm(ntlm); 104 105 /* Query the security package for NTLM */ 106 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_NTLM), 107 &SecurityPackage); 108 if(status != SEC_E_OK) { 109 failf(data, "SSPI: couldn't get auth info"); 110 return CURLE_AUTH_ERROR; 111 } 112 113 ntlm->token_max = SecurityPackage->cbMaxToken; 114 115 /* Release the package buffer as it is not required anymore */ 116 s_pSecFn->FreeContextBuffer(SecurityPackage); 117 118 /* Allocate our output buffer */ 119 ntlm->output_token = malloc(ntlm->token_max); 120 if(!ntlm->output_token) 121 return CURLE_OUT_OF_MEMORY; 122 123 if(userp && *userp) { 124 CURLcode result; 125 126 /* Populate our identity structure */ 127 result = Curl_create_sspi_identity(userp, passwdp, &ntlm->identity); 128 if(result) 129 return result; 130 131 /* Allow proper cleanup of the identity structure */ 132 ntlm->p_identity = &ntlm->identity; 133 } 134 else 135 /* Use the current Windows user */ 136 ntlm->p_identity = NULL; 137 138 /* Allocate our credentials handle */ 139 ntlm->credentials = calloc(1, sizeof(CredHandle)); 140 if(!ntlm->credentials) 141 return CURLE_OUT_OF_MEMORY; 142 143 /* Acquire our credentials handle */ 144 status = s_pSecFn->AcquireCredentialsHandle(NULL, 145 (TCHAR *) TEXT(SP_NAME_NTLM), 146 SECPKG_CRED_OUTBOUND, NULL, 147 ntlm->p_identity, NULL, NULL, 148 ntlm->credentials, &expiry); 149 if(status != SEC_E_OK) 150 return CURLE_LOGIN_DENIED; 151 152 /* Allocate our new context handle */ 153 ntlm->context = calloc(1, sizeof(CtxtHandle)); 154 if(!ntlm->context) 155 return CURLE_OUT_OF_MEMORY; 156 157 ntlm->spn = Curl_auth_build_spn(service, host, NULL); 158 if(!ntlm->spn) 159 return CURLE_OUT_OF_MEMORY; 160 161 /* Setup the type-1 "output" security buffer */ 162 type_1_desc.ulVersion = SECBUFFER_VERSION; 163 type_1_desc.cBuffers = 1; 164 type_1_desc.pBuffers = &type_1_buf; 165 type_1_buf.BufferType = SECBUFFER_TOKEN; 166 type_1_buf.pvBuffer = ntlm->output_token; 167 type_1_buf.cbBuffer = curlx_uztoul(ntlm->token_max); 168 169 /* Generate our type-1 message */ 170 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, NULL, 171 ntlm->spn, 172 0, 0, SECURITY_NETWORK_DREP, 173 NULL, 0, 174 ntlm->context, &type_1_desc, 175 &attrs, &expiry); 176 if(status == SEC_I_COMPLETE_NEEDED || 177 status == SEC_I_COMPLETE_AND_CONTINUE) 178 s_pSecFn->CompleteAuthToken(ntlm->context, &type_1_desc); 179 else if(status == SEC_E_INSUFFICIENT_MEMORY) 180 return CURLE_OUT_OF_MEMORY; 181 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) 182 return CURLE_AUTH_ERROR; 183 184 /* Return the response. */ 185 Curl_bufref_set(out, ntlm->output_token, type_1_buf.cbBuffer, NULL); 186 return CURLE_OK; 187} 188 189/* 190 * Curl_auth_decode_ntlm_type2_message() 191 * 192 * This is used to decode an already encoded NTLM type-2 message. 193 * 194 * Parameters: 195 * 196 * data [in] - The session handle. 197 * type2 [in] - The type-2 message. 198 * ntlm [in/out] - The NTLM data struct being used and modified. 199 * 200 * Returns CURLE_OK on success. 201 */ 202CURLcode Curl_auth_decode_ntlm_type2_message(struct Curl_easy *data, 203 const struct bufref *type2, 204 struct ntlmdata *ntlm) 205{ 206#if defined(CURL_DISABLE_VERBOSE_STRINGS) 207 (void) data; 208#endif 209 210 /* Ensure we have a valid type-2 message */ 211 if(!Curl_bufref_len(type2)) { 212 infof(data, "NTLM handshake failure (empty type-2 message)"); 213 return CURLE_BAD_CONTENT_ENCODING; 214 } 215 216 /* Store the challenge for later use */ 217 ntlm->input_token = Curl_memdup0((const char *)Curl_bufref_ptr(type2), 218 Curl_bufref_len(type2)); 219 if(!ntlm->input_token) 220 return CURLE_OUT_OF_MEMORY; 221 ntlm->input_token_len = Curl_bufref_len(type2); 222 223 return CURLE_OK; 224} 225 226/* 227* Curl_auth_create_ntlm_type3_message() 228 * Curl_auth_create_ntlm_type3_message() 229 * 230 * This is used to generate an already encoded NTLM type-3 message ready for 231 * sending to the recipient. 232 * 233 * Parameters: 234 * 235 * data [in] - The session handle. 236 * userp [in] - The user name in the format User or Domain\User. 237 * passwdp [in] - The user's password. 238 * ntlm [in/out] - The NTLM data struct being used and modified. 239 * out [out] - The result storage. 240 * 241 * Returns CURLE_OK on success. 242 */ 243CURLcode Curl_auth_create_ntlm_type3_message(struct Curl_easy *data, 244 const char *userp, 245 const char *passwdp, 246 struct ntlmdata *ntlm, 247 struct bufref *out) 248{ 249 CURLcode result = CURLE_OK; 250 SecBuffer type_2_bufs[2]; 251 SecBuffer type_3_buf; 252 SecBufferDesc type_2_desc; 253 SecBufferDesc type_3_desc; 254 SECURITY_STATUS status; 255 unsigned long attrs; 256 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ 257 258#if defined(CURL_DISABLE_VERBOSE_STRINGS) 259 (void) data; 260#endif 261 (void) passwdp; 262 (void) userp; 263 264 /* Setup the type-2 "input" security buffer */ 265 type_2_desc.ulVersion = SECBUFFER_VERSION; 266 type_2_desc.cBuffers = 1; 267 type_2_desc.pBuffers = &type_2_bufs[0]; 268 type_2_bufs[0].BufferType = SECBUFFER_TOKEN; 269 type_2_bufs[0].pvBuffer = ntlm->input_token; 270 type_2_bufs[0].cbBuffer = curlx_uztoul(ntlm->input_token_len); 271 272#ifdef SECPKG_ATTR_ENDPOINT_BINDINGS 273 /* ssl context comes from schannel. 274 * When extended protection is used in IIS server, 275 * we have to pass a second SecBuffer to the SecBufferDesc 276 * otherwise IIS will not pass the authentication (401 response). 277 * Minimum supported version is Windows 7. 278 * https://docs.microsoft.com/en-us/security-updates 279 * /SecurityAdvisories/2009/973811 280 */ 281 if(ntlm->sslContext) { 282 SEC_CHANNEL_BINDINGS channelBindings; 283 SecPkgContext_Bindings pkgBindings; 284 pkgBindings.Bindings = &channelBindings; 285 status = s_pSecFn->QueryContextAttributes( 286 ntlm->sslContext, 287 SECPKG_ATTR_ENDPOINT_BINDINGS, 288 &pkgBindings 289 ); 290 if(status == SEC_E_OK) { 291 type_2_desc.cBuffers++; 292 type_2_bufs[1].BufferType = SECBUFFER_CHANNEL_BINDINGS; 293 type_2_bufs[1].cbBuffer = pkgBindings.BindingsLength; 294 type_2_bufs[1].pvBuffer = pkgBindings.Bindings; 295 } 296 } 297#endif 298 299 /* Setup the type-3 "output" security buffer */ 300 type_3_desc.ulVersion = SECBUFFER_VERSION; 301 type_3_desc.cBuffers = 1; 302 type_3_desc.pBuffers = &type_3_buf; 303 type_3_buf.BufferType = SECBUFFER_TOKEN; 304 type_3_buf.pvBuffer = ntlm->output_token; 305 type_3_buf.cbBuffer = curlx_uztoul(ntlm->token_max); 306 307 /* Generate our type-3 message */ 308 status = s_pSecFn->InitializeSecurityContext(ntlm->credentials, 309 ntlm->context, 310 ntlm->spn, 311 0, 0, SECURITY_NETWORK_DREP, 312 &type_2_desc, 313 0, ntlm->context, 314 &type_3_desc, 315 &attrs, &expiry); 316 if(status != SEC_E_OK) { 317 infof(data, "NTLM handshake failure (type-3 message): Status=%lx", 318 status); 319 320 if(status == SEC_E_INSUFFICIENT_MEMORY) 321 return CURLE_OUT_OF_MEMORY; 322 323 return CURLE_AUTH_ERROR; 324 } 325 326 /* Return the response. */ 327 result = Curl_bufref_memdup(out, ntlm->output_token, type_3_buf.cbBuffer); 328 Curl_auth_cleanup_ntlm(ntlm); 329 return result; 330} 331 332/* 333 * Curl_auth_cleanup_ntlm() 334 * 335 * This is used to clean up the NTLM specific data. 336 * 337 * Parameters: 338 * 339 * ntlm [in/out] - The NTLM data struct being cleaned up. 340 * 341 */ 342void Curl_auth_cleanup_ntlm(struct ntlmdata *ntlm) 343{ 344 /* Free our security context */ 345 if(ntlm->context) { 346 s_pSecFn->DeleteSecurityContext(ntlm->context); 347 free(ntlm->context); 348 ntlm->context = NULL; 349 } 350 351 /* Free our credentials handle */ 352 if(ntlm->credentials) { 353 s_pSecFn->FreeCredentialsHandle(ntlm->credentials); 354 free(ntlm->credentials); 355 ntlm->credentials = NULL; 356 } 357 358 /* Free our identity */ 359 Curl_sspi_free_identity(ntlm->p_identity); 360 ntlm->p_identity = NULL; 361 362 /* Free the input and output tokens */ 363 Curl_safefree(ntlm->input_token); 364 Curl_safefree(ntlm->output_token); 365 366 /* Reset any variables */ 367 ntlm->token_max = 0; 368 369 Curl_safefree(ntlm->spn); 370} 371 372#endif /* USE_WINDOWS_SSPI && USE_NTLM */ 373