1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012 - 2014, Marc Hoersken, <info@marc-hoersken.de> 9 * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com> 10 * Copyright (C) 2012 - 2014, 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 http://curl.haxx.se/docs/copyright.html. 15 * 16 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 17 * copies of the Software, and permit persons to whom the Software is 18 * furnished to do so, under the terms of the COPYING file. 19 * 20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 * KIND, either express or implied. 22 * 23 ***************************************************************************/ 24 25/* 26 * Source file for all SChannel-specific code for the TLS/SSL layer. No code 27 * but vtls.c should ever call or use these functions. 28 * 29 */ 30 31/* 32 * Based upon the PolarSSL implementation in polarssl.c and polarssl.h: 33 * Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com> 34 * 35 * Based upon the CyaSSL implementation in cyassl.c and cyassl.h: 36 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al. 37 * 38 * Thanks for code and inspiration! 39 */ 40 41/* 42 * TODO list for TLS/SSL implementation: 43 * - implement client certificate authentication 44 * - implement custom server certificate validation 45 * - implement cipher/algorithm option 46 * 47 * Related articles on MSDN: 48 * - Getting a Certificate for Schannel 49 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa375447.aspx 50 * - Specifying Schannel Ciphers and Cipher Strengths 51 * http://msdn.microsoft.com/en-us/library/windows/desktop/aa380161.aspx 52 */ 53 54#include "curl_setup.h" 55 56#ifdef USE_SCHANNEL 57 58#ifndef USE_WINDOWS_SSPI 59# error "Can't compile SCHANNEL support without SSPI." 60#endif 61 62#include "curl_sspi.h" 63#include "curl_schannel.h" 64#include "vtls.h" 65#include "sendf.h" 66#include "connect.h" /* for the connect timeout */ 67#include "strerror.h" 68#include "select.h" /* for the socket readyness */ 69#include "inet_pton.h" /* for IP addr SNI check */ 70#include "curl_multibyte.h" 71#include "warnless.h" 72 73#define _MPRINTF_REPLACE /* use our functions only */ 74#include <curl/mprintf.h> 75 76#include "curl_memory.h" 77/* The last #include file should be: */ 78#include "memdebug.h" 79 80/* Uncomment to force verbose output 81 * #define infof(x, y, ...) printf(y, __VA_ARGS__) 82 * #define failf(x, y, ...) printf(y, __VA_ARGS__) 83 */ 84 85static Curl_recv schannel_recv; 86static Curl_send schannel_send; 87 88#ifdef _WIN32_WCE 89static CURLcode verify_certificate(struct connectdata *conn, int sockindex); 90#endif 91 92static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType, 93 void *BufDataPtr, unsigned long BufByteSize) 94{ 95 buffer->cbBuffer = BufByteSize; 96 buffer->BufferType = BufType; 97 buffer->pvBuffer = BufDataPtr; 98} 99 100static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr, 101 unsigned long NumArrElem) 102{ 103 desc->ulVersion = SECBUFFER_VERSION; 104 desc->pBuffers = BufArr; 105 desc->cBuffers = NumArrElem; 106} 107 108static CURLcode 109schannel_connect_step1(struct connectdata *conn, int sockindex) 110{ 111 ssize_t written = -1; 112 struct SessionHandle *data = conn->data; 113 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 114 SecBuffer outbuf; 115 SecBufferDesc outbuf_desc; 116 SCHANNEL_CRED schannel_cred; 117 SECURITY_STATUS sspi_status = SEC_E_OK; 118 struct curl_schannel_cred *old_cred = NULL; 119 struct in_addr addr; 120#ifdef ENABLE_IPV6 121 struct in6_addr addr6; 122#endif 123 TCHAR *host_name; 124 CURLcode code; 125 126 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n", 127 conn->host.name, conn->remote_port); 128 129 /* check for an existing re-usable credential handle */ 130 if(!Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)) { 131 connssl->cred = old_cred; 132 infof(data, "schannel: re-using existing credential handle\n"); 133 } 134 else { 135 /* setup Schannel API options */ 136 memset(&schannel_cred, 0, sizeof(schannel_cred)); 137 schannel_cred.dwVersion = SCHANNEL_CRED_VERSION; 138 139 if(data->set.ssl.verifypeer) { 140#ifdef _WIN32_WCE 141 /* certificate validation on CE doesn't seem to work right; we'll 142 do it following a more manual process. */ 143 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | 144 SCH_CRED_IGNORE_NO_REVOCATION_CHECK | 145 SCH_CRED_IGNORE_REVOCATION_OFFLINE; 146#else 147 schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION | 148 SCH_CRED_REVOCATION_CHECK_CHAIN; 149#endif 150 infof(data, "schannel: checking server certificate revocation\n"); 151 } 152 else { 153 schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION | 154 SCH_CRED_IGNORE_NO_REVOCATION_CHECK | 155 SCH_CRED_IGNORE_REVOCATION_OFFLINE; 156 infof(data, "schannel: disable server certificate revocation checks\n"); 157 } 158 159 if(!data->set.ssl.verifyhost) { 160 schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK; 161 infof(data, "schannel: verifyhost setting prevents Schannel from " 162 "comparing the supplied target name with the subject " 163 "names in server certificates. Also disables SNI.\n"); 164 } 165 166 switch(data->set.ssl.version) { 167 case CURL_SSLVERSION_TLSv1: 168 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | 169 SP_PROT_TLS1_1_CLIENT | 170 SP_PROT_TLS1_2_CLIENT; 171 break; 172 case CURL_SSLVERSION_TLSv1_0: 173 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT; 174 break; 175 case CURL_SSLVERSION_TLSv1_1: 176 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT; 177 break; 178 case CURL_SSLVERSION_TLSv1_2: 179 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT; 180 break; 181 case CURL_SSLVERSION_SSLv3: 182 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT; 183 break; 184 case CURL_SSLVERSION_SSLv2: 185 schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT; 186 break; 187 default: 188 schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT | 189 SP_PROT_TLS1_1_CLIENT | 190 SP_PROT_TLS1_2_CLIENT | 191 SP_PROT_SSL3_CLIENT; 192 break; 193 } 194 195 /* allocate memory for the re-usable credential handle */ 196 connssl->cred = malloc(sizeof(struct curl_schannel_cred)); 197 if(!connssl->cred) { 198 failf(data, "schannel: unable to allocate memory"); 199 return CURLE_OUT_OF_MEMORY; 200 } 201 memset(connssl->cred, 0, sizeof(struct curl_schannel_cred)); 202 203 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx */ 204 sspi_status = s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME, 205 SECPKG_CRED_OUTBOUND, NULL, &schannel_cred, NULL, NULL, 206 &connssl->cred->cred_handle, &connssl->cred->time_stamp); 207 208 if(sspi_status != SEC_E_OK) { 209 if(sspi_status == SEC_E_WRONG_PRINCIPAL) 210 failf(data, "schannel: SNI or certificate check failed: %s", 211 Curl_sspi_strerror(conn, sspi_status)); 212 else 213 failf(data, "schannel: AcquireCredentialsHandle failed: %s", 214 Curl_sspi_strerror(conn, sspi_status)); 215 Curl_safefree(connssl->cred); 216 return CURLE_SSL_CONNECT_ERROR; 217 } 218 } 219 220 /* Warn if SNI is disabled due to use of an IP address */ 221 if(Curl_inet_pton(AF_INET, conn->host.name, &addr) 222#ifdef ENABLE_IPV6 223 || Curl_inet_pton(AF_INET6, conn->host.name, &addr6) 224#endif 225 ) { 226 infof(data, "schannel: using IP address, SNI is not supported by OS.\n"); 227 } 228 229 /* setup output buffer */ 230 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); 231 InitSecBufferDesc(&outbuf_desc, &outbuf, 1); 232 233 /* setup request flags */ 234 connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT | 235 ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY | 236 ISC_REQ_STREAM; 237 238 /* allocate memory for the security context handle */ 239 connssl->ctxt = malloc(sizeof(struct curl_schannel_ctxt)); 240 if(!connssl->ctxt) { 241 failf(data, "schannel: unable to allocate memory"); 242 return CURLE_OUT_OF_MEMORY; 243 } 244 memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt)); 245 246 host_name = Curl_convert_UTF8_to_tchar(conn->host.name); 247 if(!host_name) 248 return CURLE_OUT_OF_MEMORY; 249 250 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */ 251 252 sspi_status = s_pSecFn->InitializeSecurityContext( 253 &connssl->cred->cred_handle, NULL, host_name, 254 connssl->req_flags, 0, 0, NULL, 0, &connssl->ctxt->ctxt_handle, 255 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp); 256 257 Curl_unicodefree(host_name); 258 259 if(sspi_status != SEC_I_CONTINUE_NEEDED) { 260 if(sspi_status == SEC_E_WRONG_PRINCIPAL) 261 failf(data, "schannel: SNI or certificate check failed: %s", 262 Curl_sspi_strerror(conn, sspi_status)); 263 else 264 failf(data, "schannel: initial InitializeSecurityContext failed: %s", 265 Curl_sspi_strerror(conn, sspi_status)); 266 Curl_safefree(connssl->ctxt); 267 return CURLE_SSL_CONNECT_ERROR; 268 } 269 270 infof(data, "schannel: sending initial handshake data: " 271 "sending %lu bytes...\n", outbuf.cbBuffer); 272 273 /* send initial handshake data which is now stored in output buffer */ 274 code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, 275 outbuf.cbBuffer, &written); 276 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); 277 if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) { 278 failf(data, "schannel: failed to send initial handshake data: " 279 "sent %zd of %lu bytes", written, outbuf.cbBuffer); 280 return CURLE_SSL_CONNECT_ERROR; 281 } 282 283 infof(data, "schannel: sent initial handshake data: " 284 "sent %zd bytes\n", written); 285 286 /* continue to second handshake step */ 287 connssl->connecting_state = ssl_connect_2; 288 289 return CURLE_OK; 290} 291 292static CURLcode 293schannel_connect_step2(struct connectdata *conn, int sockindex) 294{ 295 int i; 296 ssize_t nread = -1, written = -1; 297 struct SessionHandle *data = conn->data; 298 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 299 SecBuffer outbuf[2]; 300 SecBufferDesc outbuf_desc; 301 SecBuffer inbuf[2]; 302 SecBufferDesc inbuf_desc; 303 SECURITY_STATUS sspi_status = SEC_E_OK; 304 TCHAR *host_name; 305 CURLcode code; 306 bool doread; 307 308 doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE; 309 310 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n", 311 conn->host.name, conn->remote_port); 312 313 /* buffer to store previously received and encrypted data */ 314 if(connssl->encdata_buffer == NULL) { 315 connssl->encdata_offset = 0; 316 connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; 317 connssl->encdata_buffer = malloc(connssl->encdata_length); 318 if(connssl->encdata_buffer == NULL) { 319 failf(data, "schannel: unable to allocate memory"); 320 return CURLE_OUT_OF_MEMORY; 321 } 322 } 323 324 /* if we need a bigger buffer to read a full message, increase buffer now */ 325 if(connssl->encdata_length - connssl->encdata_offset < 326 CURL_SCHANNEL_BUFFER_FREE_SIZE) { 327 /* increase internal encrypted data buffer */ 328 connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR; 329 connssl->encdata_buffer = realloc(connssl->encdata_buffer, 330 connssl->encdata_length); 331 332 if(connssl->encdata_buffer == NULL) { 333 failf(data, "schannel: unable to re-allocate memory"); 334 return CURLE_OUT_OF_MEMORY; 335 } 336 } 337 338 for(;;) { 339 if(doread) { 340 /* read encrypted handshake data from socket */ 341 code = Curl_read_plain(conn->sock[sockindex], 342 (char *) (connssl->encdata_buffer + connssl->encdata_offset), 343 connssl->encdata_length - connssl->encdata_offset, 344 &nread); 345 if(code == CURLE_AGAIN) { 346 if(connssl->connecting_state != ssl_connect_2_writing) 347 connssl->connecting_state = ssl_connect_2_reading; 348 infof(data, "schannel: failed to receive handshake, " 349 "need more data\n"); 350 return CURLE_OK; 351 } 352 else if((code != CURLE_OK) || (nread == 0)) { 353 failf(data, "schannel: failed to receive handshake, " 354 "SSL/TLS connection failed"); 355 return CURLE_SSL_CONNECT_ERROR; 356 } 357 358 /* increase encrypted data buffer offset */ 359 connssl->encdata_offset += nread; 360 } 361 362 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", 363 connssl->encdata_offset, connssl->encdata_length); 364 365 /* setup input buffers */ 366 InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset), 367 curlx_uztoul(connssl->encdata_offset)); 368 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); 369 InitSecBufferDesc(&inbuf_desc, inbuf, 2); 370 371 /* setup output buffers */ 372 InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0); 373 InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0); 374 InitSecBufferDesc(&outbuf_desc, outbuf, 2); 375 376 if(inbuf[0].pvBuffer == NULL) { 377 failf(data, "schannel: unable to allocate memory"); 378 return CURLE_OUT_OF_MEMORY; 379 } 380 381 /* copy received handshake data into input buffer */ 382 memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer, 383 connssl->encdata_offset); 384 385 host_name = Curl_convert_UTF8_to_tchar(conn->host.name); 386 if(!host_name) 387 return CURLE_OUT_OF_MEMORY; 388 389 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx */ 390 391 sspi_status = s_pSecFn->InitializeSecurityContext( 392 &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle, 393 host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL, 394 &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp); 395 396 Curl_unicodefree(host_name); 397 398 /* free buffer for received handshake data */ 399 Curl_safefree(inbuf[0].pvBuffer); 400 401 /* check if the handshake was incomplete */ 402 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { 403 connssl->connecting_state = ssl_connect_2_reading; 404 infof(data, "schannel: received incomplete message, need more data\n"); 405 return CURLE_OK; 406 } 407 408 /* check if the handshake needs to be continued */ 409 if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) { 410 for(i = 0; i < 2; i++) { 411 /* search for handshake tokens that need to be send */ 412 if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) { 413 infof(data, "schannel: sending next handshake data: " 414 "sending %lu bytes...\n", outbuf[i].cbBuffer); 415 416 /* send handshake token to server */ 417 code = Curl_write_plain(conn, conn->sock[sockindex], 418 outbuf[i].pvBuffer, outbuf[i].cbBuffer, 419 &written); 420 if((code != CURLE_OK) || (outbuf[i].cbBuffer != (size_t)written)) { 421 failf(data, "schannel: failed to send next handshake data: " 422 "sent %zd of %lu bytes", written, outbuf[i].cbBuffer); 423 return CURLE_SSL_CONNECT_ERROR; 424 } 425 } 426 427 /* free obsolete buffer */ 428 if(outbuf[i].pvBuffer != NULL) { 429 s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer); 430 } 431 } 432 } 433 else { 434 if(sspi_status == SEC_E_WRONG_PRINCIPAL) 435 failf(data, "schannel: SNI or certificate check failed: %s", 436 Curl_sspi_strerror(conn, sspi_status)); 437 else 438 failf(data, "schannel: next InitializeSecurityContext failed: %s", 439 Curl_sspi_strerror(conn, sspi_status)); 440 return CURLE_SSL_CONNECT_ERROR; 441 } 442 443 /* check if there was additional remaining encrypted data */ 444 if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) { 445 infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer); 446 /* 447 There are two cases where we could be getting extra data here: 448 1) If we're renegotiating a connection and the handshake is already 449 complete (from the server perspective), it can encrypted app data 450 (not handshake data) in an extra buffer at this point. 451 2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a 452 connection and this extra data is part of the handshake. 453 We should process the data immediately; waiting for the socket to 454 be ready may fail since the server is done sending handshake data. 455 */ 456 /* check if the remaining data is less than the total amount 457 and therefore begins after the already processed data */ 458 if(connssl->encdata_offset > inbuf[1].cbBuffer) { 459 memmove(connssl->encdata_buffer, 460 (connssl->encdata_buffer + connssl->encdata_offset) - 461 inbuf[1].cbBuffer, inbuf[1].cbBuffer); 462 connssl->encdata_offset = inbuf[1].cbBuffer; 463 if(sspi_status == SEC_I_CONTINUE_NEEDED) { 464 doread = FALSE; 465 continue; 466 } 467 } 468 } 469 else { 470 connssl->encdata_offset = 0; 471 } 472 break; 473 } 474 475 /* check if the handshake needs to be continued */ 476 if(sspi_status == SEC_I_CONTINUE_NEEDED) { 477 connssl->connecting_state = ssl_connect_2_reading; 478 return CURLE_OK; 479 } 480 481 /* check if the handshake is complete */ 482 if(sspi_status == SEC_E_OK) { 483 connssl->connecting_state = ssl_connect_3; 484 infof(data, "schannel: SSL/TLS handshake complete\n"); 485 } 486 487#ifdef _WIN32_WCE 488 /* Windows CE doesn't do any server certificate validation. 489 We have to do it manually. */ 490 if(data->set.ssl.verifypeer) 491 return verify_certificate(conn, sockindex); 492#endif 493 494 return CURLE_OK; 495} 496 497static CURLcode 498schannel_connect_step3(struct connectdata *conn, int sockindex) 499{ 500 CURLcode retcode = CURLE_OK; 501 struct SessionHandle *data = conn->data; 502 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 503 struct curl_schannel_cred *old_cred = NULL; 504 int incache; 505 506 DEBUGASSERT(ssl_connect_3 == connssl->connecting_state); 507 508 infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n", 509 conn->host.name, conn->remote_port); 510 511 /* check if the required context attributes are met */ 512 if(connssl->ret_flags != connssl->req_flags) { 513 if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT)) 514 failf(data, "schannel: failed to setup sequence detection"); 515 if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT)) 516 failf(data, "schannel: failed to setup replay detection"); 517 if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY)) 518 failf(data, "schannel: failed to setup confidentiality"); 519 if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY)) 520 failf(data, "schannel: failed to setup memory allocation"); 521 if(!(connssl->ret_flags & ISC_RET_STREAM)) 522 failf(data, "schannel: failed to setup stream orientation"); 523 return CURLE_SSL_CONNECT_ERROR; 524 } 525 526 /* increment the reference counter of the credential/session handle */ 527 if(connssl->cred && connssl->ctxt) { 528 connssl->cred->refcount++; 529 infof(data, "schannel: incremented credential handle refcount = %d\n", 530 connssl->cred->refcount); 531 } 532 533 /* save the current session data for possible re-use */ 534 incache = !(Curl_ssl_getsessionid(conn, (void**)&old_cred, NULL)); 535 if(incache) { 536 if(old_cred != connssl->cred) { 537 infof(data, "schannel: old credential handle is stale, removing\n"); 538 Curl_ssl_delsessionid(conn, (void*)old_cred); 539 incache = FALSE; 540 } 541 } 542 if(!incache) { 543 retcode = Curl_ssl_addsessionid(conn, (void*)connssl->cred, 544 sizeof(struct curl_schannel_cred)); 545 if(retcode) { 546 failf(data, "schannel: failed to store credential handle"); 547 return retcode; 548 } 549 else { 550 connssl->cred->cached = TRUE; 551 infof(data, "schannel: stored credential handle in session cache\n"); 552 } 553 } 554 555 connssl->connecting_state = ssl_connect_done; 556 557 return CURLE_OK; 558} 559 560static CURLcode 561schannel_connect_common(struct connectdata *conn, int sockindex, 562 bool nonblocking, bool *done) 563{ 564 CURLcode retcode; 565 struct SessionHandle *data = conn->data; 566 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 567 curl_socket_t sockfd = conn->sock[sockindex]; 568 long timeout_ms; 569 int what; 570 571 /* check if the connection has already been established */ 572 if(ssl_connection_complete == connssl->state) { 573 *done = TRUE; 574 return CURLE_OK; 575 } 576 577 if(ssl_connect_1 == connssl->connecting_state) { 578 /* check out how much more time we're allowed */ 579 timeout_ms = Curl_timeleft(data, NULL, TRUE); 580 581 if(timeout_ms < 0) { 582 /* no need to continue if time already is up */ 583 failf(data, "SSL/TLS connection timeout"); 584 return CURLE_OPERATION_TIMEDOUT; 585 } 586 587 retcode = schannel_connect_step1(conn, sockindex); 588 if(retcode) 589 return retcode; 590 } 591 592 while(ssl_connect_2 == connssl->connecting_state || 593 ssl_connect_2_reading == connssl->connecting_state || 594 ssl_connect_2_writing == connssl->connecting_state) { 595 596 /* check out how much more time we're allowed */ 597 timeout_ms = Curl_timeleft(data, NULL, TRUE); 598 599 if(timeout_ms < 0) { 600 /* no need to continue if time already is up */ 601 failf(data, "SSL/TLS connection timeout"); 602 return CURLE_OPERATION_TIMEDOUT; 603 } 604 605 /* if ssl is expecting something, check if it's available. */ 606 if(connssl->connecting_state == ssl_connect_2_reading 607 || connssl->connecting_state == ssl_connect_2_writing) { 608 609 curl_socket_t writefd = ssl_connect_2_writing == 610 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; 611 curl_socket_t readfd = ssl_connect_2_reading == 612 connssl->connecting_state ? sockfd : CURL_SOCKET_BAD; 613 614 what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms); 615 if(what < 0) { 616 /* fatal error */ 617 failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO); 618 return CURLE_SSL_CONNECT_ERROR; 619 } 620 else if(0 == what) { 621 if(nonblocking) { 622 *done = FALSE; 623 return CURLE_OK; 624 } 625 else { 626 /* timeout */ 627 failf(data, "SSL/TLS connection timeout"); 628 return CURLE_OPERATION_TIMEDOUT; 629 } 630 } 631 /* socket is readable or writable */ 632 } 633 634 /* Run transaction, and return to the caller if it failed or if 635 * this connection is part of a multi handle and this loop would 636 * execute again. This permits the owner of a multi handle to 637 * abort a connection attempt before step2 has completed while 638 * ensuring that a client using select() or epoll() will always 639 * have a valid fdset to wait on. 640 */ 641 retcode = schannel_connect_step2(conn, sockindex); 642 if(retcode || (nonblocking && 643 (ssl_connect_2 == connssl->connecting_state || 644 ssl_connect_2_reading == connssl->connecting_state || 645 ssl_connect_2_writing == connssl->connecting_state))) 646 return retcode; 647 648 } /* repeat step2 until all transactions are done. */ 649 650 if(ssl_connect_3 == connssl->connecting_state) { 651 retcode = schannel_connect_step3(conn, sockindex); 652 if(retcode) 653 return retcode; 654 } 655 656 if(ssl_connect_done == connssl->connecting_state) { 657 connssl->state = ssl_connection_complete; 658 conn->recv[sockindex] = schannel_recv; 659 conn->send[sockindex] = schannel_send; 660 *done = TRUE; 661 } 662 else 663 *done = FALSE; 664 665 /* reset our connection state machine */ 666 connssl->connecting_state = ssl_connect_1; 667 668 return CURLE_OK; 669} 670 671static ssize_t 672schannel_send(struct connectdata *conn, int sockindex, 673 const void *buf, size_t len, CURLcode *err) 674{ 675 ssize_t written = -1; 676 size_t data_len = 0; 677 unsigned char *data = NULL; 678 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 679 SecBuffer outbuf[4]; 680 SecBufferDesc outbuf_desc; 681 SECURITY_STATUS sspi_status = SEC_E_OK; 682 CURLcode code; 683 684 /* check if the maximum stream sizes were queried */ 685 if(connssl->stream_sizes.cbMaximumMessage == 0) { 686 sspi_status = s_pSecFn->QueryContextAttributes( 687 &connssl->ctxt->ctxt_handle, 688 SECPKG_ATTR_STREAM_SIZES, 689 &connssl->stream_sizes); 690 if(sspi_status != SEC_E_OK) { 691 *err = CURLE_SEND_ERROR; 692 return -1; 693 } 694 } 695 696 /* check if the buffer is longer than the maximum message length */ 697 if(len > connssl->stream_sizes.cbMaximumMessage) { 698 *err = CURLE_SEND_ERROR; 699 return -1; 700 } 701 702 /* calculate the complete message length and allocate a buffer for it */ 703 data_len = connssl->stream_sizes.cbHeader + len + 704 connssl->stream_sizes.cbTrailer; 705 data = (unsigned char*) malloc(data_len); 706 if(data == NULL) { 707 *err = CURLE_OUT_OF_MEMORY; 708 return -1; 709 } 710 711 /* setup output buffers (header, data, trailer, empty) */ 712 InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER, 713 data, connssl->stream_sizes.cbHeader); 714 InitSecBuffer(&outbuf[1], SECBUFFER_DATA, 715 data + connssl->stream_sizes.cbHeader, curlx_uztoul(len)); 716 InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER, 717 data + connssl->stream_sizes.cbHeader + len, 718 connssl->stream_sizes.cbTrailer); 719 InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0); 720 InitSecBufferDesc(&outbuf_desc, outbuf, 4); 721 722 /* copy data into output buffer */ 723 memcpy(outbuf[1].pvBuffer, buf, len); 724 725 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */ 726 sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0, 727 &outbuf_desc, 0); 728 729 /* check if the message was encrypted */ 730 if(sspi_status == SEC_E_OK) { 731 written = 0; 732 733 /* send the encrypted message including header, data and trailer */ 734 len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer; 735 736 /* 737 It's important to send the full message which includes the header, 738 encrypted payload, and trailer. Until the client receives all the 739 data a coherent message has not been delivered and the client 740 can't read any of it. 741 742 If we wanted to buffer the unwritten encrypted bytes, we would 743 tell the client that all data it has requested to be sent has been 744 sent. The unwritten encrypted bytes would be the first bytes to 745 send on the next invocation. 746 Here's the catch with this - if we tell the client that all the 747 bytes have been sent, will the client call this method again to 748 send the buffered data? Looking at who calls this function, it 749 seems the answer is NO. 750 */ 751 752 /* send entire message or fail */ 753 while(len > (size_t)written) { 754 ssize_t this_write; 755 long timeleft; 756 int what; 757 758 this_write = 0; 759 760 timeleft = Curl_timeleft(conn->data, NULL, TRUE); 761 if(timeleft < 0) { 762 /* we already got the timeout */ 763 failf(conn->data, "schannel: timed out sending data " 764 "(bytes sent: %zd)", written); 765 *err = CURLE_OPERATION_TIMEDOUT; 766 written = -1; 767 break; 768 } 769 770 what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex], 771 timeleft); 772 if(what < 0) { 773 /* fatal error */ 774 failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO); 775 *err = CURLE_SEND_ERROR; 776 written = -1; 777 break; 778 } 779 else if(0 == what) { 780 failf(conn->data, "schannel: timed out sending data " 781 "(bytes sent: %zd)", written); 782 *err = CURLE_OPERATION_TIMEDOUT; 783 written = -1; 784 break; 785 } 786 /* socket is writable */ 787 788 code = Curl_write_plain(conn, conn->sock[sockindex], data + written, 789 len - written, &this_write); 790 if(code == CURLE_AGAIN) 791 continue; 792 else if(code != CURLE_OK) { 793 *err = code; 794 written = -1; 795 break; 796 } 797 798 written += this_write; 799 } 800 } 801 else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) { 802 *err = CURLE_OUT_OF_MEMORY; 803 } 804 else{ 805 *err = CURLE_SEND_ERROR; 806 } 807 808 Curl_safefree(data); 809 810 if(len == (size_t)written) 811 /* Encrypted message including header, data and trailer entirely sent. 812 The return value is the number of unencrypted bytes that were sent. */ 813 written = outbuf[1].cbBuffer; 814 815 return written; 816} 817 818static ssize_t 819schannel_recv(struct connectdata *conn, int sockindex, 820 char *buf, size_t len, CURLcode *err) 821{ 822 size_t size = 0; 823 ssize_t nread = 0, ret = -1; 824 CURLcode retcode; 825 struct SessionHandle *data = conn->data; 826 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 827 bool done = FALSE; 828 SecBuffer inbuf[4]; 829 SecBufferDesc inbuf_desc; 830 SECURITY_STATUS sspi_status = SEC_E_OK; 831 832 infof(data, "schannel: client wants to read %zu bytes\n", len); 833 *err = CURLE_OK; 834 835 /* buffer to store previously received and decrypted data */ 836 if(connssl->decdata_buffer == NULL) { 837 connssl->decdata_offset = 0; 838 connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE; 839 connssl->decdata_buffer = malloc(connssl->decdata_length); 840 if(connssl->decdata_buffer == NULL) { 841 failf(data, "schannel: unable to allocate memory"); 842 *err = CURLE_OUT_OF_MEMORY; 843 return -1; 844 } 845 } 846 847 /* increase buffer in order to fit the requested amount of data */ 848 while(connssl->encdata_length - connssl->encdata_offset < 849 CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) { 850 /* increase internal encrypted data buffer */ 851 connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR; 852 connssl->encdata_buffer = realloc(connssl->encdata_buffer, 853 connssl->encdata_length); 854 855 if(connssl->encdata_buffer == NULL) { 856 failf(data, "schannel: unable to re-allocate memory"); 857 *err = CURLE_OUT_OF_MEMORY; 858 return -1; 859 } 860 } 861 862 /* read encrypted data from socket */ 863 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", 864 connssl->encdata_offset, connssl->encdata_length); 865 size = connssl->encdata_length - connssl->encdata_offset; 866 if(size > 0) { 867 *err = Curl_read_plain(conn->sock[sockindex], 868 (char *) (connssl->encdata_buffer + connssl->encdata_offset), 869 size, &nread); 870 /* check for received data */ 871 if(*err != CURLE_OK) 872 ret = -1; 873 else { 874 if(nread > 0) 875 /* increase encrypted data buffer offset */ 876 connssl->encdata_offset += nread; 877 ret = nread; 878 } 879 infof(data, "schannel: encrypted data got %zd\n", ret); 880 } 881 882 infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n", 883 connssl->encdata_offset, connssl->encdata_length); 884 885 /* check if we still have some data in our buffers */ 886 while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK && 887 connssl->decdata_offset < len) { 888 /* prepare data buffer for DecryptMessage call */ 889 InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer, 890 curlx_uztoul(connssl->encdata_offset)); 891 892 /* we need 3 more empty input buffers for possible output */ 893 InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0); 894 InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0); 895 InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0); 896 897 InitSecBufferDesc(&inbuf_desc, inbuf, 4); 898 899 /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */ 900 sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle, 901 &inbuf_desc, 0, NULL); 902 903 /* check if we need more data */ 904 if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) { 905 infof(data, "schannel: failed to decrypt data, need more data\n"); 906 *err = CURLE_AGAIN; 907 return -1; 908 } 909 910 /* check if everything went fine (server may want to renegotiate 911 context) */ 912 if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE || 913 sspi_status == SEC_I_CONTEXT_EXPIRED) { 914 /* check for successfully decrypted data */ 915 if(inbuf[1].BufferType == SECBUFFER_DATA) { 916 infof(data, "schannel: decrypted data length: %lu\n", 917 inbuf[1].cbBuffer); 918 919 /* increase buffer in order to fit the received amount of data */ 920 size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ? 921 inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE; 922 while(connssl->decdata_length - connssl->decdata_offset < size || 923 connssl->decdata_length < len) { 924 /* increase internal decrypted data buffer */ 925 connssl->decdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR; 926 connssl->decdata_buffer = realloc(connssl->decdata_buffer, 927 connssl->decdata_length); 928 929 if(connssl->decdata_buffer == NULL) { 930 failf(data, "schannel: unable to re-allocate memory"); 931 *err = CURLE_OUT_OF_MEMORY; 932 return -1; 933 } 934 } 935 936 /* copy decrypted data to internal buffer */ 937 size = inbuf[1].cbBuffer; 938 if(size > 0) { 939 memcpy(connssl->decdata_buffer + connssl->decdata_offset, 940 inbuf[1].pvBuffer, size); 941 connssl->decdata_offset += size; 942 } 943 944 infof(data, "schannel: decrypted data added: %zu\n", size); 945 infof(data, "schannel: decrypted data cached: offset %zu length %zu\n", 946 connssl->decdata_offset, connssl->decdata_length); 947 } 948 949 /* check for remaining encrypted data */ 950 if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) { 951 infof(data, "schannel: encrypted data length: %lu\n", 952 inbuf[3].cbBuffer); 953 954 /* check if the remaining data is less than the total amount 955 * and therefore begins after the already processed data 956 */ 957 if(connssl->encdata_offset > inbuf[3].cbBuffer) { 958 /* move remaining encrypted data forward to the beginning of 959 buffer */ 960 memmove(connssl->encdata_buffer, 961 (connssl->encdata_buffer + connssl->encdata_offset) - 962 inbuf[3].cbBuffer, inbuf[3].cbBuffer); 963 connssl->encdata_offset = inbuf[3].cbBuffer; 964 } 965 966 infof(data, "schannel: encrypted data cached: offset %zu length %zu\n", 967 connssl->encdata_offset, connssl->encdata_length); 968 } 969 else{ 970 /* reset encrypted buffer offset, because there is no data remaining */ 971 connssl->encdata_offset = 0; 972 } 973 } 974 975 /* check if server wants to renegotiate the connection context */ 976 if(sspi_status == SEC_I_RENEGOTIATE) { 977 infof(data, "schannel: remote party requests SSL/TLS renegotiation\n"); 978 979 /* begin renegotiation */ 980 infof(data, "schannel: renegotiating SSL/TLS connection\n"); 981 connssl->state = ssl_connection_negotiating; 982 connssl->connecting_state = ssl_connect_2_writing; 983 retcode = schannel_connect_common(conn, sockindex, FALSE, &done); 984 if(retcode) 985 *err = retcode; 986 else { 987 infof(data, "schannel: SSL/TLS connection renegotiated\n"); 988 /* now retry receiving data */ 989 return schannel_recv(conn, sockindex, buf, len, err); 990 } 991 } 992 } 993 994 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n", 995 connssl->decdata_offset, connssl->decdata_length); 996 997 /* copy requested decrypted data to supplied buffer */ 998 size = len < connssl->decdata_offset ? len : connssl->decdata_offset; 999 if(size > 0) { 1000 memcpy(buf, connssl->decdata_buffer, size); 1001 ret = size; 1002 1003 /* move remaining decrypted data forward to the beginning of buffer */ 1004 memmove(connssl->decdata_buffer, connssl->decdata_buffer + size, 1005 connssl->decdata_offset - size); 1006 connssl->decdata_offset -= size; 1007 1008 infof(data, "schannel: decrypted data returned %zd\n", size); 1009 infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n", 1010 connssl->decdata_offset, connssl->decdata_length); 1011 } 1012 1013 /* check if the server closed the connection */ 1014 if(ret <= 0 && ( /* special check for Windows 2000 Professional */ 1015 sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK && 1016 connssl->encdata_offset > 0 && connssl->encdata_buffer[0] == 0x15))) { 1017 infof(data, "schannel: server closed the connection\n"); 1018 *err = CURLE_OK; 1019 return 0; 1020 } 1021 1022 /* check if something went wrong and we need to return an error */ 1023 if(ret < 0 && sspi_status != SEC_E_OK) { 1024 infof(data, "schannel: failed to read data from server: %s\n", 1025 Curl_sspi_strerror(conn, sspi_status)); 1026 *err = CURLE_RECV_ERROR; 1027 return -1; 1028 } 1029 1030 return ret; 1031} 1032 1033CURLcode 1034Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex, 1035 bool *done) 1036{ 1037 return schannel_connect_common(conn, sockindex, TRUE, done); 1038} 1039 1040CURLcode 1041Curl_schannel_connect(struct connectdata *conn, int sockindex) 1042{ 1043 CURLcode retcode; 1044 bool done = FALSE; 1045 1046 retcode = schannel_connect_common(conn, sockindex, FALSE, &done); 1047 if(retcode) 1048 return retcode; 1049 1050 DEBUGASSERT(done); 1051 1052 return CURLE_OK; 1053} 1054 1055bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex) 1056{ 1057 const struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 1058 1059 if(connssl->use) /* SSL/TLS is in use */ 1060 return (connssl->encdata_offset > 0 || 1061 connssl->decdata_offset > 0 ) ? TRUE : FALSE; 1062 else 1063 return FALSE; 1064} 1065 1066void Curl_schannel_close(struct connectdata *conn, int sockindex) 1067{ 1068 if(conn->ssl[sockindex].use) 1069 /* if the SSL/TLS channel hasn't been shut down yet, do that now. */ 1070 Curl_ssl_shutdown(conn, sockindex); 1071} 1072 1073int Curl_schannel_shutdown(struct connectdata *conn, int sockindex) 1074{ 1075 /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx 1076 * Shutting Down an Schannel Connection 1077 */ 1078 struct SessionHandle *data = conn->data; 1079 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 1080 1081 infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n", 1082 conn->host.name, conn->remote_port); 1083 1084 if(connssl->cred && connssl->ctxt) { 1085 SecBufferDesc BuffDesc; 1086 SecBuffer Buffer; 1087 SECURITY_STATUS sspi_status; 1088 SecBuffer outbuf; 1089 SecBufferDesc outbuf_desc; 1090 CURLcode code; 1091 TCHAR *host_name; 1092 DWORD dwshut = SCHANNEL_SHUTDOWN; 1093 1094 InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut)); 1095 InitSecBufferDesc(&BuffDesc, &Buffer, 1); 1096 1097 sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle, 1098 &BuffDesc); 1099 1100 if(sspi_status != SEC_E_OK) 1101 failf(data, "schannel: ApplyControlToken failure: %s", 1102 Curl_sspi_strerror(conn, sspi_status)); 1103 1104 host_name = Curl_convert_UTF8_to_tchar(conn->host.name); 1105 if(!host_name) 1106 return CURLE_OUT_OF_MEMORY; 1107 1108 /* setup output buffer */ 1109 InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0); 1110 InitSecBufferDesc(&outbuf_desc, &outbuf, 1); 1111 1112 sspi_status = s_pSecFn->InitializeSecurityContext( 1113 &connssl->cred->cred_handle, 1114 &connssl->ctxt->ctxt_handle, 1115 host_name, 1116 connssl->req_flags, 1117 0, 1118 0, 1119 NULL, 1120 0, 1121 &connssl->ctxt->ctxt_handle, 1122 &outbuf_desc, 1123 &connssl->ret_flags, 1124 &connssl->ctxt->time_stamp); 1125 1126 Curl_unicodefree(host_name); 1127 1128 if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) { 1129 /* send close message which is in output buffer */ 1130 ssize_t written; 1131 code = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer, 1132 outbuf.cbBuffer, &written); 1133 1134 s_pSecFn->FreeContextBuffer(outbuf.pvBuffer); 1135 if((code != CURLE_OK) || (outbuf.cbBuffer != (size_t)written)) { 1136 infof(data, "schannel: failed to send close msg: %s" 1137 " (bytes written: %zd)\n", curl_easy_strerror(code), written); 1138 } 1139 } 1140 1141 /* free SSPI Schannel API security context handle */ 1142 if(connssl->ctxt) { 1143 infof(data, "schannel: clear security context handle\n"); 1144 s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle); 1145 Curl_safefree(connssl->ctxt); 1146 } 1147 1148 /* free SSPI Schannel API credential handle */ 1149 if(connssl->cred) { 1150 /* decrement the reference counter of the credential/session handle */ 1151 if(connssl->cred->refcount > 0) { 1152 connssl->cred->refcount--; 1153 infof(data, "schannel: decremented credential handle refcount = %d\n", 1154 connssl->cred->refcount); 1155 } 1156 1157 /* if the handle was not cached and the refcount is zero */ 1158 if(!connssl->cred->cached && connssl->cred->refcount == 0) { 1159 infof(data, "schannel: clear credential handle\n"); 1160 s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle); 1161 Curl_safefree(connssl->cred); 1162 } 1163 } 1164 } 1165 1166 /* free internal buffer for received encrypted data */ 1167 if(connssl->encdata_buffer != NULL) { 1168 Curl_safefree(connssl->encdata_buffer); 1169 connssl->encdata_length = 0; 1170 connssl->encdata_offset = 0; 1171 } 1172 1173 /* free internal buffer for received decrypted data */ 1174 if(connssl->decdata_buffer != NULL) { 1175 Curl_safefree(connssl->decdata_buffer); 1176 connssl->decdata_length = 0; 1177 connssl->decdata_offset = 0; 1178 } 1179 1180 return CURLE_OK; 1181} 1182 1183void Curl_schannel_session_free(void *ptr) 1184{ 1185 struct curl_schannel_cred *cred = ptr; 1186 1187 if(cred && cred->cached && cred->refcount == 0) { 1188 s_pSecFn->FreeCredentialsHandle(&cred->cred_handle); 1189 Curl_safefree(cred); 1190 } 1191} 1192 1193int Curl_schannel_init(void) 1194{ 1195 return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0); 1196} 1197 1198void Curl_schannel_cleanup(void) 1199{ 1200 Curl_sspi_global_cleanup(); 1201} 1202 1203size_t Curl_schannel_version(char *buffer, size_t size) 1204{ 1205 size = snprintf(buffer, size, "WinSSL"); 1206 1207 return size; 1208} 1209 1210#ifdef _WIN32_WCE 1211static CURLcode verify_certificate(struct connectdata *conn, int sockindex) 1212{ 1213 SECURITY_STATUS status; 1214 struct SessionHandle *data = conn->data; 1215 struct ssl_connect_data *connssl = &conn->ssl[sockindex]; 1216 CURLcode result = CURLE_OK; 1217 CERT_CONTEXT *pCertContextServer = NULL; 1218 const CERT_CHAIN_CONTEXT *pChainContext = NULL; 1219 1220 status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle, 1221 SECPKG_ATTR_REMOTE_CERT_CONTEXT, 1222 &pCertContextServer); 1223 1224 if((status != SEC_E_OK) || (pCertContextServer == NULL)) { 1225 failf(data, "schannel: Failed to read remote certificate context: %s", 1226 Curl_sspi_strerror(conn, status)); 1227 result = CURLE_PEER_FAILED_VERIFICATION; 1228 } 1229 1230 if(result == CURLE_OK) { 1231 CERT_CHAIN_PARA ChainPara; 1232 memset(&ChainPara, 0, sizeof(ChainPara)); 1233 ChainPara.cbSize = sizeof(ChainPara); 1234 1235 if(!CertGetCertificateChain(NULL, 1236 pCertContextServer, 1237 NULL, 1238 pCertContextServer->hCertStore, 1239 &ChainPara, 1240 0, 1241 NULL, 1242 &pChainContext)) { 1243 failf(data, "schannel: CertGetCertificateChain failed: %s", 1244 Curl_sspi_strerror(conn, GetLastError())); 1245 pChainContext = NULL; 1246 result = CURLE_PEER_FAILED_VERIFICATION; 1247 } 1248 1249 if(result == CURLE_OK) { 1250 CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0]; 1251 DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED| 1252 CERT_TRUST_REVOCATION_STATUS_UNKNOWN); 1253 dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus; 1254 if(dwTrustErrorMask) { 1255 if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN) 1256 failf(data, "schannel: CertGetCertificateChain trust error" 1257 " CERT_TRUST_IS_PARTIAL_CHAIN"); 1258 if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT) 1259 failf(data, "schannel: CertGetCertificateChain trust error" 1260 " CERT_TRUST_IS_UNTRUSTED_ROOT"); 1261 if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID) 1262 failf(data, "schannel: CertGetCertificateChain trust error" 1263 " CERT_TRUST_IS_NOT_TIME_VALID"); 1264 failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x", 1265 dwTrustErrorMask); 1266 result = CURLE_PEER_FAILED_VERIFICATION; 1267 } 1268 } 1269 } 1270 1271 if(result == CURLE_OK) { 1272 if(data->set.ssl.verifyhost) { 1273 TCHAR cert_hostname_buff[128]; 1274 xcharp_u hostname; 1275 xcharp_u cert_hostname; 1276 DWORD len; 1277 1278 cert_hostname.const_tchar_ptr = cert_hostname_buff; 1279 hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name); 1280 1281 len = CertGetNameString(pCertContextServer, 1282 CERT_NAME_DNS_TYPE, 1283 0, 1284 NULL, 1285 cert_hostname.tchar_ptr, 1286 128); 1287 if(len > 0 && *cert_hostname.tchar_ptr == '*') { 1288 /* this is a wildcard cert. try matching the last len - 1 chars */ 1289 int hostname_len = strlen(conn->host.name); 1290 cert_hostname.tchar_ptr++; 1291 if(_tcsicmp(cert_hostname.const_tchar_ptr, 1292 hostname.const_tchar_ptr + hostname_len - len + 2) != 0) 1293 result = CURLE_PEER_FAILED_VERIFICATION; 1294 } 1295 else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr, 1296 cert_hostname.const_tchar_ptr) != 0) { 1297 result = CURLE_PEER_FAILED_VERIFICATION; 1298 } 1299 if(result == CURLE_PEER_FAILED_VERIFICATION) { 1300 char *_cert_hostname; 1301 _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr); 1302 failf(data, "schannel: CertGetNameString() certificate hostname " 1303 "(%s) did not match connection (%s)", 1304 _cert_hostname, conn->host.name); 1305 Curl_unicodefree(_cert_hostname); 1306 } 1307 Curl_unicodefree(hostname.tchar_ptr); 1308 } 1309 } 1310 1311 if(pChainContext) 1312 CertFreeCertificateChain(pChainContext); 1313 1314 if(pCertContextServer) 1315 CertFreeCertificateContext(pCertContextServer); 1316 1317 return result; 1318} 1319#endif /* _WIN32_WCE */ 1320 1321#endif /* USE_SCHANNEL */ 1322