Mercurial > hgrepos > Python2 > PyMuPDF
comparison mupdf-source/thirdparty/curl/lib/vtls/schannel.c @ 2:b50eed0cc0ef upstream
ADD: MuPDF v1.26.7: the MuPDF source as downloaded by a default build of PyMuPDF 1.26.4.
The directory name has changed: no version number in the expanded directory now.
| author | Franz Glasner <fzglas.hg@dom66.de> |
|---|---|
| date | Mon, 15 Sep 2025 11:43:07 +0200 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| 1:1d09e1dec1d9 | 2:b50eed0cc0ef |
|---|---|
| 1 /*************************************************************************** | |
| 2 * _ _ ____ _ | |
| 3 * Project ___| | | | _ \| | | |
| 4 * / __| | | | |_) | | | |
| 5 * | (__| |_| | _ <| |___ | |
| 6 * \___|\___/|_| \_\_____| | |
| 7 * | |
| 8 * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de> | |
| 9 * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> | |
| 10 * Copyright (C) 2012 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al. | |
| 11 * | |
| 12 * This software is licensed as described in the file COPYING, which | |
| 13 * you should have received as part of this distribution. The terms | |
| 14 * are also available at https://curl.haxx.se/docs/copyright.html. | |
| 15 * | |
| 16 * You may opt to use, copy, modify, merge, publish, distribute and/or sell | |
| 17 * copies of the Software, and permit persons to whom the Software is | |
| 18 * furnished to do so, under the terms of the COPYING file. | |
| 19 * | |
| 20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | |
| 21 * KIND, either express or implied. | |
| 22 * | |
| 23 ***************************************************************************/ | |
| 24 | |
| 25 /* | |
| 26 * Source file for all Schannel-specific code for the TLS/SSL layer. No code | |
| 27 * but vtls.c should ever call or use these functions. | |
| 28 */ | |
| 29 | |
| 30 /* | |
| 31 * Based upon the PolarSSL implementation in polarssl.c and polarssl.h: | |
| 32 * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> | |
| 33 * | |
| 34 * Based upon the CyaSSL implementation in cyassl.c and cyassl.h: | |
| 35 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. | |
| 36 * | |
| 37 * Thanks for code and inspiration! | |
| 38 */ | |
| 39 | |
| 40 #include "curl_setup.h" | |
| 41 | |
| 42 #ifdef USE_SCHANNEL | |
| 43 | |
| 44 #define EXPOSE_SCHANNEL_INTERNAL_STRUCTS | |
| 45 | |
| 46 #ifndef USE_WINDOWS_SSPI | |
| 47 # error "Can't compile SCHANNEL support without SSPI." | |
| 48 #endif | |
| 49 | |
| 50 #include "schannel.h" | |
| 51 #include "vtls.h" | |
| 52 #include "sendf.h" | |
| 53 #include "connect.h" /* for the connect timeout */ | |
| 54 #include "strerror.h" | |
| 55 #include "select.h" /* for the socket readyness */ | |
| 56 #include "inet_pton.h" /* for IP addr SNI check */ | |
| 57 #include "curl_multibyte.h" | |
| 58 #include "warnless.h" | |
| 59 #include "x509asn1.h" | |
| 60 #include "curl_printf.h" | |
| 61 #include "multiif.h" | |
| 62 #include "system_win32.h" | |
| 63 | |
| 64 /* The last #include file should be: */ | |
| 65 #include "curl_memory.h" | |
| 66 #include "memdebug.h" | |
| 67 | |
| 68 /* ALPN requires version 8.1 of the Windows SDK, which was | |
| 69 shipped with Visual Studio 2013, aka _MSC_VER 1800: | |
| 70 | |
| 71 https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx | |
| 72 */ | |
| 73 #if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_) | |
| 74 # define HAS_ALPN 1 | |
| 75 #endif | |
| 76 | |
| 77 #ifndef UNISP_NAME_A | |
| 78 #define UNISP_NAME_A "Microsoft Unified Security Protocol Provider" | |
| 79 #endif | |
| 80 | |
| 81 #ifndef UNISP_NAME_W | |
| 82 #define UNISP_NAME_W L"Microsoft Unified Security Protocol Provider" | |
| 83 #endif | |
| 84 | |
| 85 #ifndef UNISP_NAME | |
| 86 #ifdef UNICODE | |
| 87 #define UNISP_NAME UNISP_NAME_W | |
| 88 #else | |
| 89 #define UNISP_NAME UNISP_NAME_A | |
| 90 #endif | |
| 91 #endif | |
| 92 | |
| 93 #if defined(CryptStringToBinary) && defined(CRYPT_STRING_HEX) | |
| 94 #define HAS_CLIENT_CERT_PATH | |
| 95 #endif | |
| 96 | |
| 97 #ifdef HAS_CLIENT_CERT_PATH | |
| 98 #ifdef UNICODE | |
| 99 #define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_W | |
| 100 #else | |
| 101 #define CURL_CERT_STORE_PROV_SYSTEM CERT_STORE_PROV_SYSTEM_A | |
| 102 #endif | |
| 103 #endif | |
| 104 | |
| 105 #ifndef SP_PROT_SSL2_CLIENT | |
| 106 #define SP_PROT_SSL2_CLIENT 0x00000008 | |
| 107 #endif | |
| 108 | |
| 109 #ifndef SP_PROT_SSL3_CLIENT | |
| 110 #define SP_PROT_SSL3_CLIENT 0x00000008 | |
| 111 #endif | |
| 112 | |
| 113 #ifndef SP_PROT_TLS1_CLIENT | |
| 114 #define SP_PROT_TLS1_CLIENT 0x00000080 | |
| 115 #endif | |
| 116 | |
| 117 #ifndef SP_PROT_TLS1_0_CLIENT | |
| 118 #define SP_PROT_TLS1_0_CLIENT SP_PROT_TLS1_CLIENT | |
| 119 #endif | |
| 120 | |
| 121 #ifndef SP_PROT_TLS1_1_CLIENT | |
| 122 #define SP_PROT_TLS1_1_CLIENT 0x00000200 | |
| 123 #endif | |
| 124 | |
| 125 #ifndef SP_PROT_TLS1_2_CLIENT | |
| 126 #define SP_PROT_TLS1_2_CLIENT 0x00000800 | |
| 127 #endif | |
| 128 | |
| 129 #ifndef SECBUFFER_ALERT | |
| 130 #define SECBUFFER_ALERT 17 | |
| 131 #endif | |
| 132 | |
| 133 /* Both schannel buffer sizes must be > 0 */ | |
| 134 #define CURL_SCHANNEL_BUFFER_INIT_SIZE 4096 | |
| 135 #define CURL_SCHANNEL_BUFFER_FREE_SIZE 1024 | |
| 136 | |
| 137 #define CERT_THUMBPRINT_STR_LEN 40 | |
| 138 #define CERT_THUMBPRINT_DATA_LEN 20 | |
| 139 | |
| 140 /* Uncomment to force verbose output | |
| 141 * #define infof(x, y, ...) printf(y, __VA_ARGS__) | |
| 142 * #define failf(x, y, ...) printf(y, __VA_ARGS__) | |
| 143 */ | |
| 144 | |
| 145 #ifndef CALG_SHA_256 | |
| 146 # define CALG_SHA_256 0x0000800c | |
| 147 #endif | |
| 148 | |
| 149 #define BACKEND connssl->backend | |
| 150 | |
| 151 static Curl_recv schannel_recv; | |
| 152 static Curl_send schannel_send; | |
| 153 | |
| 154 static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, | |
| 155 const char *pinnedpubkey); | |
| 156 | |
| 157 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, | |
| 158 void *BufDataPtr, unsigned long BufByteSize) | |
| 159 { | |
| 160 buffer->cbBuffer = BufByteSize; | |
| 161 buffer->BufferType = BufType; | |
| 162 buffer->pvBuffer = BufDataPtr; | |
| 163 } | |
| 164 | |
| 165 static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, | |
| 166 unsigned long NumArrElem) | |
| 167 { | |
| 168 desc->ulVersion = SECBUFFER_VERSION; | |
| 169 desc->pBuffers = BufArr; | |
| 170 desc->cBuffers = NumArrElem; | |
| 171 } | |
| 172 | |
| 173 static CURLcode | |
| 174 set_ssl_version_min_max(SCHANNEL_CRED *schannel_cred, struct connectdata *conn) | |
| 175 { | |
| 176 struct Curl_easy *data = conn->data; | |
| 177 long ssl_version = SSL_CONN_CONFIG(version); | |
| 178 long ssl_version_max = SSL_CONN_CONFIG(version_max); | |
| 179 long i = ssl_version; | |
| 180 | |
| 181 switch(ssl_version_max) { | |
| 182 case CURL_SSLVERSION_MAX_NONE: | |
| 183 case CURL_SSLVERSION_MAX_DEFAULT: | |
| 184 ssl_version_max = CURL_SSLVERSION_MAX_TLSv1_2; | |
| 185 break; | |
| 186 } | |
| 187 for(; i <= (ssl_version_max >> 16); ++i) { | |
| 188 switch(i) { | |
| 189 case CURL_SSLVERSION_TLSv1_0: | |
| 190 schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_0_CLIENT; | |
| 191 break; | |
| 192 case CURL_SSLVERSION_TLSv1_1: | |
| 193 schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_1_CLIENT; | |
| 194 break; | |
| 195 case CURL_SSLVERSION_TLSv1_2: | |
| 196 schannel_cred->grbitEnabledProtocols |= SP_PROT_TLS1_2_CLIENT; | |
| 197 break; | |
| 198 case CURL_SSLVERSION_TLSv1_3: | |
| 199 failf(data, "schannel: TLS 1.3 is not yet supported"); | |
| 200 return CURLE_SSL_CONNECT_ERROR; | |
| 201 } | |
| 202 } | |
| 203 return CURLE_OK; | |
| 204 } | |
| 205 | |
| 206 /*longest is 26, buffer is slightly bigger*/ | |
| 207 #define LONGEST_ALG_ID 32 | |
| 208 #define CIPHEROPTION(X) \ | |
| 209 if(strcmp(#X, tmp) == 0) \ | |
| 210 return X | |
| 211 | |
| 212 static int | |
| 213 get_alg_id_by_name(char *name) | |
| 214 { | |
| 215 char tmp[LONGEST_ALG_ID] = { 0 }; | |
| 216 char *nameEnd = strchr(name, ':'); | |
| 217 size_t n = nameEnd ? min((size_t)(nameEnd - name), LONGEST_ALG_ID - 1) : \ | |
| 218 min(strlen(name), LONGEST_ALG_ID - 1); | |
| 219 strncpy(tmp, name, n); | |
| 220 tmp[n] = 0; | |
| 221 CIPHEROPTION(CALG_MD2); | |
| 222 CIPHEROPTION(CALG_MD4); | |
| 223 CIPHEROPTION(CALG_MD5); | |
| 224 CIPHEROPTION(CALG_SHA); | |
| 225 CIPHEROPTION(CALG_SHA1); | |
| 226 CIPHEROPTION(CALG_MAC); | |
| 227 CIPHEROPTION(CALG_RSA_SIGN); | |
| 228 CIPHEROPTION(CALG_DSS_SIGN); | |
| 229 /*ifdefs for the options that are defined conditionally in wincrypt.h*/ | |
| 230 #ifdef CALG_NO_SIGN | |
| 231 CIPHEROPTION(CALG_NO_SIGN); | |
| 232 #endif | |
| 233 CIPHEROPTION(CALG_RSA_KEYX); | |
| 234 CIPHEROPTION(CALG_DES); | |
| 235 #ifdef CALG_3DES_112 | |
| 236 CIPHEROPTION(CALG_3DES_112); | |
| 237 #endif | |
| 238 CIPHEROPTION(CALG_3DES); | |
| 239 CIPHEROPTION(CALG_DESX); | |
| 240 CIPHEROPTION(CALG_RC2); | |
| 241 CIPHEROPTION(CALG_RC4); | |
| 242 CIPHEROPTION(CALG_SEAL); | |
| 243 #ifdef CALG_DH_SF | |
| 244 CIPHEROPTION(CALG_DH_SF); | |
| 245 #endif | |
| 246 CIPHEROPTION(CALG_DH_EPHEM); | |
| 247 #ifdef CALG_AGREEDKEY_ANY | |
| 248 CIPHEROPTION(CALG_AGREEDKEY_ANY); | |
| 249 #endif | |
| 250 #ifdef CALG_HUGHES_MD5 | |
| 251 CIPHEROPTION(CALG_HUGHES_MD5); | |
| 252 #endif | |
| 253 CIPHEROPTION(CALG_SKIPJACK); | |
| 254 #ifdef CALG_TEK | |
| 255 CIPHEROPTION(CALG_TEK); | |
| 256 #endif | |
| 257 CIPHEROPTION(CALG_CYLINK_MEK); | |
| 258 CIPHEROPTION(CALG_SSL3_SHAMD5); | |
| 259 #ifdef CALG_SSL3_MASTER | |
| 260 CIPHEROPTION(CALG_SSL3_MASTER); | |
| 261 #endif | |
| 262 #ifdef CALG_SCHANNEL_MASTER_HASH | |
| 263 CIPHEROPTION(CALG_SCHANNEL_MASTER_HASH); | |
| 264 #endif | |
| 265 #ifdef CALG_SCHANNEL_MAC_KEY | |
| 266 CIPHEROPTION(CALG_SCHANNEL_MAC_KEY); | |
| 267 #endif | |
| 268 #ifdef CALG_SCHANNEL_ENC_KEY | |
| 269 CIPHEROPTION(CALG_SCHANNEL_ENC_KEY); | |
| 270 #endif | |
| 271 #ifdef CALG_PCT1_MASTER | |
| 272 CIPHEROPTION(CALG_PCT1_MASTER); | |
| 273 #endif | |
| 274 #ifdef CALG_SSL2_MASTER | |
| 275 CIPHEROPTION(CALG_SSL2_MASTER); | |
| 276 #endif | |
| 277 #ifdef CALG_TLS1_MASTER | |
| 278 CIPHEROPTION(CALG_TLS1_MASTER); | |
| 279 #endif | |
| 280 #ifdef CALG_RC5 | |
| 281 CIPHEROPTION(CALG_RC5); | |
| 282 #endif | |
| 283 #ifdef CALG_HMAC | |
| 284 CIPHEROPTION(CALG_HMAC); | |
| 285 #endif | |
| 286 #if !defined(__W32API_MAJOR_VERSION) || \ | |
| 287 !defined(__W32API_MINOR_VERSION) || \ | |
| 288 defined(__MINGW64_VERSION_MAJOR) || \ | |
| 289 (__W32API_MAJOR_VERSION > 5) || \ | |
| 290 ((__W32API_MAJOR_VERSION == 5) && (__W32API_MINOR_VERSION > 0)) | |
| 291 /* CALG_TLS1PRF has a syntax error in MinGW's w32api up to version 5.0, | |
| 292 see https://osdn.net/projects/mingw/ticket/38391 */ | |
| 293 CIPHEROPTION(CALG_TLS1PRF); | |
| 294 #endif | |
| 295 #ifdef CALG_HASH_REPLACE_OWF | |
| 296 CIPHEROPTION(CALG_HASH_REPLACE_OWF); | |
| 297 #endif | |
| 298 #ifdef CALG_AES_128 | |
| 299 CIPHEROPTION(CALG_AES_128); | |
| 300 #endif | |
| 301 #ifdef CALG_AES_192 | |
| 302 CIPHEROPTION(CALG_AES_192); | |
| 303 #endif | |
| 304 #ifdef CALG_AES_256 | |
| 305 CIPHEROPTION(CALG_AES_256); | |
| 306 #endif | |
| 307 #ifdef CALG_AES | |
| 308 CIPHEROPTION(CALG_AES); | |
| 309 #endif | |
| 310 #ifdef CALG_SHA_256 | |
| 311 CIPHEROPTION(CALG_SHA_256); | |
| 312 #endif | |
| 313 #ifdef CALG_SHA_384 | |
| 314 CIPHEROPTION(CALG_SHA_384); | |
| 315 #endif | |
| 316 #ifdef CALG_SHA_512 | |
| 317 CIPHEROPTION(CALG_SHA_512); | |
| 318 #endif | |
| 319 #ifdef CALG_ECDH | |
| 320 CIPHEROPTION(CALG_ECDH); | |
| 321 #endif | |
| 322 #ifdef CALG_ECMQV | |
| 323 CIPHEROPTION(CALG_ECMQV); | |
| 324 #endif | |
| 325 #ifdef CALG_ECDSA | |
| 326 CIPHEROPTION(CALG_ECDSA); | |
| 327 #endif | |
| 328 #ifdef CALG_ECDH_EPHEM | |
| 329 CIPHEROPTION(CALG_ECDH_EPHEM); | |
| 330 #endif | |
| 331 return 0; | |
| 332 } | |
| 333 | |
| 334 static CURLcode | |
| 335 set_ssl_ciphers(SCHANNEL_CRED *schannel_cred, char *ciphers) | |
| 336 { | |
| 337 char *startCur = ciphers; | |
| 338 int algCount = 0; | |
| 339 static ALG_ID algIds[45]; /*There are 45 listed in the MS headers*/ | |
| 340 while(startCur && (0 != *startCur) && (algCount < 45)) { | |
| 341 long alg = strtol(startCur, 0, 0); | |
| 342 if(!alg) | |
| 343 alg = get_alg_id_by_name(startCur); | |
| 344 if(alg) | |
| 345 algIds[algCount++] = alg; | |
| 346 else | |
| 347 return CURLE_SSL_CIPHER; | |
| 348 startCur = strchr(startCur, ':'); | |
| 349 if(startCur) | |
| 350 startCur++; | |
| 351 } | |
| 352 schannel_cred->palgSupportedAlgs = algIds; | |
| 353 schannel_cred->cSupportedAlgs = algCount; | |
| 354 return CURLE_OK; | |
| 355 } | |
| 356 | |
| 357 #ifdef HAS_CLIENT_CERT_PATH | |
| 358 static CURLcode | |
| 359 get_cert_location(TCHAR *path, DWORD *store_name, TCHAR **store_path, | |
| 360 TCHAR **thumbprint) | |
| 361 { | |
| 362 TCHAR *sep; | |
| 363 TCHAR *store_path_start; | |
| 364 size_t store_name_len; | |
| 365 | |
| 366 sep = _tcschr(path, TEXT('\\')); | |
| 367 if(sep == NULL) | |
| 368 return CURLE_SSL_CERTPROBLEM; | |
| 369 | |
| 370 store_name_len = sep - path; | |
| 371 | |
| 372 if(_tcsnccmp(path, TEXT("CurrentUser"), store_name_len) == 0) | |
| 373 *store_name = CERT_SYSTEM_STORE_CURRENT_USER; | |
| 374 else if(_tcsnccmp(path, TEXT("LocalMachine"), store_name_len) == 0) | |
| 375 *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE; | |
| 376 else if(_tcsnccmp(path, TEXT("CurrentService"), store_name_len) == 0) | |
| 377 *store_name = CERT_SYSTEM_STORE_CURRENT_SERVICE; | |
| 378 else if(_tcsnccmp(path, TEXT("Services"), store_name_len) == 0) | |
| 379 *store_name = CERT_SYSTEM_STORE_SERVICES; | |
| 380 else if(_tcsnccmp(path, TEXT("Users"), store_name_len) == 0) | |
| 381 *store_name = CERT_SYSTEM_STORE_USERS; | |
| 382 else if(_tcsnccmp(path, TEXT("CurrentUserGroupPolicy"), | |
| 383 store_name_len) == 0) | |
| 384 *store_name = CERT_SYSTEM_STORE_CURRENT_USER_GROUP_POLICY; | |
| 385 else if(_tcsnccmp(path, TEXT("LocalMachineGroupPolicy"), | |
| 386 store_name_len) == 0) | |
| 387 *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_GROUP_POLICY; | |
| 388 else if(_tcsnccmp(path, TEXT("LocalMachineEnterprise"), | |
| 389 store_name_len) == 0) | |
| 390 *store_name = CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE; | |
| 391 else | |
| 392 return CURLE_SSL_CERTPROBLEM; | |
| 393 | |
| 394 store_path_start = sep + 1; | |
| 395 | |
| 396 sep = _tcschr(store_path_start, TEXT('\\')); | |
| 397 if(sep == NULL) | |
| 398 return CURLE_SSL_CERTPROBLEM; | |
| 399 | |
| 400 *sep = TEXT('\0'); | |
| 401 *store_path = _tcsdup(store_path_start); | |
| 402 *sep = TEXT('\\'); | |
| 403 if(*store_path == NULL) | |
| 404 return CURLE_OUT_OF_MEMORY; | |
| 405 | |
| 406 *thumbprint = sep + 1; | |
| 407 if(_tcslen(*thumbprint) != CERT_THUMBPRINT_STR_LEN) | |
| 408 return CURLE_SSL_CERTPROBLEM; | |
| 409 | |
| 410 return CURLE_OK; | |
| 411 } | |
| 412 #endif | |
| 413 | |
| 414 static CURLcode | |
| 415 schannel_connect_step1(struct connectdata *conn, int sockindex) | |
| 416 { | |
| 417 ssize_t written = -1; | |
| 418 struct Curl_easy *data = conn->data; | |
| 419 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 420 SecBuffer outbuf; | |
| 421 SecBufferDesc outbuf_desc; | |
| 422 SecBuffer inbuf; | |
| 423 SecBufferDesc inbuf_desc; | |
| 424 #ifdef HAS_ALPN | |
| 425 unsigned char alpn_buffer[128]; | |
| 426 #endif | |
| 427 SCHANNEL_CRED schannel_cred; | |
| 428 PCCERT_CONTEXT client_certs[1] = { NULL }; | |
| 429 SECURITY_STATUS sspi_status = SEC_E_OK; | |
| 430 struct curl_schannel_cred *old_cred = NULL; | |
| 431 struct in_addr addr; | |
| 432 #ifdef ENABLE_IPV6 | |
| 433 struct in6_addr addr6; | |
| 434 #endif | |
| 435 TCHAR *host_name; | |
| 436 CURLcode result; | |
| 437 char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : | |
| 438 conn->host.name; | |
| 439 | |
| 440 DEBUGF(infof(data, | |
| 441 "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n", | |
| 442 hostname, conn->remote_port)); | |
| 443 | |
| 444 if(Curl_verify_windows_version(5, 1, PLATFORM_WINNT, | |
| 445 VERSION_LESS_THAN_EQUAL)) { | |
| 446 /* Schannel in Windows XP (OS version 5.1) uses legacy handshakes and | |
| 447 algorithms that may not be supported by all servers. */ | |
| 448 infof(data, "schannel: Windows version is old and may not be able to " | |
| 449 "connect to some servers due to lack of SNI, algorithms, etc.\n"); | |
| 450 } | |
| 451 | |
| 452 #ifdef HAS_ALPN | |
| 453 /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above. | |
| 454 Also it doesn't seem to be supported for Wine, see curl bug #983. */ | |
| 455 BACKEND->use_alpn = conn->bits.tls_enable_alpn && | |
| 456 !GetProcAddress(GetModuleHandleA("ntdll"), | |
| 457 "wine_get_version") && | |
| 458 Curl_verify_windows_version(6, 3, PLATFORM_WINNT, | |
| 459 VERSION_GREATER_THAN_EQUAL); | |
| 460 #else | |
| 461 BACKEND->use_alpn = false; | |
| 462 #endif | |
| 463 | |
| 464 #ifdef _WIN32_WCE | |
| 465 #ifdef HAS_MANUAL_VERIFY_API | |
| 466 /* certificate validation on CE doesn't seem to work right; we'll | |
| 467 * do it following a more manual process. */ | |
| 468 BACKEND->use_manual_cred_validation = true; | |
| 469 #else | |
| 470 #error "compiler too old to support requisite manual cert verify for Win CE" | |
| 471 #endif | |
| 472 #else | |
| 473 #ifdef HAS_MANUAL_VERIFY_API | |
| 474 if(SSL_CONN_CONFIG(CAfile)) { | |
| 475 if(Curl_verify_windows_version(6, 1, PLATFORM_WINNT, | |
| 476 VERSION_GREATER_THAN_EQUAL)) { | |
| 477 BACKEND->use_manual_cred_validation = true; | |
| 478 } | |
| 479 else { | |
| 480 failf(data, "schannel: this version of Windows is too old to support " | |
| 481 "certificate verification via CA bundle file."); | |
| 482 return CURLE_SSL_CACERT_BADFILE; | |
| 483 } | |
| 484 } | |
| 485 else | |
| 486 BACKEND->use_manual_cred_validation = false; | |
| 487 #else | |
| 488 if(SSL_CONN_CONFIG(CAfile)) { | |
| 489 failf(data, "schannel: CA cert support not built in"); | |
| 490 return CURLE_NOT_BUILT_IN; | |
| 491 } | |
| 492 #endif | |
| 493 #endif | |
| 494 | |
| 495 BACKEND->cred = NULL; | |
| 496 | |
| 497 /* check for an existing re-usable credential handle */ | |
| 498 if(SSL_SET_OPTION(primary.sessionid)) { | |
| 499 Curl_ssl_sessionid_lock(conn); | |
| 500 if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) { | |
| 501 BACKEND->cred = old_cred; | |
| 502 DEBUGF(infof(data, "schannel: re-using existing credential handle\n")); | |
| 503 | |
| 504 /* increment the reference counter of the credential/session handle */ | |
| 505 BACKEND->cred->refcount++; | |
| 506 DEBUGF(infof(data, | |
| 507 "schannel: incremented credential handle refcount = %d\n", | |
| 508 BACKEND->cred->refcount)); | |
| 509 } | |
| 510 Curl_ssl_sessionid_unlock(conn); | |
| 511 } | |
| 512 | |
| 513 if(!BACKEND->cred) { | |
| 514 /* setup Schannel API options */ | |
| 515 memset(&schannel_cred, 0, sizeof(schannel_cred)); | |
| 516 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; | |
| 517 | |
| 518 if(conn->ssl_config.verifypeer) { | |
| 519 #ifdef HAS_MANUAL_VERIFY_API | |
| 520 if(BACKEND->use_manual_cred_validation) | |
| 521 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION; | |
| 522 else | |
| 523 #endif | |
| 524 schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION; | |
| 525 | |
| 526 if(data->set.ssl.no_revoke) { | |
| 527 schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK | | |
| 528 SCH_CRED_IGNORE_REVOCATION_OFFLINE; | |
| 529 | |
| 530 DEBUGF(infof(data, "schannel: disabled server certificate revocation " | |
| 531 "checks\n")); | |
| 532 } | |
| 533 else { | |
| 534 schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN; | |
| 535 DEBUGF(infof(data, | |
| 536 "schannel: checking server certificate revocation\n")); | |
| 537 } | |
| 538 } | |
| 539 else { | |
| 540 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | | |
| 541 SCH_CRED_IGNORE_NO_REVOCATION_CHECK | | |
| 542 SCH_CRED_IGNORE_REVOCATION_OFFLINE; | |
| 543 DEBUGF(infof(data, | |
| 544 "schannel: disabled server cert revocation checks\n")); | |
| 545 } | |
| 546 | |
| 547 if(!conn->ssl_config.verifyhost) { | |
| 548 schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; | |
| 549 DEBUGF(infof(data, "schannel: verifyhost setting prevents Schannel from " | |
| 550 "comparing the supplied target name with the subject " | |
| 551 "names in server certificates.\n")); | |
| 552 } | |
| 553 | |
| 554 switch(conn->ssl_config.version) { | |
| 555 case CURL_SSLVERSION_DEFAULT: | |
| 556 case CURL_SSLVERSION_TLSv1: | |
| 557 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | | |
| 558 SP_PROT_TLS1_1_CLIENT | | |
| 559 SP_PROT_TLS1_2_CLIENT; | |
| 560 break; | |
| 561 case CURL_SSLVERSION_TLSv1_0: | |
| 562 case CURL_SSLVERSION_TLSv1_1: | |
| 563 case CURL_SSLVERSION_TLSv1_2: | |
| 564 case CURL_SSLVERSION_TLSv1_3: | |
| 565 { | |
| 566 result = set_ssl_version_min_max(&schannel_cred, conn); | |
| 567 if(result != CURLE_OK) | |
| 568 return result; | |
| 569 break; | |
| 570 } | |
| 571 case CURL_SSLVERSION_SSLv3: | |
| 572 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; | |
| 573 break; | |
| 574 case CURL_SSLVERSION_SSLv2: | |
| 575 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT; | |
| 576 break; | |
| 577 default: | |
| 578 failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION"); | |
| 579 return CURLE_SSL_CONNECT_ERROR; | |
| 580 } | |
| 581 | |
| 582 if(SSL_CONN_CONFIG(cipher_list)) { | |
| 583 result = set_ssl_ciphers(&schannel_cred, SSL_CONN_CONFIG(cipher_list)); | |
| 584 if(CURLE_OK != result) { | |
| 585 failf(data, "Unable to set ciphers to passed via SSL_CONN_CONFIG"); | |
| 586 return result; | |
| 587 } | |
| 588 } | |
| 589 | |
| 590 | |
| 591 #ifdef HAS_CLIENT_CERT_PATH | |
| 592 /* client certificate */ | |
| 593 if(data->set.ssl.cert) { | |
| 594 DWORD cert_store_name; | |
| 595 TCHAR *cert_store_path; | |
| 596 TCHAR *cert_thumbprint_str; | |
| 597 CRYPT_HASH_BLOB cert_thumbprint; | |
| 598 BYTE cert_thumbprint_data[CERT_THUMBPRINT_DATA_LEN]; | |
| 599 HCERTSTORE cert_store; | |
| 600 | |
| 601 TCHAR *cert_path = Curl_convert_UTF8_to_tchar(data->set.ssl.cert); | |
| 602 if(!cert_path) | |
| 603 return CURLE_OUT_OF_MEMORY; | |
| 604 | |
| 605 result = get_cert_location(cert_path, &cert_store_name, | |
| 606 &cert_store_path, &cert_thumbprint_str); | |
| 607 if(result != CURLE_OK) { | |
| 608 failf(data, "schannel: Failed to get certificate location for %s", | |
| 609 cert_path); | |
| 610 Curl_unicodefree(cert_path); | |
| 611 return result; | |
| 612 } | |
| 613 | |
| 614 cert_store = | |
| 615 CertOpenStore(CURL_CERT_STORE_PROV_SYSTEM, 0, | |
| 616 (HCRYPTPROV)NULL, | |
| 617 CERT_STORE_OPEN_EXISTING_FLAG | cert_store_name, | |
| 618 cert_store_path); | |
| 619 if(!cert_store) { | |
| 620 failf(data, "schannel: Failed to open cert store %x %s, " | |
| 621 "last error is %x", | |
| 622 cert_store_name, cert_store_path, GetLastError()); | |
| 623 free(cert_store_path); | |
| 624 Curl_unicodefree(cert_path); | |
| 625 return CURLE_SSL_CERTPROBLEM; | |
| 626 } | |
| 627 free(cert_store_path); | |
| 628 | |
| 629 cert_thumbprint.pbData = cert_thumbprint_data; | |
| 630 cert_thumbprint.cbData = CERT_THUMBPRINT_DATA_LEN; | |
| 631 | |
| 632 if(!CryptStringToBinary(cert_thumbprint_str, CERT_THUMBPRINT_STR_LEN, | |
| 633 CRYPT_STRING_HEX, | |
| 634 cert_thumbprint_data, &cert_thumbprint.cbData, | |
| 635 NULL, NULL)) { | |
| 636 Curl_unicodefree(cert_path); | |
| 637 return CURLE_SSL_CERTPROBLEM; | |
| 638 } | |
| 639 | |
| 640 client_certs[0] = CertFindCertificateInStore( | |
| 641 cert_store, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, | |
| 642 CERT_FIND_HASH, &cert_thumbprint, NULL); | |
| 643 | |
| 644 Curl_unicodefree(cert_path); | |
| 645 | |
| 646 if(client_certs[0]) { | |
| 647 schannel_cred.cCreds = 1; | |
| 648 schannel_cred.paCred = client_certs; | |
| 649 } | |
| 650 else { | |
| 651 /* CRYPT_E_NOT_FOUND / E_INVALIDARG */ | |
| 652 return CURLE_SSL_CERTPROBLEM; | |
| 653 } | |
| 654 | |
| 655 CertCloseStore(cert_store, 0); | |
| 656 } | |
| 657 #else | |
| 658 if(data->set.ssl.cert) { | |
| 659 failf(data, "schannel: client cert support not built in"); | |
| 660 return CURLE_NOT_BUILT_IN; | |
| 661 } | |
| 662 #endif | |
| 663 | |
| 664 /* allocate memory for the re-usable credential handle */ | |
| 665 BACKEND->cred = (struct curl_schannel_cred *) | |
| 666 calloc(1, sizeof(struct curl_schannel_cred)); | |
| 667 if(!BACKEND->cred) { | |
| 668 failf(data, "schannel: unable to allocate memory"); | |
| 669 | |
| 670 if(client_certs[0]) | |
| 671 CertFreeCertificateContext(client_certs[0]); | |
| 672 | |
| 673 return CURLE_OUT_OF_MEMORY; | |
| 674 } | |
| 675 BACKEND->cred->refcount = 1; | |
| 676 | |
| 677 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx | |
| 678 */ | |
| 679 sspi_status = | |
| 680 s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, | |
| 681 SECPKG_CRED_OUTBOUND, NULL, | |
| 682 &schannel_cred, NULL, NULL, | |
| 683 &BACKEND->cred->cred_handle, | |
| 684 &BACKEND->cred->time_stamp); | |
| 685 | |
| 686 if(client_certs[0]) | |
| 687 CertFreeCertificateContext(client_certs[0]); | |
| 688 | |
| 689 if(sspi_status != SEC_E_OK) { | |
| 690 char buffer[STRERROR_LEN]; | |
| 691 failf(data, "schannel: AcquireCredentialsHandle failed: %s", | |
| 692 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 693 Curl_safefree(BACKEND->cred); | |
| 694 switch(sspi_status) { | |
| 695 case SEC_E_INSUFFICIENT_MEMORY: | |
| 696 return CURLE_OUT_OF_MEMORY; | |
| 697 case SEC_E_NO_CREDENTIALS: | |
| 698 case SEC_E_SECPKG_NOT_FOUND: | |
| 699 case SEC_E_NOT_OWNER: | |
| 700 case SEC_E_UNKNOWN_CREDENTIALS: | |
| 701 case SEC_E_INTERNAL_ERROR: | |
| 702 default: | |
| 703 return CURLE_SSL_CONNECT_ERROR; | |
| 704 } | |
| 705 } | |
| 706 } | |
| 707 | |
| 708 /* Warn if SNI is disabled due to use of an IP address */ | |
| 709 if(Curl_inet_pton(AF_INET, hostname, &addr) | |
| 710 #ifdef ENABLE_IPV6 | |
| 711 || Curl_inet_pton(AF_INET6, hostname, &addr6) | |
| 712 #endif | |
| 713 ) { | |
| 714 infof(data, "schannel: using IP address, SNI is not supported by OS.\n"); | |
| 715 } | |
| 716 | |
| 717 #ifdef HAS_ALPN | |
| 718 if(BACKEND->use_alpn) { | |
| 719 int cur = 0; | |
| 720 int list_start_index = 0; | |
| 721 unsigned int *extension_len = NULL; | |
| 722 unsigned short* list_len = NULL; | |
| 723 | |
| 724 /* The first four bytes will be an unsigned int indicating number | |
| 725 of bytes of data in the rest of the the buffer. */ | |
| 726 extension_len = (unsigned int *)(&alpn_buffer[cur]); | |
| 727 cur += sizeof(unsigned int); | |
| 728 | |
| 729 /* The next four bytes are an indicator that this buffer will contain | |
| 730 ALPN data, as opposed to NPN, for example. */ | |
| 731 *(unsigned int *)&alpn_buffer[cur] = | |
| 732 SecApplicationProtocolNegotiationExt_ALPN; | |
| 733 cur += sizeof(unsigned int); | |
| 734 | |
| 735 /* The next two bytes will be an unsigned short indicating the number | |
| 736 of bytes used to list the preferred protocols. */ | |
| 737 list_len = (unsigned short*)(&alpn_buffer[cur]); | |
| 738 cur += sizeof(unsigned short); | |
| 739 | |
| 740 list_start_index = cur; | |
| 741 | |
| 742 #ifdef USE_NGHTTP2 | |
| 743 if(data->set.httpversion >= CURL_HTTP_VERSION_2) { | |
| 744 memcpy(&alpn_buffer[cur], NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN); | |
| 745 cur += NGHTTP2_PROTO_ALPN_LEN; | |
| 746 infof(data, "schannel: ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID); | |
| 747 } | |
| 748 #endif | |
| 749 | |
| 750 alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH; | |
| 751 memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH); | |
| 752 cur += ALPN_HTTP_1_1_LENGTH; | |
| 753 infof(data, "schannel: ALPN, offering %s\n", ALPN_HTTP_1_1); | |
| 754 | |
| 755 *list_len = curlx_uitous(cur - list_start_index); | |
| 756 *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short); | |
| 757 | |
| 758 InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur); | |
| 759 InitSecBufferDesc(&inbuf_desc, &inbuf, 1); | |
| 760 } | |
| 761 else { | |
| 762 InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); | |
| 763 InitSecBufferDesc(&inbuf_desc, &inbuf, 1); | |
| 764 } | |
| 765 #else /* HAS_ALPN */ | |
| 766 InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0); | |
| 767 InitSecBufferDesc(&inbuf_desc, &inbuf, 1); | |
| 768 #endif | |
| 769 | |
| 770 /* setup output buffer */ | |
| 771 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); | |
| 772 InitSecBufferDesc(&outbuf_desc, &outbuf, 1); | |
| 773 | |
| 774 /* setup request flags */ | |
| 775 BACKEND->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | | |
| 776 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | | |
| 777 ISC_REQ_STREAM; | |
| 778 | |
| 779 /* allocate memory for the security context handle */ | |
| 780 BACKEND->ctxt = (struct curl_schannel_ctxt *) | |
| 781 calloc(1, sizeof(struct curl_schannel_ctxt)); | |
| 782 if(!BACKEND->ctxt) { | |
| 783 failf(data, "schannel: unable to allocate memory"); | |
| 784 return CURLE_OUT_OF_MEMORY; | |
| 785 } | |
| 786 | |
| 787 host_name = Curl_convert_UTF8_to_tchar(hostname); | |
| 788 if(!host_name) | |
| 789 return CURLE_OUT_OF_MEMORY; | |
| 790 | |
| 791 /* Schannel InitializeSecurityContext: | |
| 792 https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx | |
| 793 | |
| 794 At the moment we don't pass inbuf unless we're using ALPN since we only | |
| 795 use it for that, and Wine (for which we currently disable ALPN) is giving | |
| 796 us problems with inbuf regardless. https://github.com/curl/curl/issues/983 | |
| 797 */ | |
| 798 sspi_status = s_pSecFn->InitializeSecurityContext( | |
| 799 &BACKEND->cred->cred_handle, NULL, host_name, BACKEND->req_flags, 0, 0, | |
| 800 (BACKEND->use_alpn ? &inbuf_desc : NULL), | |
| 801 0, &BACKEND->ctxt->ctxt_handle, | |
| 802 &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); | |
| 803 | |
| 804 Curl_unicodefree(host_name); | |
| 805 | |
| 806 if(sspi_status != SEC_I_CONTINUE_NEEDED) { | |
| 807 char buffer[STRERROR_LEN]; | |
| 808 Curl_safefree(BACKEND->ctxt); | |
| 809 switch(sspi_status) { | |
| 810 case SEC_E_INSUFFICIENT_MEMORY: | |
| 811 failf(data, "schannel: initial InitializeSecurityContext failed: %s", | |
| 812 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 813 return CURLE_OUT_OF_MEMORY; | |
| 814 case SEC_E_WRONG_PRINCIPAL: | |
| 815 failf(data, "schannel: SNI or certificate check failed: %s", | |
| 816 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 817 return CURLE_PEER_FAILED_VERIFICATION; | |
| 818 /* | |
| 819 case SEC_E_INVALID_HANDLE: | |
| 820 case SEC_E_INVALID_TOKEN: | |
| 821 case SEC_E_LOGON_DENIED: | |
| 822 case SEC_E_TARGET_UNKNOWN: | |
| 823 case SEC_E_NO_AUTHENTICATING_AUTHORITY: | |
| 824 case SEC_E_INTERNAL_ERROR: | |
| 825 case SEC_E_NO_CREDENTIALS: | |
| 826 case SEC_E_UNSUPPORTED_FUNCTION: | |
| 827 case SEC_E_APPLICATION_PROTOCOL_MISMATCH: | |
| 828 */ | |
| 829 default: | |
| 830 failf(data, "schannel: initial InitializeSecurityContext failed: %s", | |
| 831 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 832 return CURLE_SSL_CONNECT_ERROR; | |
| 833 } | |
| 834 } | |
| 835 | |
| 836 DEBUGF(infof(data, "schannel: sending initial handshake data: " | |
| 837 "sending %lu bytes...\n", outbuf.cbBuffer)); | |
| 838 | |
| 839 /* send initial handshake data which is now stored in output buffer */ | |
| 840 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, | |
| 841 outbuf.cbBuffer, &written); | |
| 842 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); | |
| 843 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { | |
| 844 failf(data, "schannel: failed to send initial handshake data: " | |
| 845 "sent %zd of %lu bytes", written, outbuf.cbBuffer); | |
| 846 return CURLE_SSL_CONNECT_ERROR; | |
| 847 } | |
| 848 | |
| 849 DEBUGF(infof(data, "schannel: sent initial handshake data: " | |
| 850 "sent %zd bytes\n", written)); | |
| 851 | |
| 852 BACKEND->recv_unrecoverable_err = CURLE_OK; | |
| 853 BACKEND->recv_sspi_close_notify = false; | |
| 854 BACKEND->recv_connection_closed = false; | |
| 855 BACKEND->encdata_is_incomplete = false; | |
| 856 | |
| 857 /* continue to second handshake step */ | |
| 858 connssl->connecting_state = ssl_connect_2; | |
| 859 | |
| 860 return CURLE_OK; | |
| 861 } | |
| 862 | |
| 863 static CURLcode | |
| 864 schannel_connect_step2(struct connectdata *conn, int sockindex) | |
| 865 { | |
| 866 int i; | |
| 867 ssize_t nread = -1, written = -1; | |
| 868 struct Curl_easy *data = conn->data; | |
| 869 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 870 unsigned char *reallocated_buffer; | |
| 871 SecBuffer outbuf[3]; | |
| 872 SecBufferDesc outbuf_desc; | |
| 873 SecBuffer inbuf[2]; | |
| 874 SecBufferDesc inbuf_desc; | |
| 875 SECURITY_STATUS sspi_status = SEC_E_OK; | |
| 876 CURLcode result; | |
| 877 bool doread; | |
| 878 char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : | |
| 879 conn->host.name; | |
| 880 const char *pubkey_ptr; | |
| 881 | |
| 882 doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; | |
| 883 | |
| 884 DEBUGF(infof(data, | |
| 885 "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n", | |
| 886 hostname, conn->remote_port)); | |
| 887 | |
| 888 if(!BACKEND->cred || !BACKEND->ctxt) | |
| 889 return CURLE_SSL_CONNECT_ERROR; | |
| 890 | |
| 891 /* buffer to store previously received and decrypted data */ | |
| 892 if(BACKEND->decdata_buffer == NULL) { | |
| 893 BACKEND->decdata_offset = 0; | |
| 894 BACKEND->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; | |
| 895 BACKEND->decdata_buffer = malloc(BACKEND->decdata_length); | |
| 896 if(BACKEND->decdata_buffer == NULL) { | |
| 897 failf(data, "schannel: unable to allocate memory"); | |
| 898 return CURLE_OUT_OF_MEMORY; | |
| 899 } | |
| 900 } | |
| 901 | |
| 902 /* buffer to store previously received and encrypted data */ | |
| 903 if(BACKEND->encdata_buffer == NULL) { | |
| 904 BACKEND->encdata_is_incomplete = false; | |
| 905 BACKEND->encdata_offset = 0; | |
| 906 BACKEND->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; | |
| 907 BACKEND->encdata_buffer = malloc(BACKEND->encdata_length); | |
| 908 if(BACKEND->encdata_buffer == NULL) { | |
| 909 failf(data, "schannel: unable to allocate memory"); | |
| 910 return CURLE_OUT_OF_MEMORY; | |
| 911 } | |
| 912 } | |
| 913 | |
| 914 /* if we need a bigger buffer to read a full message, increase buffer now */ | |
| 915 if(BACKEND->encdata_length - BACKEND->encdata_offset < | |
| 916 CURL_SCHANNEL_BUFFER_FREE_SIZE) { | |
| 917 /* increase internal encrypted data buffer */ | |
| 918 size_t reallocated_length = BACKEND->encdata_offset + | |
| 919 CURL_SCHANNEL_BUFFER_FREE_SIZE; | |
| 920 reallocated_buffer = realloc(BACKEND->encdata_buffer, | |
| 921 reallocated_length); | |
| 922 | |
| 923 if(reallocated_buffer == NULL) { | |
| 924 failf(data, "schannel: unable to re-allocate memory"); | |
| 925 return CURLE_OUT_OF_MEMORY; | |
| 926 } | |
| 927 else { | |
| 928 BACKEND->encdata_buffer = reallocated_buffer; | |
| 929 BACKEND->encdata_length = reallocated_length; | |
| 930 } | |
| 931 } | |
| 932 | |
| 933 for(;;) { | |
| 934 TCHAR *host_name; | |
| 935 if(doread) { | |
| 936 /* read encrypted handshake data from socket */ | |
| 937 result = Curl_read_plain(conn->sock[sockindex], | |
| 938 (char *) (BACKEND->encdata_buffer + | |
| 939 BACKEND->encdata_offset), | |
| 940 BACKEND->encdata_length - | |
| 941 BACKEND->encdata_offset, | |
| 942 &nread); | |
| 943 if(result == CURLE_AGAIN) { | |
| 944 if(connssl->connecting_state != ssl_connect_2_writing) | |
| 945 connssl->connecting_state = ssl_connect_2_reading; | |
| 946 DEBUGF(infof(data, "schannel: failed to receive handshake, " | |
| 947 "need more data\n")); | |
| 948 return CURLE_OK; | |
| 949 } | |
| 950 else if((result != CURLE_OK) || (nread == 0)) { | |
| 951 failf(data, "schannel: failed to receive handshake, " | |
| 952 "SSL/TLS connection failed"); | |
| 953 return CURLE_SSL_CONNECT_ERROR; | |
| 954 } | |
| 955 | |
| 956 /* increase encrypted data buffer offset */ | |
| 957 BACKEND->encdata_offset += nread; | |
| 958 BACKEND->encdata_is_incomplete = false; | |
| 959 DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread)); | |
| 960 } | |
| 961 | |
| 962 DEBUGF(infof(data, | |
| 963 "schannel: encrypted data buffer: offset %zu length %zu\n", | |
| 964 BACKEND->encdata_offset, BACKEND->encdata_length)); | |
| 965 | |
| 966 /* setup input buffers */ | |
| 967 InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(BACKEND->encdata_offset), | |
| 968 curlx_uztoul(BACKEND->encdata_offset)); | |
| 969 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); | |
| 970 InitSecBufferDesc(&inbuf_desc, inbuf, 2); | |
| 971 | |
| 972 /* setup output buffers */ | |
| 973 InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0); | |
| 974 InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0); | |
| 975 InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0); | |
| 976 InitSecBufferDesc(&outbuf_desc, outbuf, 3); | |
| 977 | |
| 978 if(inbuf[0].pvBuffer == NULL) { | |
| 979 failf(data, "schannel: unable to allocate memory"); | |
| 980 return CURLE_OUT_OF_MEMORY; | |
| 981 } | |
| 982 | |
| 983 /* copy received handshake data into input buffer */ | |
| 984 memcpy(inbuf[0].pvBuffer, BACKEND->encdata_buffer, | |
| 985 BACKEND->encdata_offset); | |
| 986 | |
| 987 host_name = Curl_convert_UTF8_to_tchar(hostname); | |
| 988 if(!host_name) | |
| 989 return CURLE_OUT_OF_MEMORY; | |
| 990 | |
| 991 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx | |
| 992 */ | |
| 993 sspi_status = s_pSecFn->InitializeSecurityContext( | |
| 994 &BACKEND->cred->cred_handle, &BACKEND->ctxt->ctxt_handle, | |
| 995 host_name, BACKEND->req_flags, 0, 0, &inbuf_desc, 0, NULL, | |
| 996 &outbuf_desc, &BACKEND->ret_flags, &BACKEND->ctxt->time_stamp); | |
| 997 | |
| 998 Curl_unicodefree(host_name); | |
| 999 | |
| 1000 /* free buffer for received handshake data */ | |
| 1001 Curl_safefree(inbuf[0].pvBuffer); | |
| 1002 | |
| 1003 /* check if the handshake was incomplete */ | |
| 1004 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { | |
| 1005 BACKEND->encdata_is_incomplete = true; | |
| 1006 connssl->connecting_state = ssl_connect_2_reading; | |
| 1007 DEBUGF(infof(data, | |
| 1008 "schannel: received incomplete message, need more data\n")); | |
| 1009 return CURLE_OK; | |
| 1010 } | |
| 1011 | |
| 1012 /* If the server has requested a client certificate, attempt to continue | |
| 1013 the handshake without one. This will allow connections to servers which | |
| 1014 request a client certificate but do not require it. */ | |
| 1015 if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS && | |
| 1016 !(BACKEND->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) { | |
| 1017 BACKEND->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS; | |
| 1018 connssl->connecting_state = ssl_connect_2_writing; | |
| 1019 DEBUGF(infof(data, | |
| 1020 "schannel: a client certificate has been requested\n")); | |
| 1021 return CURLE_OK; | |
| 1022 } | |
| 1023 | |
| 1024 /* check if the handshake needs to be continued */ | |
| 1025 if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { | |
| 1026 for(i = 0; i < 3; i++) { | |
| 1027 /* search for handshake tokens that need to be send */ | |
| 1028 if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { | |
| 1029 DEBUGF(infof(data, "schannel: sending next handshake data: " | |
| 1030 "sending %lu bytes...\n", outbuf[i].cbBuffer)); | |
| 1031 | |
| 1032 /* send handshake token to server */ | |
| 1033 result = Curl_write_plain(conn, conn->sock[sockindex], | |
| 1034 outbuf[i].pvBuffer, outbuf[i].cbBuffer, | |
| 1035 &written); | |
| 1036 if((result != CURLE_OK) || | |
| 1037 (outbuf[i].cbBuffer != (size_t) written)) { | |
| 1038 failf(data, "schannel: failed to send next handshake data: " | |
| 1039 "sent %zd of %lu bytes", written, outbuf[i].cbBuffer); | |
| 1040 return CURLE_SSL_CONNECT_ERROR; | |
| 1041 } | |
| 1042 } | |
| 1043 | |
| 1044 /* free obsolete buffer */ | |
| 1045 if(outbuf[i].pvBuffer != NULL) { | |
| 1046 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); | |
| 1047 } | |
| 1048 } | |
| 1049 } | |
| 1050 else { | |
| 1051 char buffer[STRERROR_LEN]; | |
| 1052 switch(sspi_status) { | |
| 1053 case SEC_E_INSUFFICIENT_MEMORY: | |
| 1054 failf(data, "schannel: next InitializeSecurityContext failed: %s", | |
| 1055 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 1056 return CURLE_OUT_OF_MEMORY; | |
| 1057 case SEC_E_WRONG_PRINCIPAL: | |
| 1058 failf(data, "schannel: SNI or certificate check failed: %s", | |
| 1059 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 1060 return CURLE_PEER_FAILED_VERIFICATION; | |
| 1061 /* | |
| 1062 case SEC_E_INVALID_HANDLE: | |
| 1063 case SEC_E_INVALID_TOKEN: | |
| 1064 case SEC_E_LOGON_DENIED: | |
| 1065 case SEC_E_TARGET_UNKNOWN: | |
| 1066 case SEC_E_NO_AUTHENTICATING_AUTHORITY: | |
| 1067 case SEC_E_INTERNAL_ERROR: | |
| 1068 case SEC_E_NO_CREDENTIALS: | |
| 1069 case SEC_E_UNSUPPORTED_FUNCTION: | |
| 1070 case SEC_E_APPLICATION_PROTOCOL_MISMATCH: | |
| 1071 */ | |
| 1072 default: | |
| 1073 failf(data, "schannel: next InitializeSecurityContext failed: %s", | |
| 1074 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 1075 return CURLE_SSL_CONNECT_ERROR; | |
| 1076 } | |
| 1077 } | |
| 1078 | |
| 1079 /* check if there was additional remaining encrypted data */ | |
| 1080 if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { | |
| 1081 DEBUGF(infof(data, "schannel: encrypted data length: %lu\n", | |
| 1082 inbuf[1].cbBuffer)); | |
| 1083 /* | |
| 1084 There are two cases where we could be getting extra data here: | |
| 1085 1) If we're renegotiating a connection and the handshake is already | |
| 1086 complete (from the server perspective), it can encrypted app data | |
| 1087 (not handshake data) in an extra buffer at this point. | |
| 1088 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a | |
| 1089 connection and this extra data is part of the handshake. | |
| 1090 We should process the data immediately; waiting for the socket to | |
| 1091 be ready may fail since the server is done sending handshake data. | |
| 1092 */ | |
| 1093 /* check if the remaining data is less than the total amount | |
| 1094 and therefore begins after the already processed data */ | |
| 1095 if(BACKEND->encdata_offset > inbuf[1].cbBuffer) { | |
| 1096 memmove(BACKEND->encdata_buffer, | |
| 1097 (BACKEND->encdata_buffer + BACKEND->encdata_offset) - | |
| 1098 inbuf[1].cbBuffer, inbuf[1].cbBuffer); | |
| 1099 BACKEND->encdata_offset = inbuf[1].cbBuffer; | |
| 1100 if(sspi_status == SEC_I_CONTINUE_NEEDED) { | |
| 1101 doread = FALSE; | |
| 1102 continue; | |
| 1103 } | |
| 1104 } | |
| 1105 } | |
| 1106 else { | |
| 1107 BACKEND->encdata_offset = 0; | |
| 1108 } | |
| 1109 break; | |
| 1110 } | |
| 1111 | |
| 1112 /* check if the handshake needs to be continued */ | |
| 1113 if(sspi_status == SEC_I_CONTINUE_NEEDED) { | |
| 1114 connssl->connecting_state = ssl_connect_2_reading; | |
| 1115 return CURLE_OK; | |
| 1116 } | |
| 1117 | |
| 1118 /* check if the handshake is complete */ | |
| 1119 if(sspi_status == SEC_E_OK) { | |
| 1120 connssl->connecting_state = ssl_connect_3; | |
| 1121 DEBUGF(infof(data, "schannel: SSL/TLS handshake complete\n")); | |
| 1122 } | |
| 1123 | |
| 1124 pubkey_ptr = SSL_IS_PROXY() ? | |
| 1125 data->set.str[STRING_SSL_PINNEDPUBLICKEY_PROXY] : | |
| 1126 data->set.str[STRING_SSL_PINNEDPUBLICKEY_ORIG]; | |
| 1127 if(pubkey_ptr) { | |
| 1128 result = pkp_pin_peer_pubkey(conn, sockindex, pubkey_ptr); | |
| 1129 if(result) { | |
| 1130 failf(data, "SSL: public key does not match pinned public key!"); | |
| 1131 return result; | |
| 1132 } | |
| 1133 } | |
| 1134 | |
| 1135 #ifdef HAS_MANUAL_VERIFY_API | |
| 1136 if(conn->ssl_config.verifypeer && BACKEND->use_manual_cred_validation) { | |
| 1137 return Curl_verify_certificate(conn, sockindex); | |
| 1138 } | |
| 1139 #endif | |
| 1140 | |
| 1141 return CURLE_OK; | |
| 1142 } | |
| 1143 | |
| 1144 static bool | |
| 1145 valid_cert_encoding(const CERT_CONTEXT *cert_context) | |
| 1146 { | |
| 1147 return (cert_context != NULL) && | |
| 1148 ((cert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) && | |
| 1149 (cert_context->pbCertEncoded != NULL) && | |
| 1150 (cert_context->cbCertEncoded > 0); | |
| 1151 } | |
| 1152 | |
| 1153 typedef bool(*Read_crt_func)(const CERT_CONTEXT *ccert_context, void *arg); | |
| 1154 | |
| 1155 static void | |
| 1156 traverse_cert_store(const CERT_CONTEXT *context, Read_crt_func func, | |
| 1157 void *arg) | |
| 1158 { | |
| 1159 const CERT_CONTEXT *current_context = NULL; | |
| 1160 bool should_continue = true; | |
| 1161 while(should_continue && | |
| 1162 (current_context = CertEnumCertificatesInStore( | |
| 1163 context->hCertStore, | |
| 1164 current_context)) != NULL) | |
| 1165 should_continue = func(current_context, arg); | |
| 1166 | |
| 1167 if(current_context) | |
| 1168 CertFreeCertificateContext(current_context); | |
| 1169 } | |
| 1170 | |
| 1171 static bool | |
| 1172 cert_counter_callback(const CERT_CONTEXT *ccert_context, void *certs_count) | |
| 1173 { | |
| 1174 if(valid_cert_encoding(ccert_context)) | |
| 1175 (*(int *)certs_count)++; | |
| 1176 return true; | |
| 1177 } | |
| 1178 | |
| 1179 struct Adder_args | |
| 1180 { | |
| 1181 struct connectdata *conn; | |
| 1182 CURLcode result; | |
| 1183 int idx; | |
| 1184 }; | |
| 1185 | |
| 1186 static bool | |
| 1187 add_cert_to_certinfo(const CERT_CONTEXT *ccert_context, void *raw_arg) | |
| 1188 { | |
| 1189 struct Adder_args *args = (struct Adder_args*)raw_arg; | |
| 1190 args->result = CURLE_OK; | |
| 1191 if(valid_cert_encoding(ccert_context)) { | |
| 1192 const char *beg = (const char *) ccert_context->pbCertEncoded; | |
| 1193 const char *end = beg + ccert_context->cbCertEncoded; | |
| 1194 args->result = Curl_extract_certinfo(args->conn, (args->idx)++, beg, end); | |
| 1195 } | |
| 1196 return args->result == CURLE_OK; | |
| 1197 } | |
| 1198 | |
| 1199 static CURLcode | |
| 1200 schannel_connect_step3(struct connectdata *conn, int sockindex) | |
| 1201 { | |
| 1202 CURLcode result = CURLE_OK; | |
| 1203 struct Curl_easy *data = conn->data; | |
| 1204 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1205 SECURITY_STATUS sspi_status = SEC_E_OK; | |
| 1206 CERT_CONTEXT *ccert_context = NULL; | |
| 1207 #ifdef DEBUGBUILD | |
| 1208 const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : | |
| 1209 conn->host.name; | |
| 1210 #endif | |
| 1211 #ifdef HAS_ALPN | |
| 1212 SecPkgContext_ApplicationProtocol alpn_result; | |
| 1213 #endif | |
| 1214 | |
| 1215 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); | |
| 1216 | |
| 1217 DEBUGF(infof(data, | |
| 1218 "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n", | |
| 1219 hostname, conn->remote_port)); | |
| 1220 | |
| 1221 if(!BACKEND->cred) | |
| 1222 return CURLE_SSL_CONNECT_ERROR; | |
| 1223 | |
| 1224 /* check if the required context attributes are met */ | |
| 1225 if(BACKEND->ret_flags != BACKEND->req_flags) { | |
| 1226 if(!(BACKEND->ret_flags & ISC_RET_SEQUENCE_DETECT)) | |
| 1227 failf(data, "schannel: failed to setup sequence detection"); | |
| 1228 if(!(BACKEND->ret_flags & ISC_RET_REPLAY_DETECT)) | |
| 1229 failf(data, "schannel: failed to setup replay detection"); | |
| 1230 if(!(BACKEND->ret_flags & ISC_RET_CONFIDENTIALITY)) | |
| 1231 failf(data, "schannel: failed to setup confidentiality"); | |
| 1232 if(!(BACKEND->ret_flags & ISC_RET_ALLOCATED_MEMORY)) | |
| 1233 failf(data, "schannel: failed to setup memory allocation"); | |
| 1234 if(!(BACKEND->ret_flags & ISC_RET_STREAM)) | |
| 1235 failf(data, "schannel: failed to setup stream orientation"); | |
| 1236 return CURLE_SSL_CONNECT_ERROR; | |
| 1237 } | |
| 1238 | |
| 1239 #ifdef HAS_ALPN | |
| 1240 if(BACKEND->use_alpn) { | |
| 1241 sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, | |
| 1242 SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result); | |
| 1243 | |
| 1244 if(sspi_status != SEC_E_OK) { | |
| 1245 failf(data, "schannel: failed to retrieve ALPN result"); | |
| 1246 return CURLE_SSL_CONNECT_ERROR; | |
| 1247 } | |
| 1248 | |
| 1249 if(alpn_result.ProtoNegoStatus == | |
| 1250 SecApplicationProtocolNegotiationStatus_Success) { | |
| 1251 | |
| 1252 infof(data, "schannel: ALPN, server accepted to use %.*s\n", | |
| 1253 alpn_result.ProtocolIdSize, alpn_result.ProtocolId); | |
| 1254 | |
| 1255 #ifdef USE_NGHTTP2 | |
| 1256 if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN && | |
| 1257 !memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId, | |
| 1258 NGHTTP2_PROTO_VERSION_ID_LEN)) { | |
| 1259 conn->negnpn = CURL_HTTP_VERSION_2; | |
| 1260 } | |
| 1261 else | |
| 1262 #endif | |
| 1263 if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH && | |
| 1264 !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId, | |
| 1265 ALPN_HTTP_1_1_LENGTH)) { | |
| 1266 conn->negnpn = CURL_HTTP_VERSION_1_1; | |
| 1267 } | |
| 1268 } | |
| 1269 else | |
| 1270 infof(data, "ALPN, server did not agree to a protocol\n"); | |
| 1271 Curl_multiuse_state(conn, conn->negnpn == CURL_HTTP_VERSION_2 ? | |
| 1272 BUNDLE_MULTIPLEX : BUNDLE_NO_MULTIUSE); | |
| 1273 } | |
| 1274 #endif | |
| 1275 | |
| 1276 /* save the current session data for possible re-use */ | |
| 1277 if(SSL_SET_OPTION(primary.sessionid)) { | |
| 1278 bool incache; | |
| 1279 struct curl_schannel_cred *old_cred = NULL; | |
| 1280 | |
| 1281 Curl_ssl_sessionid_lock(conn); | |
| 1282 incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, | |
| 1283 sockindex)); | |
| 1284 if(incache) { | |
| 1285 if(old_cred != BACKEND->cred) { | |
| 1286 DEBUGF(infof(data, | |
| 1287 "schannel: old credential handle is stale, removing\n")); | |
| 1288 /* we're not taking old_cred ownership here, no refcount++ is needed */ | |
| 1289 Curl_ssl_delsessionid(conn, (void *)old_cred); | |
| 1290 incache = FALSE; | |
| 1291 } | |
| 1292 } | |
| 1293 if(!incache) { | |
| 1294 result = Curl_ssl_addsessionid(conn, (void *)BACKEND->cred, | |
| 1295 sizeof(struct curl_schannel_cred), | |
| 1296 sockindex); | |
| 1297 if(result) { | |
| 1298 Curl_ssl_sessionid_unlock(conn); | |
| 1299 failf(data, "schannel: failed to store credential handle"); | |
| 1300 return result; | |
| 1301 } | |
| 1302 else { | |
| 1303 /* this cred session is now also referenced by sessionid cache */ | |
| 1304 BACKEND->cred->refcount++; | |
| 1305 DEBUGF(infof(data, | |
| 1306 "schannel: stored credential handle in session cache\n")); | |
| 1307 } | |
| 1308 } | |
| 1309 Curl_ssl_sessionid_unlock(conn); | |
| 1310 } | |
| 1311 | |
| 1312 if(data->set.ssl.certinfo) { | |
| 1313 int certs_count = 0; | |
| 1314 sspi_status = s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, | |
| 1315 SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context); | |
| 1316 | |
| 1317 if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) { | |
| 1318 failf(data, "schannel: failed to retrieve remote cert context"); | |
| 1319 return CURLE_PEER_FAILED_VERIFICATION; | |
| 1320 } | |
| 1321 | |
| 1322 traverse_cert_store(ccert_context, cert_counter_callback, &certs_count); | |
| 1323 | |
| 1324 result = Curl_ssl_init_certinfo(data, certs_count); | |
| 1325 if(!result) { | |
| 1326 struct Adder_args args; | |
| 1327 args.conn = conn; | |
| 1328 args.idx = 0; | |
| 1329 traverse_cert_store(ccert_context, add_cert_to_certinfo, &args); | |
| 1330 result = args.result; | |
| 1331 } | |
| 1332 CertFreeCertificateContext(ccert_context); | |
| 1333 if(result) | |
| 1334 return result; | |
| 1335 } | |
| 1336 | |
| 1337 connssl->connecting_state = ssl_connect_done; | |
| 1338 | |
| 1339 return CURLE_OK; | |
| 1340 } | |
| 1341 | |
| 1342 static CURLcode | |
| 1343 schannel_connect_common(struct connectdata *conn, int sockindex, | |
| 1344 bool nonblocking, bool *done) | |
| 1345 { | |
| 1346 CURLcode result; | |
| 1347 struct Curl_easy *data = conn->data; | |
| 1348 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1349 curl_socket_t sockfd = conn->sock[sockindex]; | |
| 1350 time_t timeout_ms; | |
| 1351 int what; | |
| 1352 | |
| 1353 /* check if the connection has already been established */ | |
| 1354 if(ssl_connection_complete == connssl->state) { | |
| 1355 *done = TRUE; | |
| 1356 return CURLE_OK; | |
| 1357 } | |
| 1358 | |
| 1359 if(ssl_connect_1 == connssl->connecting_state) { | |
| 1360 /* check out how much more time we're allowed */ | |
| 1361 timeout_ms = Curl_timeleft(data, NULL, TRUE); | |
| 1362 | |
| 1363 if(timeout_ms < 0) { | |
| 1364 /* no need to continue if time already is up */ | |
| 1365 failf(data, "SSL/TLS connection timeout"); | |
| 1366 return CURLE_OPERATION_TIMEDOUT; | |
| 1367 } | |
| 1368 | |
| 1369 result = schannel_connect_step1(conn, sockindex); | |
| 1370 if(result) | |
| 1371 return result; | |
| 1372 } | |
| 1373 | |
| 1374 while(ssl_connect_2 == connssl->connecting_state || | |
| 1375 ssl_connect_2_reading == connssl->connecting_state || | |
| 1376 ssl_connect_2_writing == connssl->connecting_state) { | |
| 1377 | |
| 1378 /* check out how much more time we're allowed */ | |
| 1379 timeout_ms = Curl_timeleft(data, NULL, TRUE); | |
| 1380 | |
| 1381 if(timeout_ms < 0) { | |
| 1382 /* no need to continue if time already is up */ | |
| 1383 failf(data, "SSL/TLS connection timeout"); | |
| 1384 return CURLE_OPERATION_TIMEDOUT; | |
| 1385 } | |
| 1386 | |
| 1387 /* if ssl is expecting something, check if it's available. */ | |
| 1388 if(connssl->connecting_state == ssl_connect_2_reading | |
| 1389 || connssl->connecting_state == ssl_connect_2_writing) { | |
| 1390 | |
| 1391 curl_socket_t writefd = ssl_connect_2_writing == | |
| 1392 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; | |
| 1393 curl_socket_t readfd = ssl_connect_2_reading == | |
| 1394 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; | |
| 1395 | |
| 1396 what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd, | |
| 1397 nonblocking ? 0 : timeout_ms); | |
| 1398 if(what < 0) { | |
| 1399 /* fatal error */ | |
| 1400 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO); | |
| 1401 return CURLE_SSL_CONNECT_ERROR; | |
| 1402 } | |
| 1403 else if(0 == what) { | |
| 1404 if(nonblocking) { | |
| 1405 *done = FALSE; | |
| 1406 return CURLE_OK; | |
| 1407 } | |
| 1408 else { | |
| 1409 /* timeout */ | |
| 1410 failf(data, "SSL/TLS connection timeout"); | |
| 1411 return CURLE_OPERATION_TIMEDOUT; | |
| 1412 } | |
| 1413 } | |
| 1414 /* socket is readable or writable */ | |
| 1415 } | |
| 1416 | |
| 1417 /* Run transaction, and return to the caller if it failed or if | |
| 1418 * this connection is part of a multi handle and this loop would | |
| 1419 * execute again. This permits the owner of a multi handle to | |
| 1420 * abort a connection attempt before step2 has completed while | |
| 1421 * ensuring that a client using select() or epoll() will always | |
| 1422 * have a valid fdset to wait on. | |
| 1423 */ | |
| 1424 result = schannel_connect_step2(conn, sockindex); | |
| 1425 if(result || (nonblocking && | |
| 1426 (ssl_connect_2 == connssl->connecting_state || | |
| 1427 ssl_connect_2_reading == connssl->connecting_state || | |
| 1428 ssl_connect_2_writing == connssl->connecting_state))) | |
| 1429 return result; | |
| 1430 | |
| 1431 } /* repeat step2 until all transactions are done. */ | |
| 1432 | |
| 1433 if(ssl_connect_3 == connssl->connecting_state) { | |
| 1434 result = schannel_connect_step3(conn, sockindex); | |
| 1435 if(result) | |
| 1436 return result; | |
| 1437 } | |
| 1438 | |
| 1439 if(ssl_connect_done == connssl->connecting_state) { | |
| 1440 connssl->state = ssl_connection_complete; | |
| 1441 conn->recv[sockindex] = schannel_recv; | |
| 1442 conn->send[sockindex] = schannel_send; | |
| 1443 | |
| 1444 #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS | |
| 1445 /* When SSPI is used in combination with Schannel | |
| 1446 * we need the Schannel context to create the Schannel | |
| 1447 * binding to pass the IIS extended protection checks. | |
| 1448 * Available on Windows 7 or later. | |
| 1449 */ | |
| 1450 conn->sslContext = &BACKEND->ctxt->ctxt_handle; | |
| 1451 #endif | |
| 1452 | |
| 1453 *done = TRUE; | |
| 1454 } | |
| 1455 else | |
| 1456 *done = FALSE; | |
| 1457 | |
| 1458 /* reset our connection state machine */ | |
| 1459 connssl->connecting_state = ssl_connect_1; | |
| 1460 | |
| 1461 return CURLE_OK; | |
| 1462 } | |
| 1463 | |
| 1464 static ssize_t | |
| 1465 schannel_send(struct connectdata *conn, int sockindex, | |
| 1466 const void *buf, size_t len, CURLcode *err) | |
| 1467 { | |
| 1468 ssize_t written = -1; | |
| 1469 size_t data_len = 0; | |
| 1470 unsigned char *data = NULL; | |
| 1471 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1472 SecBuffer outbuf[4]; | |
| 1473 SecBufferDesc outbuf_desc; | |
| 1474 SECURITY_STATUS sspi_status = SEC_E_OK; | |
| 1475 CURLcode result; | |
| 1476 | |
| 1477 /* check if the maximum stream sizes were queried */ | |
| 1478 if(BACKEND->stream_sizes.cbMaximumMessage == 0) { | |
| 1479 sspi_status = s_pSecFn->QueryContextAttributes( | |
| 1480 &BACKEND->ctxt->ctxt_handle, | |
| 1481 SECPKG_ATTR_STREAM_SIZES, | |
| 1482 &BACKEND->stream_sizes); | |
| 1483 if(sspi_status != SEC_E_OK) { | |
| 1484 *err = CURLE_SEND_ERROR; | |
| 1485 return -1; | |
| 1486 } | |
| 1487 } | |
| 1488 | |
| 1489 /* check if the buffer is longer than the maximum message length */ | |
| 1490 if(len > BACKEND->stream_sizes.cbMaximumMessage) { | |
| 1491 len = BACKEND->stream_sizes.cbMaximumMessage; | |
| 1492 } | |
| 1493 | |
| 1494 /* calculate the complete message length and allocate a buffer for it */ | |
| 1495 data_len = BACKEND->stream_sizes.cbHeader + len + | |
| 1496 BACKEND->stream_sizes.cbTrailer; | |
| 1497 data = (unsigned char *) malloc(data_len); | |
| 1498 if(data == NULL) { | |
| 1499 *err = CURLE_OUT_OF_MEMORY; | |
| 1500 return -1; | |
| 1501 } | |
| 1502 | |
| 1503 /* setup output buffers (header, data, trailer, empty) */ | |
| 1504 InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER, | |
| 1505 data, BACKEND->stream_sizes.cbHeader); | |
| 1506 InitSecBuffer(&outbuf[1], SECBUFFER_DATA, | |
| 1507 data + BACKEND->stream_sizes.cbHeader, curlx_uztoul(len)); | |
| 1508 InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER, | |
| 1509 data + BACKEND->stream_sizes.cbHeader + len, | |
| 1510 BACKEND->stream_sizes.cbTrailer); | |
| 1511 InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0); | |
| 1512 InitSecBufferDesc(&outbuf_desc, outbuf, 4); | |
| 1513 | |
| 1514 /* copy data into output buffer */ | |
| 1515 memcpy(outbuf[1].pvBuffer, buf, len); | |
| 1516 | |
| 1517 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ | |
| 1518 sspi_status = s_pSecFn->EncryptMessage(&BACKEND->ctxt->ctxt_handle, 0, | |
| 1519 &outbuf_desc, 0); | |
| 1520 | |
| 1521 /* check if the message was encrypted */ | |
| 1522 if(sspi_status == SEC_E_OK) { | |
| 1523 written = 0; | |
| 1524 | |
| 1525 /* send the encrypted message including header, data and trailer */ | |
| 1526 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; | |
| 1527 | |
| 1528 /* | |
| 1529 It's important to send the full message which includes the header, | |
| 1530 encrypted payload, and trailer. Until the client receives all the | |
| 1531 data a coherent message has not been delivered and the client | |
| 1532 can't read any of it. | |
| 1533 | |
| 1534 If we wanted to buffer the unwritten encrypted bytes, we would | |
| 1535 tell the client that all data it has requested to be sent has been | |
| 1536 sent. The unwritten encrypted bytes would be the first bytes to | |
| 1537 send on the next invocation. | |
| 1538 Here's the catch with this - if we tell the client that all the | |
| 1539 bytes have been sent, will the client call this method again to | |
| 1540 send the buffered data? Looking at who calls this function, it | |
| 1541 seems the answer is NO. | |
| 1542 */ | |
| 1543 | |
| 1544 /* send entire message or fail */ | |
| 1545 while(len > (size_t)written) { | |
| 1546 ssize_t this_write; | |
| 1547 time_t timeleft; | |
| 1548 int what; | |
| 1549 | |
| 1550 this_write = 0; | |
| 1551 | |
| 1552 timeleft = Curl_timeleft(conn->data, NULL, FALSE); | |
| 1553 if(timeleft < 0) { | |
| 1554 /* we already got the timeout */ | |
| 1555 failf(conn->data, "schannel: timed out sending data " | |
| 1556 "(bytes sent: %zd)", written); | |
| 1557 *err = CURLE_OPERATION_TIMEDOUT; | |
| 1558 written = -1; | |
| 1559 break; | |
| 1560 } | |
| 1561 | |
| 1562 what = SOCKET_WRITABLE(conn->sock[sockindex], timeleft); | |
| 1563 if(what < 0) { | |
| 1564 /* fatal error */ | |
| 1565 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO); | |
| 1566 *err = CURLE_SEND_ERROR; | |
| 1567 written = -1; | |
| 1568 break; | |
| 1569 } | |
| 1570 else if(0 == what) { | |
| 1571 failf(conn->data, "schannel: timed out sending data " | |
| 1572 "(bytes sent: %zd)", written); | |
| 1573 *err = CURLE_OPERATION_TIMEDOUT; | |
| 1574 written = -1; | |
| 1575 break; | |
| 1576 } | |
| 1577 /* socket is writable */ | |
| 1578 | |
| 1579 result = Curl_write_plain(conn, conn->sock[sockindex], data + written, | |
| 1580 len - written, &this_write); | |
| 1581 if(result == CURLE_AGAIN) | |
| 1582 continue; | |
| 1583 else if(result != CURLE_OK) { | |
| 1584 *err = result; | |
| 1585 written = -1; | |
| 1586 break; | |
| 1587 } | |
| 1588 | |
| 1589 written += this_write; | |
| 1590 } | |
| 1591 } | |
| 1592 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) { | |
| 1593 *err = CURLE_OUT_OF_MEMORY; | |
| 1594 } | |
| 1595 else{ | |
| 1596 *err = CURLE_SEND_ERROR; | |
| 1597 } | |
| 1598 | |
| 1599 Curl_safefree(data); | |
| 1600 | |
| 1601 if(len == (size_t)written) | |
| 1602 /* Encrypted message including header, data and trailer entirely sent. | |
| 1603 The return value is the number of unencrypted bytes that were sent. */ | |
| 1604 written = outbuf[1].cbBuffer; | |
| 1605 | |
| 1606 return written; | |
| 1607 } | |
| 1608 | |
| 1609 static ssize_t | |
| 1610 schannel_recv(struct connectdata *conn, int sockindex, | |
| 1611 char *buf, size_t len, CURLcode *err) | |
| 1612 { | |
| 1613 size_t size = 0; | |
| 1614 ssize_t nread = -1; | |
| 1615 struct Curl_easy *data = conn->data; | |
| 1616 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1617 unsigned char *reallocated_buffer; | |
| 1618 size_t reallocated_length; | |
| 1619 bool done = FALSE; | |
| 1620 SecBuffer inbuf[4]; | |
| 1621 SecBufferDesc inbuf_desc; | |
| 1622 SECURITY_STATUS sspi_status = SEC_E_OK; | |
| 1623 /* we want the length of the encrypted buffer to be at least large enough | |
| 1624 that it can hold all the bytes requested and some TLS record overhead. */ | |
| 1625 size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE; | |
| 1626 | |
| 1627 /**************************************************************************** | |
| 1628 * Don't return or set BACKEND->recv_unrecoverable_err unless in the cleanup. | |
| 1629 * The pattern for return error is set *err, optional infof, goto cleanup. | |
| 1630 * | |
| 1631 * Our priority is to always return as much decrypted data to the caller as | |
| 1632 * possible, even if an error occurs. The state of the decrypted buffer must | |
| 1633 * always be valid. Transfer of decrypted data to the caller's buffer is | |
| 1634 * handled in the cleanup. | |
| 1635 */ | |
| 1636 | |
| 1637 DEBUGF(infof(data, "schannel: client wants to read %zu bytes\n", len)); | |
| 1638 *err = CURLE_OK; | |
| 1639 | |
| 1640 if(len && len <= BACKEND->decdata_offset) { | |
| 1641 infof(data, "schannel: enough decrypted data is already available\n"); | |
| 1642 goto cleanup; | |
| 1643 } | |
| 1644 else if(BACKEND->recv_unrecoverable_err) { | |
| 1645 *err = BACKEND->recv_unrecoverable_err; | |
| 1646 infof(data, "schannel: an unrecoverable error occurred in a prior call\n"); | |
| 1647 goto cleanup; | |
| 1648 } | |
| 1649 else if(BACKEND->recv_sspi_close_notify) { | |
| 1650 /* once a server has indicated shutdown there is no more encrypted data */ | |
| 1651 infof(data, "schannel: server indicated shutdown in a prior call\n"); | |
| 1652 goto cleanup; | |
| 1653 } | |
| 1654 else if(!len) { | |
| 1655 /* It's debatable what to return when !len. Regardless we can't return | |
| 1656 immediately because there may be data to decrypt (in the case we want to | |
| 1657 decrypt all encrypted cached data) so handle !len later in cleanup. | |
| 1658 */ | |
| 1659 ; /* do nothing */ | |
| 1660 } | |
| 1661 else if(!BACKEND->recv_connection_closed) { | |
| 1662 /* increase enc buffer in order to fit the requested amount of data */ | |
| 1663 size = BACKEND->encdata_length - BACKEND->encdata_offset; | |
| 1664 if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE || | |
| 1665 BACKEND->encdata_length < min_encdata_length) { | |
| 1666 reallocated_length = BACKEND->encdata_offset + | |
| 1667 CURL_SCHANNEL_BUFFER_FREE_SIZE; | |
| 1668 if(reallocated_length < min_encdata_length) { | |
| 1669 reallocated_length = min_encdata_length; | |
| 1670 } | |
| 1671 reallocated_buffer = realloc(BACKEND->encdata_buffer, | |
| 1672 reallocated_length); | |
| 1673 if(reallocated_buffer == NULL) { | |
| 1674 *err = CURLE_OUT_OF_MEMORY; | |
| 1675 failf(data, "schannel: unable to re-allocate memory"); | |
| 1676 goto cleanup; | |
| 1677 } | |
| 1678 | |
| 1679 BACKEND->encdata_buffer = reallocated_buffer; | |
| 1680 BACKEND->encdata_length = reallocated_length; | |
| 1681 size = BACKEND->encdata_length - BACKEND->encdata_offset; | |
| 1682 DEBUGF(infof(data, "schannel: encdata_buffer resized %zu\n", | |
| 1683 BACKEND->encdata_length)); | |
| 1684 } | |
| 1685 | |
| 1686 DEBUGF(infof(data, | |
| 1687 "schannel: encrypted data buffer: offset %zu length %zu\n", | |
| 1688 BACKEND->encdata_offset, BACKEND->encdata_length)); | |
| 1689 | |
| 1690 /* read encrypted data from socket */ | |
| 1691 *err = Curl_read_plain(conn->sock[sockindex], | |
| 1692 (char *)(BACKEND->encdata_buffer + | |
| 1693 BACKEND->encdata_offset), | |
| 1694 size, &nread); | |
| 1695 if(*err) { | |
| 1696 nread = -1; | |
| 1697 if(*err == CURLE_AGAIN) | |
| 1698 DEBUGF(infof(data, | |
| 1699 "schannel: Curl_read_plain returned CURLE_AGAIN\n")); | |
| 1700 else if(*err == CURLE_RECV_ERROR) | |
| 1701 infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n"); | |
| 1702 else | |
| 1703 infof(data, "schannel: Curl_read_plain returned error %d\n", *err); | |
| 1704 } | |
| 1705 else if(nread == 0) { | |
| 1706 BACKEND->recv_connection_closed = true; | |
| 1707 DEBUGF(infof(data, "schannel: server closed the connection\n")); | |
| 1708 } | |
| 1709 else if(nread > 0) { | |
| 1710 BACKEND->encdata_offset += (size_t)nread; | |
| 1711 BACKEND->encdata_is_incomplete = false; | |
| 1712 DEBUGF(infof(data, "schannel: encrypted data got %zd\n", nread)); | |
| 1713 } | |
| 1714 } | |
| 1715 | |
| 1716 DEBUGF(infof(data, | |
| 1717 "schannel: encrypted data buffer: offset %zu length %zu\n", | |
| 1718 BACKEND->encdata_offset, BACKEND->encdata_length)); | |
| 1719 | |
| 1720 /* decrypt loop */ | |
| 1721 while(BACKEND->encdata_offset > 0 && sspi_status == SEC_E_OK && | |
| 1722 (!len || BACKEND->decdata_offset < len || | |
| 1723 BACKEND->recv_connection_closed)) { | |
| 1724 /* prepare data buffer for DecryptMessage call */ | |
| 1725 InitSecBuffer(&inbuf[0], SECBUFFER_DATA, BACKEND->encdata_buffer, | |
| 1726 curlx_uztoul(BACKEND->encdata_offset)); | |
| 1727 | |
| 1728 /* we need 3 more empty input buffers for possible output */ | |
| 1729 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); | |
| 1730 InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0); | |
| 1731 InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0); | |
| 1732 InitSecBufferDesc(&inbuf_desc, inbuf, 4); | |
| 1733 | |
| 1734 /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx | |
| 1735 */ | |
| 1736 sspi_status = s_pSecFn->DecryptMessage(&BACKEND->ctxt->ctxt_handle, | |
| 1737 &inbuf_desc, 0, NULL); | |
| 1738 | |
| 1739 /* check if everything went fine (server may want to renegotiate | |
| 1740 or shutdown the connection context) */ | |
| 1741 if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE || | |
| 1742 sspi_status == SEC_I_CONTEXT_EXPIRED) { | |
| 1743 /* check for successfully decrypted data, even before actual | |
| 1744 renegotiation or shutdown of the connection context */ | |
| 1745 if(inbuf[1].BufferType == SECBUFFER_DATA) { | |
| 1746 DEBUGF(infof(data, "schannel: decrypted data length: %lu\n", | |
| 1747 inbuf[1].cbBuffer)); | |
| 1748 | |
| 1749 /* increase buffer in order to fit the received amount of data */ | |
| 1750 size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? | |
| 1751 inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; | |
| 1752 if(BACKEND->decdata_length - BACKEND->decdata_offset < size || | |
| 1753 BACKEND->decdata_length < len) { | |
| 1754 /* increase internal decrypted data buffer */ | |
| 1755 reallocated_length = BACKEND->decdata_offset + size; | |
| 1756 /* make sure that the requested amount of data fits */ | |
| 1757 if(reallocated_length < len) { | |
| 1758 reallocated_length = len; | |
| 1759 } | |
| 1760 reallocated_buffer = realloc(BACKEND->decdata_buffer, | |
| 1761 reallocated_length); | |
| 1762 if(reallocated_buffer == NULL) { | |
| 1763 *err = CURLE_OUT_OF_MEMORY; | |
| 1764 failf(data, "schannel: unable to re-allocate memory"); | |
| 1765 goto cleanup; | |
| 1766 } | |
| 1767 BACKEND->decdata_buffer = reallocated_buffer; | |
| 1768 BACKEND->decdata_length = reallocated_length; | |
| 1769 } | |
| 1770 | |
| 1771 /* copy decrypted data to internal buffer */ | |
| 1772 size = inbuf[1].cbBuffer; | |
| 1773 if(size) { | |
| 1774 memcpy(BACKEND->decdata_buffer + BACKEND->decdata_offset, | |
| 1775 inbuf[1].pvBuffer, size); | |
| 1776 BACKEND->decdata_offset += size; | |
| 1777 } | |
| 1778 | |
| 1779 DEBUGF(infof(data, "schannel: decrypted data added: %zu\n", size)); | |
| 1780 DEBUGF(infof(data, | |
| 1781 "schannel: decrypted cached: offset %zu length %zu\n", | |
| 1782 BACKEND->decdata_offset, BACKEND->decdata_length)); | |
| 1783 } | |
| 1784 | |
| 1785 /* check for remaining encrypted data */ | |
| 1786 if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) { | |
| 1787 DEBUGF(infof(data, "schannel: encrypted data length: %lu\n", | |
| 1788 inbuf[3].cbBuffer)); | |
| 1789 | |
| 1790 /* check if the remaining data is less than the total amount | |
| 1791 * and therefore begins after the already processed data | |
| 1792 */ | |
| 1793 if(BACKEND->encdata_offset > inbuf[3].cbBuffer) { | |
| 1794 /* move remaining encrypted data forward to the beginning of | |
| 1795 buffer */ | |
| 1796 memmove(BACKEND->encdata_buffer, | |
| 1797 (BACKEND->encdata_buffer + BACKEND->encdata_offset) - | |
| 1798 inbuf[3].cbBuffer, inbuf[3].cbBuffer); | |
| 1799 BACKEND->encdata_offset = inbuf[3].cbBuffer; | |
| 1800 } | |
| 1801 | |
| 1802 DEBUGF(infof(data, | |
| 1803 "schannel: encrypted cached: offset %zu length %zu\n", | |
| 1804 BACKEND->encdata_offset, BACKEND->encdata_length)); | |
| 1805 } | |
| 1806 else { | |
| 1807 /* reset encrypted buffer offset, because there is no data remaining */ | |
| 1808 BACKEND->encdata_offset = 0; | |
| 1809 } | |
| 1810 | |
| 1811 /* check if server wants to renegotiate the connection context */ | |
| 1812 if(sspi_status == SEC_I_RENEGOTIATE) { | |
| 1813 infof(data, "schannel: remote party requests renegotiation\n"); | |
| 1814 if(*err && *err != CURLE_AGAIN) { | |
| 1815 infof(data, "schannel: can't renogotiate, an error is pending\n"); | |
| 1816 goto cleanup; | |
| 1817 } | |
| 1818 if(BACKEND->encdata_offset) { | |
| 1819 *err = CURLE_RECV_ERROR; | |
| 1820 infof(data, "schannel: can't renogotiate, " | |
| 1821 "encrypted data available\n"); | |
| 1822 goto cleanup; | |
| 1823 } | |
| 1824 /* begin renegotiation */ | |
| 1825 infof(data, "schannel: renegotiating SSL/TLS connection\n"); | |
| 1826 connssl->state = ssl_connection_negotiating; | |
| 1827 connssl->connecting_state = ssl_connect_2_writing; | |
| 1828 *err = schannel_connect_common(conn, sockindex, FALSE, &done); | |
| 1829 if(*err) { | |
| 1830 infof(data, "schannel: renegotiation failed\n"); | |
| 1831 goto cleanup; | |
| 1832 } | |
| 1833 /* now retry receiving data */ | |
| 1834 sspi_status = SEC_E_OK; | |
| 1835 infof(data, "schannel: SSL/TLS connection renegotiated\n"); | |
| 1836 continue; | |
| 1837 } | |
| 1838 /* check if the server closed the connection */ | |
| 1839 else if(sspi_status == SEC_I_CONTEXT_EXPIRED) { | |
| 1840 /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not | |
| 1841 returned so we have to work around that in cleanup. */ | |
| 1842 BACKEND->recv_sspi_close_notify = true; | |
| 1843 if(!BACKEND->recv_connection_closed) { | |
| 1844 BACKEND->recv_connection_closed = true; | |
| 1845 infof(data, "schannel: server closed the connection\n"); | |
| 1846 } | |
| 1847 goto cleanup; | |
| 1848 } | |
| 1849 } | |
| 1850 else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { | |
| 1851 BACKEND->encdata_is_incomplete = true; | |
| 1852 if(!*err) | |
| 1853 *err = CURLE_AGAIN; | |
| 1854 infof(data, "schannel: failed to decrypt data, need more data\n"); | |
| 1855 goto cleanup; | |
| 1856 } | |
| 1857 else { | |
| 1858 char buffer[STRERROR_LEN]; | |
| 1859 *err = CURLE_RECV_ERROR; | |
| 1860 infof(data, "schannel: failed to read data from server: %s\n", | |
| 1861 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 1862 goto cleanup; | |
| 1863 } | |
| 1864 } | |
| 1865 | |
| 1866 DEBUGF(infof(data, | |
| 1867 "schannel: encrypted data buffer: offset %zu length %zu\n", | |
| 1868 BACKEND->encdata_offset, BACKEND->encdata_length)); | |
| 1869 | |
| 1870 DEBUGF(infof(data, | |
| 1871 "schannel: decrypted data buffer: offset %zu length %zu\n", | |
| 1872 BACKEND->decdata_offset, BACKEND->decdata_length)); | |
| 1873 | |
| 1874 cleanup: | |
| 1875 /* Warning- there is no guarantee the encdata state is valid at this point */ | |
| 1876 DEBUGF(infof(data, "schannel: schannel_recv cleanup\n")); | |
| 1877 | |
| 1878 /* Error if the connection has closed without a close_notify. | |
| 1879 Behavior here is a matter of debate. We don't want to be vulnerable to a | |
| 1880 truncation attack however there's some browser precedent for ignoring the | |
| 1881 close_notify for compatibility reasons. | |
| 1882 Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't | |
| 1883 return close_notify. In that case if the connection was closed we assume it | |
| 1884 was graceful (close_notify) since there doesn't seem to be a way to tell. | |
| 1885 */ | |
| 1886 if(len && !BACKEND->decdata_offset && BACKEND->recv_connection_closed && | |
| 1887 !BACKEND->recv_sspi_close_notify) { | |
| 1888 bool isWin2k = Curl_verify_windows_version(5, 0, PLATFORM_WINNT, | |
| 1889 VERSION_EQUAL); | |
| 1890 | |
| 1891 if(isWin2k && sspi_status == SEC_E_OK) | |
| 1892 BACKEND->recv_sspi_close_notify = true; | |
| 1893 else { | |
| 1894 *err = CURLE_RECV_ERROR; | |
| 1895 infof(data, "schannel: server closed abruptly (missing close_notify)\n"); | |
| 1896 } | |
| 1897 } | |
| 1898 | |
| 1899 /* Any error other than CURLE_AGAIN is an unrecoverable error. */ | |
| 1900 if(*err && *err != CURLE_AGAIN) | |
| 1901 BACKEND->recv_unrecoverable_err = *err; | |
| 1902 | |
| 1903 size = len < BACKEND->decdata_offset ? len : BACKEND->decdata_offset; | |
| 1904 if(size) { | |
| 1905 memcpy(buf, BACKEND->decdata_buffer, size); | |
| 1906 memmove(BACKEND->decdata_buffer, BACKEND->decdata_buffer + size, | |
| 1907 BACKEND->decdata_offset - size); | |
| 1908 BACKEND->decdata_offset -= size; | |
| 1909 DEBUGF(infof(data, "schannel: decrypted data returned %zu\n", size)); | |
| 1910 DEBUGF(infof(data, | |
| 1911 "schannel: decrypted data buffer: offset %zu length %zu\n", | |
| 1912 BACKEND->decdata_offset, BACKEND->decdata_length)); | |
| 1913 *err = CURLE_OK; | |
| 1914 return (ssize_t)size; | |
| 1915 } | |
| 1916 | |
| 1917 if(!*err && !BACKEND->recv_connection_closed) | |
| 1918 *err = CURLE_AGAIN; | |
| 1919 | |
| 1920 /* It's debatable what to return when !len. We could return whatever error we | |
| 1921 got from decryption but instead we override here so the return is consistent. | |
| 1922 */ | |
| 1923 if(!len) | |
| 1924 *err = CURLE_OK; | |
| 1925 | |
| 1926 return *err ? -1 : 0; | |
| 1927 } | |
| 1928 | |
| 1929 static CURLcode Curl_schannel_connect_nonblocking(struct connectdata *conn, | |
| 1930 int sockindex, bool *done) | |
| 1931 { | |
| 1932 return schannel_connect_common(conn, sockindex, TRUE, done); | |
| 1933 } | |
| 1934 | |
| 1935 static CURLcode Curl_schannel_connect(struct connectdata *conn, int sockindex) | |
| 1936 { | |
| 1937 CURLcode result; | |
| 1938 bool done = FALSE; | |
| 1939 | |
| 1940 result = schannel_connect_common(conn, sockindex, FALSE, &done); | |
| 1941 if(result) | |
| 1942 return result; | |
| 1943 | |
| 1944 DEBUGASSERT(done); | |
| 1945 | |
| 1946 return CURLE_OK; | |
| 1947 } | |
| 1948 | |
| 1949 static bool Curl_schannel_data_pending(const struct connectdata *conn, | |
| 1950 int sockindex) | |
| 1951 { | |
| 1952 const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1953 | |
| 1954 if(connssl->use) /* SSL/TLS is in use */ | |
| 1955 return (BACKEND->decdata_offset > 0 || | |
| 1956 (BACKEND->encdata_offset > 0 && !BACKEND->encdata_is_incomplete)); | |
| 1957 else | |
| 1958 return FALSE; | |
| 1959 } | |
| 1960 | |
| 1961 static void Curl_schannel_close(struct connectdata *conn, int sockindex) | |
| 1962 { | |
| 1963 if(conn->ssl[sockindex].use) | |
| 1964 /* if the SSL/TLS channel hasn't been shut down yet, do that now. */ | |
| 1965 Curl_ssl_shutdown(conn, sockindex); | |
| 1966 } | |
| 1967 | |
| 1968 static void Curl_schannel_session_free(void *ptr) | |
| 1969 { | |
| 1970 /* this is expected to be called under sessionid lock */ | |
| 1971 struct curl_schannel_cred *cred = ptr; | |
| 1972 | |
| 1973 cred->refcount--; | |
| 1974 if(cred->refcount == 0) { | |
| 1975 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); | |
| 1976 Curl_safefree(cred); | |
| 1977 } | |
| 1978 } | |
| 1979 | |
| 1980 static int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) | |
| 1981 { | |
| 1982 /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx | |
| 1983 * Shutting Down an Schannel Connection | |
| 1984 */ | |
| 1985 struct Curl_easy *data = conn->data; | |
| 1986 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 1987 char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name : | |
| 1988 conn->host.name; | |
| 1989 | |
| 1990 DEBUGASSERT(data); | |
| 1991 | |
| 1992 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n", | |
| 1993 hostname, conn->remote_port); | |
| 1994 | |
| 1995 if(BACKEND->cred && BACKEND->ctxt) { | |
| 1996 SecBufferDesc BuffDesc; | |
| 1997 SecBuffer Buffer; | |
| 1998 SECURITY_STATUS sspi_status; | |
| 1999 SecBuffer outbuf; | |
| 2000 SecBufferDesc outbuf_desc; | |
| 2001 CURLcode result; | |
| 2002 TCHAR *host_name; | |
| 2003 DWORD dwshut = SCHANNEL_SHUTDOWN; | |
| 2004 | |
| 2005 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); | |
| 2006 InitSecBufferDesc(&BuffDesc, &Buffer, 1); | |
| 2007 | |
| 2008 sspi_status = s_pSecFn->ApplyControlToken(&BACKEND->ctxt->ctxt_handle, | |
| 2009 &BuffDesc); | |
| 2010 | |
| 2011 if(sspi_status != SEC_E_OK) { | |
| 2012 char buffer[STRERROR_LEN]; | |
| 2013 failf(data, "schannel: ApplyControlToken failure: %s", | |
| 2014 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 2015 } | |
| 2016 | |
| 2017 host_name = Curl_convert_UTF8_to_tchar(hostname); | |
| 2018 if(!host_name) | |
| 2019 return CURLE_OUT_OF_MEMORY; | |
| 2020 | |
| 2021 /* setup output buffer */ | |
| 2022 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); | |
| 2023 InitSecBufferDesc(&outbuf_desc, &outbuf, 1); | |
| 2024 | |
| 2025 sspi_status = s_pSecFn->InitializeSecurityContext( | |
| 2026 &BACKEND->cred->cred_handle, | |
| 2027 &BACKEND->ctxt->ctxt_handle, | |
| 2028 host_name, | |
| 2029 BACKEND->req_flags, | |
| 2030 0, | |
| 2031 0, | |
| 2032 NULL, | |
| 2033 0, | |
| 2034 &BACKEND->ctxt->ctxt_handle, | |
| 2035 &outbuf_desc, | |
| 2036 &BACKEND->ret_flags, | |
| 2037 &BACKEND->ctxt->time_stamp); | |
| 2038 | |
| 2039 Curl_unicodefree(host_name); | |
| 2040 | |
| 2041 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { | |
| 2042 /* send close message which is in output buffer */ | |
| 2043 ssize_t written; | |
| 2044 result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, | |
| 2045 outbuf.cbBuffer, &written); | |
| 2046 | |
| 2047 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); | |
| 2048 if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) { | |
| 2049 infof(data, "schannel: failed to send close msg: %s" | |
| 2050 " (bytes written: %zd)\n", curl_easy_strerror(result), written); | |
| 2051 } | |
| 2052 } | |
| 2053 } | |
| 2054 | |
| 2055 /* free SSPI Schannel API security context handle */ | |
| 2056 if(BACKEND->ctxt) { | |
| 2057 DEBUGF(infof(data, "schannel: clear security context handle\n")); | |
| 2058 s_pSecFn->DeleteSecurityContext(&BACKEND->ctxt->ctxt_handle); | |
| 2059 Curl_safefree(BACKEND->ctxt); | |
| 2060 } | |
| 2061 | |
| 2062 /* free SSPI Schannel API credential handle */ | |
| 2063 if(BACKEND->cred) { | |
| 2064 /* | |
| 2065 * When this function is called from Curl_schannel_close() the connection | |
| 2066 * might not have an associated transfer so the check for conn->data is | |
| 2067 * necessary. | |
| 2068 */ | |
| 2069 Curl_ssl_sessionid_lock(conn); | |
| 2070 Curl_schannel_session_free(BACKEND->cred); | |
| 2071 Curl_ssl_sessionid_unlock(conn); | |
| 2072 BACKEND->cred = NULL; | |
| 2073 } | |
| 2074 | |
| 2075 /* free internal buffer for received encrypted data */ | |
| 2076 if(BACKEND->encdata_buffer != NULL) { | |
| 2077 Curl_safefree(BACKEND->encdata_buffer); | |
| 2078 BACKEND->encdata_length = 0; | |
| 2079 BACKEND->encdata_offset = 0; | |
| 2080 BACKEND->encdata_is_incomplete = false; | |
| 2081 } | |
| 2082 | |
| 2083 /* free internal buffer for received decrypted data */ | |
| 2084 if(BACKEND->decdata_buffer != NULL) { | |
| 2085 Curl_safefree(BACKEND->decdata_buffer); | |
| 2086 BACKEND->decdata_length = 0; | |
| 2087 BACKEND->decdata_offset = 0; | |
| 2088 } | |
| 2089 | |
| 2090 return CURLE_OK; | |
| 2091 } | |
| 2092 | |
| 2093 static int Curl_schannel_init(void) | |
| 2094 { | |
| 2095 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0); | |
| 2096 } | |
| 2097 | |
| 2098 static void Curl_schannel_cleanup(void) | |
| 2099 { | |
| 2100 Curl_sspi_global_cleanup(); | |
| 2101 } | |
| 2102 | |
| 2103 static size_t Curl_schannel_version(char *buffer, size_t size) | |
| 2104 { | |
| 2105 size = msnprintf(buffer, size, "Schannel"); | |
| 2106 | |
| 2107 return size; | |
| 2108 } | |
| 2109 | |
| 2110 static CURLcode Curl_schannel_random(struct Curl_easy *data UNUSED_PARAM, | |
| 2111 unsigned char *entropy, size_t length) | |
| 2112 { | |
| 2113 HCRYPTPROV hCryptProv = 0; | |
| 2114 | |
| 2115 (void)data; | |
| 2116 | |
| 2117 if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL, | |
| 2118 CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) | |
| 2119 return CURLE_FAILED_INIT; | |
| 2120 | |
| 2121 if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) { | |
| 2122 CryptReleaseContext(hCryptProv, 0UL); | |
| 2123 return CURLE_FAILED_INIT; | |
| 2124 } | |
| 2125 | |
| 2126 CryptReleaseContext(hCryptProv, 0UL); | |
| 2127 return CURLE_OK; | |
| 2128 } | |
| 2129 | |
| 2130 static CURLcode pkp_pin_peer_pubkey(struct connectdata *conn, int sockindex, | |
| 2131 const char *pinnedpubkey) | |
| 2132 { | |
| 2133 struct Curl_easy *data = conn->data; | |
| 2134 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; | |
| 2135 CERT_CONTEXT *pCertContextServer = NULL; | |
| 2136 | |
| 2137 /* Result is returned to caller */ | |
| 2138 CURLcode result = CURLE_SSL_PINNEDPUBKEYNOTMATCH; | |
| 2139 | |
| 2140 /* if a path wasn't specified, don't pin */ | |
| 2141 if(!pinnedpubkey) | |
| 2142 return CURLE_OK; | |
| 2143 | |
| 2144 do { | |
| 2145 SECURITY_STATUS sspi_status; | |
| 2146 const char *x509_der; | |
| 2147 DWORD x509_der_len; | |
| 2148 curl_X509certificate x509_parsed; | |
| 2149 curl_asn1Element *pubkey; | |
| 2150 | |
| 2151 sspi_status = | |
| 2152 s_pSecFn->QueryContextAttributes(&BACKEND->ctxt->ctxt_handle, | |
| 2153 SECPKG_ATTR_REMOTE_CERT_CONTEXT, | |
| 2154 &pCertContextServer); | |
| 2155 | |
| 2156 if((sspi_status != SEC_E_OK) || (pCertContextServer == NULL)) { | |
| 2157 char buffer[STRERROR_LEN]; | |
| 2158 failf(data, "schannel: Failed to read remote certificate context: %s", | |
| 2159 Curl_sspi_strerror(sspi_status, buffer, sizeof(buffer))); | |
| 2160 break; /* failed */ | |
| 2161 } | |
| 2162 | |
| 2163 | |
| 2164 if(!(((pCertContextServer->dwCertEncodingType & X509_ASN_ENCODING) != 0) && | |
| 2165 (pCertContextServer->cbCertEncoded > 0))) | |
| 2166 break; | |
| 2167 | |
| 2168 x509_der = (const char *)pCertContextServer->pbCertEncoded; | |
| 2169 x509_der_len = pCertContextServer->cbCertEncoded; | |
| 2170 memset(&x509_parsed, 0, sizeof(x509_parsed)); | |
| 2171 if(Curl_parseX509(&x509_parsed, x509_der, x509_der + x509_der_len)) | |
| 2172 break; | |
| 2173 | |
| 2174 pubkey = &x509_parsed.subjectPublicKeyInfo; | |
| 2175 if(!pubkey->header || pubkey->end <= pubkey->header) { | |
| 2176 failf(data, "SSL: failed retrieving public key from server certificate"); | |
| 2177 break; | |
| 2178 } | |
| 2179 | |
| 2180 result = Curl_pin_peer_pubkey(data, | |
| 2181 pinnedpubkey, | |
| 2182 (const unsigned char *)pubkey->header, | |
| 2183 (size_t)(pubkey->end - pubkey->header)); | |
| 2184 if(result) { | |
| 2185 failf(data, "SSL: public key does not match pinned public key!"); | |
| 2186 } | |
| 2187 } while(0); | |
| 2188 | |
| 2189 if(pCertContextServer) | |
| 2190 CertFreeCertificateContext(pCertContextServer); | |
| 2191 | |
| 2192 return result; | |
| 2193 } | |
| 2194 | |
| 2195 static void Curl_schannel_checksum(const unsigned char *input, | |
| 2196 size_t inputlen, | |
| 2197 unsigned char *checksum, | |
| 2198 size_t checksumlen, | |
| 2199 DWORD provType, | |
| 2200 const unsigned int algId) | |
| 2201 { | |
| 2202 HCRYPTPROV hProv = 0; | |
| 2203 HCRYPTHASH hHash = 0; | |
| 2204 DWORD cbHashSize = 0; | |
| 2205 DWORD dwHashSizeLen = (DWORD)sizeof(cbHashSize); | |
| 2206 DWORD dwChecksumLen = (DWORD)checksumlen; | |
| 2207 | |
| 2208 /* since this can fail in multiple ways, zero memory first so we never | |
| 2209 * return old data | |
| 2210 */ | |
| 2211 memset(checksum, 0, checksumlen); | |
| 2212 | |
| 2213 if(!CryptAcquireContext(&hProv, NULL, NULL, provType, | |
| 2214 CRYPT_VERIFYCONTEXT)) | |
| 2215 return; /* failed */ | |
| 2216 | |
| 2217 do { | |
| 2218 if(!CryptCreateHash(hProv, algId, 0, 0, &hHash)) | |
| 2219 break; /* failed */ | |
| 2220 | |
| 2221 /* workaround for original MinGW, should be (const BYTE*) */ | |
| 2222 if(!CryptHashData(hHash, (BYTE*)input, (DWORD)inputlen, 0)) | |
| 2223 break; /* failed */ | |
| 2224 | |
| 2225 /* get hash size */ | |
| 2226 if(!CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE *)&cbHashSize, | |
| 2227 &dwHashSizeLen, 0)) | |
| 2228 break; /* failed */ | |
| 2229 | |
| 2230 /* check hash size */ | |
| 2231 if(checksumlen < cbHashSize) | |
| 2232 break; /* failed */ | |
| 2233 | |
| 2234 if(CryptGetHashParam(hHash, HP_HASHVAL, checksum, &dwChecksumLen, 0)) | |
| 2235 break; /* failed */ | |
| 2236 } while(0); | |
| 2237 | |
| 2238 if(hHash) | |
| 2239 CryptDestroyHash(hHash); | |
| 2240 | |
| 2241 if(hProv) | |
| 2242 CryptReleaseContext(hProv, 0); | |
| 2243 } | |
| 2244 | |
| 2245 static CURLcode Curl_schannel_md5sum(unsigned char *input, | |
| 2246 size_t inputlen, | |
| 2247 unsigned char *md5sum, | |
| 2248 size_t md5len) | |
| 2249 { | |
| 2250 Curl_schannel_checksum(input, inputlen, md5sum, md5len, | |
| 2251 PROV_RSA_FULL, CALG_MD5); | |
| 2252 return CURLE_OK; | |
| 2253 } | |
| 2254 | |
| 2255 static CURLcode Curl_schannel_sha256sum(const unsigned char *input, | |
| 2256 size_t inputlen, | |
| 2257 unsigned char *sha256sum, | |
| 2258 size_t sha256len) | |
| 2259 { | |
| 2260 Curl_schannel_checksum(input, inputlen, sha256sum, sha256len, | |
| 2261 PROV_RSA_AES, CALG_SHA_256); | |
| 2262 return CURLE_OK; | |
| 2263 } | |
| 2264 | |
| 2265 static void *Curl_schannel_get_internals(struct ssl_connect_data *connssl, | |
| 2266 CURLINFO info UNUSED_PARAM) | |
| 2267 { | |
| 2268 (void)info; | |
| 2269 return &BACKEND->ctxt->ctxt_handle; | |
| 2270 } | |
| 2271 | |
| 2272 const struct Curl_ssl Curl_ssl_schannel = { | |
| 2273 { CURLSSLBACKEND_SCHANNEL, "schannel" }, /* info */ | |
| 2274 | |
| 2275 SSLSUPP_CERTINFO | | |
| 2276 SSLSUPP_PINNEDPUBKEY, | |
| 2277 | |
| 2278 sizeof(struct ssl_backend_data), | |
| 2279 | |
| 2280 Curl_schannel_init, /* init */ | |
| 2281 Curl_schannel_cleanup, /* cleanup */ | |
| 2282 Curl_schannel_version, /* version */ | |
| 2283 Curl_none_check_cxn, /* check_cxn */ | |
| 2284 Curl_schannel_shutdown, /* shutdown */ | |
| 2285 Curl_schannel_data_pending, /* data_pending */ | |
| 2286 Curl_schannel_random, /* random */ | |
| 2287 Curl_none_cert_status_request, /* cert_status_request */ | |
| 2288 Curl_schannel_connect, /* connect */ | |
| 2289 Curl_schannel_connect_nonblocking, /* connect_nonblocking */ | |
| 2290 Curl_schannel_get_internals, /* get_internals */ | |
| 2291 Curl_schannel_close, /* close_one */ | |
| 2292 Curl_none_close_all, /* close_all */ | |
| 2293 Curl_schannel_session_free, /* session_free */ | |
| 2294 Curl_none_set_engine, /* set_engine */ | |
| 2295 Curl_none_set_engine_default, /* set_engine_default */ | |
| 2296 Curl_none_engines_list, /* engines_list */ | |
| 2297 Curl_none_false_start, /* false_start */ | |
| 2298 Curl_schannel_md5sum, /* md5sum */ | |
| 2299 Curl_schannel_sha256sum /* sha256sum */ | |
| 2300 }; | |
| 2301 | |
| 2302 #endif /* USE_SCHANNEL */ |
