Annotation of embedaddon/curl/lib/vtls/schannel.c, revision 1.1
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 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>