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