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