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