schannel.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 2012 - 2016, Marc Hoersken, <info@marc-hoersken.de>
00009  * Copyright (C) 2012, Mark Salisbury, <mark.salisbury@hp.com>
00010  * Copyright (C) 2012 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00011  *
00012  * This software is licensed as described in the file COPYING, which
00013  * you should have received as part of this distribution. The terms
00014  * are also available at https://curl.haxx.se/docs/copyright.html.
00015  *
00016  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00017  * copies of the Software, and permit persons to whom the Software is
00018  * furnished to do so, under the terms of the COPYING file.
00019  *
00020  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00021  * KIND, either express or implied.
00022  *
00023  ***************************************************************************/
00024 
00025 /*
00026  * Source file for all SChannel-specific code for the TLS/SSL layer. No code
00027  * but vtls.c should ever call or use these functions.
00028  *
00029  */
00030 
00031 /*
00032  * Based upon the PolarSSL implementation in polarssl.c and polarssl.h:
00033  *   Copyright (C) 2010, 2011, Hoi-Ho Chan, <hoiho.chan@gmail.com>
00034  *
00035  * Based upon the CyaSSL implementation in cyassl.c and cyassl.h:
00036  *   Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
00037  *
00038  * Thanks for code and inspiration!
00039  */
00040 
00041 #include "curl_setup.h"
00042 
00043 #ifdef USE_SCHANNEL
00044 
00045 #ifndef USE_WINDOWS_SSPI
00046 #  error "Can't compile SCHANNEL support without SSPI."
00047 #endif
00048 
00049 #include "curl_sspi.h"
00050 #include "schannel.h"
00051 #include "vtls.h"
00052 #include "sendf.h"
00053 #include "connect.h" /* for the connect timeout */
00054 #include "strerror.h"
00055 #include "select.h" /* for the socket readyness */
00056 #include "inet_pton.h" /* for IP addr SNI check */
00057 #include "curl_multibyte.h"
00058 #include "warnless.h"
00059 #include "x509asn1.h"
00060 #include "curl_printf.h"
00061 #include "system_win32.h"
00062 #include "hostcheck.h"
00063 
00064  /* The last #include file should be: */
00065 #include "curl_memory.h"
00066 #include "memdebug.h"
00067 
00068 /* ALPN requires version 8.1 of the Windows SDK, which was
00069    shipped with Visual Studio 2013, aka _MSC_VER 1800:
00070 
00071    https://technet.microsoft.com/en-us/library/hh831771%28v=ws.11%29.aspx
00072 */
00073 #if defined(_MSC_VER) && (_MSC_VER >= 1800) && !defined(_USING_V110_SDK71_)
00074 #  define HAS_ALPN 1
00075 #endif
00076 
00077 /* Uncomment to force verbose output
00078  * #define infof(x, y, ...) printf(y, __VA_ARGS__)
00079  * #define failf(x, y, ...) printf(y, __VA_ARGS__)
00080  */
00081 
00082 static Curl_recv schannel_recv;
00083 static Curl_send schannel_send;
00084 
00085 #ifdef _WIN32_WCE
00086 static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
00087 #endif
00088 
00089 static void InitSecBuffer(SecBuffer *buffer, unsigned long BufType,
00090                           void *BufDataPtr, unsigned long BufByteSize)
00091 {
00092   buffer->cbBuffer = BufByteSize;
00093   buffer->BufferType = BufType;
00094   buffer->pvBuffer = BufDataPtr;
00095 }
00096 
00097 static void InitSecBufferDesc(SecBufferDesc *desc, SecBuffer *BufArr,
00098                               unsigned long NumArrElem)
00099 {
00100   desc->ulVersion = SECBUFFER_VERSION;
00101   desc->pBuffers = BufArr;
00102   desc->cBuffers = NumArrElem;
00103 }
00104 
00105 static CURLcode
00106 schannel_connect_step1(struct connectdata *conn, int sockindex)
00107 {
00108   ssize_t written = -1;
00109   struct Curl_easy *data = conn->data;
00110   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
00111   SecBuffer outbuf;
00112   SecBufferDesc outbuf_desc;
00113   SecBuffer inbuf;
00114   SecBufferDesc inbuf_desc;
00115 #ifdef HAS_ALPN
00116   unsigned char alpn_buffer[128];
00117 #endif
00118   SCHANNEL_CRED schannel_cred;
00119   SECURITY_STATUS sspi_status = SEC_E_OK;
00120   struct curl_schannel_cred *old_cred = NULL;
00121   struct in_addr addr;
00122 #ifdef ENABLE_IPV6
00123   struct in6_addr addr6;
00124 #endif
00125   TCHAR *host_name;
00126   CURLcode result;
00127   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
00128     conn->host.name;
00129 
00130   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 1/3)\n",
00131         hostname, conn->remote_port);
00132 
00133 #ifdef HAS_ALPN
00134   /* ALPN is only supported on Windows 8.1 / Server 2012 R2 and above.
00135      Also it doesn't seem to be supported for Wine, see curl bug #983. */
00136   connssl->use_alpn = conn->bits.tls_enable_alpn &&
00137                       !GetProcAddress(GetModuleHandleA("ntdll"),
00138                                       "wine_get_version") &&
00139                       Curl_verify_windows_version(6, 3, PLATFORM_WINNT,
00140                                                   VERSION_GREATER_THAN_EQUAL);
00141 #else
00142   connssl->use_alpn = false;
00143 #endif
00144 
00145   connssl->cred = NULL;
00146 
00147   /* check for an existing re-usable credential handle */
00148   if(data->set.general_ssl.sessionid) {
00149     Curl_ssl_sessionid_lock(conn);
00150     if(!Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL, sockindex)) {
00151       connssl->cred = old_cred;
00152       infof(data, "schannel: re-using existing credential handle\n");
00153 
00154       /* increment the reference counter of the credential/session handle */
00155       connssl->cred->refcount++;
00156       infof(data, "schannel: incremented credential handle refcount = %d\n",
00157             connssl->cred->refcount);
00158     }
00159     Curl_ssl_sessionid_unlock(conn);
00160   }
00161 
00162   if(!connssl->cred) {
00163     /* setup Schannel API options */
00164     memset(&schannel_cred, 0, sizeof(schannel_cred));
00165     schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
00166 
00167     if(conn->ssl_config.verifypeer) {
00168 #ifdef _WIN32_WCE
00169       /* certificate validation on CE doesn't seem to work right; we'll
00170          do it following a more manual process. */
00171       schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
00172         SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
00173         SCH_CRED_IGNORE_REVOCATION_OFFLINE;
00174 #else
00175       schannel_cred.dwFlags = SCH_CRED_AUTO_CRED_VALIDATION;
00176       /* TODO s/data->set.ssl.no_revoke/SSL_SET_OPTION(no_revoke)/g */
00177       if(data->set.ssl.no_revoke)
00178         schannel_cred.dwFlags |= SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
00179                                  SCH_CRED_IGNORE_REVOCATION_OFFLINE;
00180       else
00181         schannel_cred.dwFlags |= SCH_CRED_REVOCATION_CHECK_CHAIN;
00182 #endif
00183       if(data->set.ssl.no_revoke)
00184         infof(data, "schannel: disabled server certificate revocation "
00185                     "checks\n");
00186       else
00187         infof(data, "schannel: checking server certificate revocation\n");
00188     }
00189     else {
00190       schannel_cred.dwFlags = SCH_CRED_MANUAL_CRED_VALIDATION |
00191         SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
00192         SCH_CRED_IGNORE_REVOCATION_OFFLINE;
00193       infof(data, "schannel: disabled server certificate revocation checks\n");
00194     }
00195 
00196     if(!conn->ssl_config.verifyhost) {
00197       schannel_cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
00198       infof(data, "schannel: verifyhost setting prevents Schannel from "
00199             "comparing the supplied target name with the subject "
00200             "names in server certificates. Also disables SNI.\n");
00201     }
00202 
00203     switch(conn->ssl_config.version) {
00204     case CURL_SSLVERSION_DEFAULT:
00205     case CURL_SSLVERSION_TLSv1:
00206       schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT |
00207         SP_PROT_TLS1_1_CLIENT |
00208         SP_PROT_TLS1_2_CLIENT;
00209       break;
00210     case CURL_SSLVERSION_TLSv1_0:
00211       schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_0_CLIENT;
00212       break;
00213     case CURL_SSLVERSION_TLSv1_1:
00214       schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_1_CLIENT;
00215       break;
00216     case CURL_SSLVERSION_TLSv1_2:
00217       schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT;
00218       break;
00219     case CURL_SSLVERSION_TLSv1_3:
00220       failf(data, "Schannel: TLS 1.3 is not yet supported");
00221       return CURLE_SSL_CONNECT_ERROR;
00222     case CURL_SSLVERSION_SSLv3:
00223       schannel_cred.grbitEnabledProtocols = SP_PROT_SSL3_CLIENT;
00224       break;
00225     case CURL_SSLVERSION_SSLv2:
00226       schannel_cred.grbitEnabledProtocols = SP_PROT_SSL2_CLIENT;
00227       break;
00228     default:
00229       failf(data, "Unrecognized parameter passed via CURLOPT_SSLVERSION");
00230       return CURLE_SSL_CONNECT_ERROR;
00231     }
00232 
00233     /* allocate memory for the re-usable credential handle */
00234     connssl->cred = (struct curl_schannel_cred *)
00235       malloc(sizeof(struct curl_schannel_cred));
00236     if(!connssl->cred) {
00237       failf(data, "schannel: unable to allocate memory");
00238       return CURLE_OUT_OF_MEMORY;
00239     }
00240     memset(connssl->cred, 0, sizeof(struct curl_schannel_cred));
00241     connssl->cred->refcount = 1;
00242 
00243     /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa374716.aspx
00244        */
00245     sspi_status =
00246       s_pSecFn->AcquireCredentialsHandle(NULL, (TCHAR *)UNISP_NAME,
00247                                          SECPKG_CRED_OUTBOUND, NULL,
00248                                          &schannel_cred, NULL, NULL,
00249                                          &connssl->cred->cred_handle,
00250                                          &connssl->cred->time_stamp);
00251 
00252     if(sspi_status != SEC_E_OK) {
00253       if(sspi_status == SEC_E_WRONG_PRINCIPAL)
00254         failf(data, "schannel: SNI or certificate check failed: %s",
00255               Curl_sspi_strerror(conn, sspi_status));
00256       else
00257         failf(data, "schannel: AcquireCredentialsHandle failed: %s",
00258               Curl_sspi_strerror(conn, sspi_status));
00259       Curl_safefree(connssl->cred);
00260       return CURLE_SSL_CONNECT_ERROR;
00261     }
00262   }
00263 
00264   /* Warn if SNI is disabled due to use of an IP address */
00265   if(Curl_inet_pton(AF_INET, hostname, &addr)
00266 #ifdef ENABLE_IPV6
00267      || Curl_inet_pton(AF_INET6, hostname, &addr6)
00268 #endif
00269     ) {
00270     infof(data, "schannel: using IP address, SNI is not supported by OS.\n");
00271   }
00272 
00273 #ifdef HAS_ALPN
00274   if(connssl->use_alpn) {
00275     int cur = 0;
00276     int list_start_index = 0;
00277     unsigned int *extension_len = NULL;
00278     unsigned short* list_len = NULL;
00279 
00280     /* The first four bytes will be an unsigned int indicating number
00281        of bytes of data in the rest of the the buffer. */
00282     extension_len = (unsigned int *)(&alpn_buffer[cur]);
00283     cur += sizeof(unsigned int);
00284 
00285     /* The next four bytes are an indicator that this buffer will contain
00286        ALPN data, as opposed to NPN, for example. */
00287     *(unsigned int *)&alpn_buffer[cur] =
00288       SecApplicationProtocolNegotiationExt_ALPN;
00289     cur += sizeof(unsigned int);
00290 
00291     /* The next two bytes will be an unsigned short indicating the number
00292        of bytes used to list the preferred protocols. */
00293     list_len = (unsigned short*)(&alpn_buffer[cur]);
00294     cur += sizeof(unsigned short);
00295 
00296     list_start_index = cur;
00297 
00298 #ifdef USE_NGHTTP2
00299     if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
00300       memcpy(&alpn_buffer[cur], NGHTTP2_PROTO_ALPN, NGHTTP2_PROTO_ALPN_LEN);
00301       cur += NGHTTP2_PROTO_ALPN_LEN;
00302       infof(data, "schannel: ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
00303     }
00304 #endif
00305 
00306     alpn_buffer[cur++] = ALPN_HTTP_1_1_LENGTH;
00307     memcpy(&alpn_buffer[cur], ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH);
00308     cur += ALPN_HTTP_1_1_LENGTH;
00309     infof(data, "schannel: ALPN, offering %s\n", ALPN_HTTP_1_1);
00310 
00311     *list_len = curlx_uitous(cur - list_start_index);
00312     *extension_len = *list_len + sizeof(unsigned int) + sizeof(unsigned short);
00313 
00314     InitSecBuffer(&inbuf, SECBUFFER_APPLICATION_PROTOCOLS, alpn_buffer, cur);
00315     InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
00316   }
00317   else
00318   {
00319     InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
00320     InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
00321   }
00322 #else /* HAS_ALPN */
00323   InitSecBuffer(&inbuf, SECBUFFER_EMPTY, NULL, 0);
00324   InitSecBufferDesc(&inbuf_desc, &inbuf, 1);
00325 #endif
00326 
00327   /* setup output buffer */
00328   InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
00329   InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
00330 
00331   /* setup request flags */
00332   connssl->req_flags = ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
00333     ISC_REQ_CONFIDENTIALITY | ISC_REQ_ALLOCATE_MEMORY |
00334     ISC_REQ_STREAM;
00335 
00336   /* allocate memory for the security context handle */
00337   connssl->ctxt = (struct curl_schannel_ctxt *)
00338     malloc(sizeof(struct curl_schannel_ctxt));
00339   if(!connssl->ctxt) {
00340     failf(data, "schannel: unable to allocate memory");
00341     return CURLE_OUT_OF_MEMORY;
00342   }
00343   memset(connssl->ctxt, 0, sizeof(struct curl_schannel_ctxt));
00344 
00345   host_name = Curl_convert_UTF8_to_tchar(hostname);
00346   if(!host_name)
00347     return CURLE_OUT_OF_MEMORY;
00348 
00349   /* Schannel InitializeSecurityContext:
00350      https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
00351 
00352      At the moment we don't pass inbuf unless we're using ALPN since we only
00353      use it for that, and Wine (for which we currently disable ALPN) is giving
00354      us problems with inbuf regardless. https://github.com/curl/curl/issues/983
00355   */
00356   sspi_status = s_pSecFn->InitializeSecurityContext(
00357     &connssl->cred->cred_handle, NULL, host_name, connssl->req_flags, 0, 0,
00358     (connssl->use_alpn ? &inbuf_desc : NULL),
00359     0, &connssl->ctxt->ctxt_handle,
00360     &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
00361 
00362   Curl_unicodefree(host_name);
00363 
00364   if(sspi_status != SEC_I_CONTINUE_NEEDED) {
00365     if(sspi_status == SEC_E_WRONG_PRINCIPAL)
00366       failf(data, "schannel: SNI or certificate check failed: %s",
00367             Curl_sspi_strerror(conn, sspi_status));
00368     else
00369       failf(data, "schannel: initial InitializeSecurityContext failed: %s",
00370             Curl_sspi_strerror(conn, sspi_status));
00371     Curl_safefree(connssl->ctxt);
00372     return CURLE_SSL_CONNECT_ERROR;
00373   }
00374 
00375   infof(data, "schannel: sending initial handshake data: "
00376         "sending %lu bytes...\n", outbuf.cbBuffer);
00377 
00378   /* send initial handshake data which is now stored in output buffer */
00379   result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
00380                             outbuf.cbBuffer, &written);
00381   s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
00382   if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
00383     failf(data, "schannel: failed to send initial handshake data: "
00384           "sent %zd of %lu bytes", written, outbuf.cbBuffer);
00385     return CURLE_SSL_CONNECT_ERROR;
00386   }
00387 
00388   infof(data, "schannel: sent initial handshake data: "
00389         "sent %zd bytes\n", written);
00390 
00391   connssl->recv_unrecoverable_err = CURLE_OK;
00392   connssl->recv_sspi_close_notify = false;
00393   connssl->recv_connection_closed = false;
00394 
00395   /* continue to second handshake step */
00396   connssl->connecting_state = ssl_connect_2;
00397 
00398   return CURLE_OK;
00399 }
00400 
00401 static CURLcode
00402 schannel_connect_step2(struct connectdata *conn, int sockindex)
00403 {
00404   int i;
00405   ssize_t nread = -1, written = -1;
00406   struct Curl_easy *data = conn->data;
00407   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
00408   unsigned char *reallocated_buffer;
00409   size_t reallocated_length;
00410   SecBuffer outbuf[3];
00411   SecBufferDesc outbuf_desc;
00412   SecBuffer inbuf[2];
00413   SecBufferDesc inbuf_desc;
00414   SECURITY_STATUS sspi_status = SEC_E_OK;
00415   TCHAR *host_name;
00416   CURLcode result;
00417   bool doread;
00418   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
00419     conn->host.name;
00420 
00421   doread = (connssl->connecting_state != ssl_connect_2_writing) ? TRUE : FALSE;
00422 
00423   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 2/3)\n",
00424         hostname, conn->remote_port);
00425 
00426   if(!connssl->cred || !connssl->ctxt)
00427     return CURLE_SSL_CONNECT_ERROR;
00428 
00429   /* buffer to store previously received and decrypted data */
00430   if(connssl->decdata_buffer == NULL) {
00431     connssl->decdata_offset = 0;
00432     connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
00433     connssl->decdata_buffer = malloc(connssl->decdata_length);
00434     if(connssl->decdata_buffer == NULL) {
00435       failf(data, "schannel: unable to allocate memory");
00436       return CURLE_OUT_OF_MEMORY;
00437     }
00438   }
00439 
00440   /* buffer to store previously received and encrypted data */
00441   if(connssl->encdata_buffer == NULL) {
00442     connssl->encdata_offset = 0;
00443     connssl->encdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
00444     connssl->encdata_buffer = malloc(connssl->encdata_length);
00445     if(connssl->encdata_buffer == NULL) {
00446       failf(data, "schannel: unable to allocate memory");
00447       return CURLE_OUT_OF_MEMORY;
00448     }
00449   }
00450 
00451   /* if we need a bigger buffer to read a full message, increase buffer now */
00452   if(connssl->encdata_length - connssl->encdata_offset <
00453      CURL_SCHANNEL_BUFFER_FREE_SIZE) {
00454     /* increase internal encrypted data buffer */
00455     reallocated_length = connssl->encdata_offset +
00456       CURL_SCHANNEL_BUFFER_FREE_SIZE;
00457     reallocated_buffer = realloc(connssl->encdata_buffer,
00458                                  reallocated_length);
00459 
00460     if(reallocated_buffer == NULL) {
00461       failf(data, "schannel: unable to re-allocate memory");
00462       return CURLE_OUT_OF_MEMORY;
00463     }
00464     else {
00465       connssl->encdata_buffer = reallocated_buffer;
00466       connssl->encdata_length = reallocated_length;
00467     }
00468   }
00469 
00470   for(;;) {
00471     if(doread) {
00472       /* read encrypted handshake data from socket */
00473       result = Curl_read_plain(conn->sock[sockindex],
00474                                (char *) (connssl->encdata_buffer +
00475                                          connssl->encdata_offset),
00476                                connssl->encdata_length -
00477                                connssl->encdata_offset,
00478                                &nread);
00479       if(result == CURLE_AGAIN) {
00480         if(connssl->connecting_state != ssl_connect_2_writing)
00481           connssl->connecting_state = ssl_connect_2_reading;
00482         infof(data, "schannel: failed to receive handshake, "
00483               "need more data\n");
00484         return CURLE_OK;
00485       }
00486       else if((result != CURLE_OK) || (nread == 0)) {
00487         failf(data, "schannel: failed to receive handshake, "
00488               "SSL/TLS connection failed");
00489         return CURLE_SSL_CONNECT_ERROR;
00490       }
00491 
00492       /* increase encrypted data buffer offset */
00493       connssl->encdata_offset += nread;
00494     }
00495 
00496     infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
00497           connssl->encdata_offset, connssl->encdata_length);
00498 
00499     /* setup input buffers */
00500     InitSecBuffer(&inbuf[0], SECBUFFER_TOKEN, malloc(connssl->encdata_offset),
00501                   curlx_uztoul(connssl->encdata_offset));
00502     InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
00503     InitSecBufferDesc(&inbuf_desc, inbuf, 2);
00504 
00505     /* setup output buffers */
00506     InitSecBuffer(&outbuf[0], SECBUFFER_TOKEN, NULL, 0);
00507     InitSecBuffer(&outbuf[1], SECBUFFER_ALERT, NULL, 0);
00508     InitSecBuffer(&outbuf[2], SECBUFFER_EMPTY, NULL, 0);
00509     InitSecBufferDesc(&outbuf_desc, outbuf, 3);
00510 
00511     if(inbuf[0].pvBuffer == NULL) {
00512       failf(data, "schannel: unable to allocate memory");
00513       return CURLE_OUT_OF_MEMORY;
00514     }
00515 
00516     /* copy received handshake data into input buffer */
00517     memcpy(inbuf[0].pvBuffer, connssl->encdata_buffer,
00518            connssl->encdata_offset);
00519 
00520     host_name = Curl_convert_UTF8_to_tchar(hostname);
00521     if(!host_name)
00522       return CURLE_OUT_OF_MEMORY;
00523 
00524     /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375924.aspx
00525        */
00526     sspi_status = s_pSecFn->InitializeSecurityContext(
00527       &connssl->cred->cred_handle, &connssl->ctxt->ctxt_handle,
00528       host_name, connssl->req_flags, 0, 0, &inbuf_desc, 0, NULL,
00529       &outbuf_desc, &connssl->ret_flags, &connssl->ctxt->time_stamp);
00530 
00531     Curl_unicodefree(host_name);
00532 
00533     /* free buffer for received handshake data */
00534     Curl_safefree(inbuf[0].pvBuffer);
00535 
00536     /* check if the handshake was incomplete */
00537     if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
00538       connssl->connecting_state = ssl_connect_2_reading;
00539       infof(data, "schannel: received incomplete message, need more data\n");
00540       return CURLE_OK;
00541     }
00542 
00543     /* If the server has requested a client certificate, attempt to continue
00544        the handshake without one. This will allow connections to servers which
00545        request a client certificate but do not require it. */
00546     if(sspi_status == SEC_I_INCOMPLETE_CREDENTIALS &&
00547        !(connssl->req_flags & ISC_REQ_USE_SUPPLIED_CREDS)) {
00548       connssl->req_flags |= ISC_REQ_USE_SUPPLIED_CREDS;
00549       connssl->connecting_state = ssl_connect_2_writing;
00550       infof(data, "schannel: a client certificate has been requested\n");
00551       return CURLE_OK;
00552     }
00553 
00554     /* check if the handshake needs to be continued */
00555     if(sspi_status == SEC_I_CONTINUE_NEEDED || sspi_status == SEC_E_OK) {
00556       for(i = 0; i < 3; i++) {
00557         /* search for handshake tokens that need to be send */
00558         if(outbuf[i].BufferType == SECBUFFER_TOKEN && outbuf[i].cbBuffer > 0) {
00559           infof(data, "schannel: sending next handshake data: "
00560                 "sending %lu bytes...\n", outbuf[i].cbBuffer);
00561 
00562           /* send handshake token to server */
00563           result = Curl_write_plain(conn, conn->sock[sockindex],
00564                                     outbuf[i].pvBuffer, outbuf[i].cbBuffer,
00565                                     &written);
00566           if((result != CURLE_OK) ||
00567              (outbuf[i].cbBuffer != (size_t) written)) {
00568             failf(data, "schannel: failed to send next handshake data: "
00569                   "sent %zd of %lu bytes", written, outbuf[i].cbBuffer);
00570             return CURLE_SSL_CONNECT_ERROR;
00571           }
00572         }
00573 
00574         /* free obsolete buffer */
00575         if(outbuf[i].pvBuffer != NULL) {
00576           s_pSecFn->FreeContextBuffer(outbuf[i].pvBuffer);
00577         }
00578       }
00579     }
00580     else {
00581       if(sspi_status == SEC_E_WRONG_PRINCIPAL)
00582         failf(data, "schannel: SNI or certificate check failed: %s",
00583               Curl_sspi_strerror(conn, sspi_status));
00584       else
00585         failf(data, "schannel: next InitializeSecurityContext failed: %s",
00586               Curl_sspi_strerror(conn, sspi_status));
00587       return CURLE_SSL_CONNECT_ERROR;
00588     }
00589 
00590     /* check if there was additional remaining encrypted data */
00591     if(inbuf[1].BufferType == SECBUFFER_EXTRA && inbuf[1].cbBuffer > 0) {
00592       infof(data, "schannel: encrypted data length: %lu\n", inbuf[1].cbBuffer);
00593       /*
00594         There are two cases where we could be getting extra data here:
00595         1) If we're renegotiating a connection and the handshake is already
00596         complete (from the server perspective), it can encrypted app data
00597         (not handshake data) in an extra buffer at this point.
00598         2) (sspi_status == SEC_I_CONTINUE_NEEDED) We are negotiating a
00599         connection and this extra data is part of the handshake.
00600         We should process the data immediately; waiting for the socket to
00601         be ready may fail since the server is done sending handshake data.
00602       */
00603       /* check if the remaining data is less than the total amount
00604          and therefore begins after the already processed data */
00605       if(connssl->encdata_offset > inbuf[1].cbBuffer) {
00606         memmove(connssl->encdata_buffer,
00607                 (connssl->encdata_buffer + connssl->encdata_offset) -
00608                 inbuf[1].cbBuffer, inbuf[1].cbBuffer);
00609         connssl->encdata_offset = inbuf[1].cbBuffer;
00610         if(sspi_status == SEC_I_CONTINUE_NEEDED) {
00611           doread = FALSE;
00612           continue;
00613         }
00614       }
00615     }
00616     else {
00617       connssl->encdata_offset = 0;
00618     }
00619     break;
00620   }
00621 
00622   /* check if the handshake needs to be continued */
00623   if(sspi_status == SEC_I_CONTINUE_NEEDED) {
00624     connssl->connecting_state = ssl_connect_2_reading;
00625     return CURLE_OK;
00626   }
00627 
00628   /* check if the handshake is complete */
00629   if(sspi_status == SEC_E_OK) {
00630     connssl->connecting_state = ssl_connect_3;
00631     infof(data, "schannel: SSL/TLS handshake complete\n");
00632   }
00633 
00634 #ifdef _WIN32_WCE
00635   /* Windows CE doesn't do any server certificate validation.
00636      We have to do it manually. */
00637   if(conn->ssl_config.verifypeer)
00638     return verify_certificate(conn, sockindex);
00639 #endif
00640 
00641   return CURLE_OK;
00642 }
00643 
00644 static CURLcode
00645 schannel_connect_step3(struct connectdata *conn, int sockindex)
00646 {
00647   CURLcode result = CURLE_OK;
00648   struct Curl_easy *data = conn->data;
00649   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
00650   SECURITY_STATUS sspi_status = SEC_E_OK;
00651   CERT_CONTEXT *ccert_context = NULL;
00652   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
00653     conn->host.name;
00654 #ifdef HAS_ALPN
00655   SecPkgContext_ApplicationProtocol alpn_result;
00656 #endif
00657 
00658   DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
00659 
00660   infof(data, "schannel: SSL/TLS connection with %s port %hu (step 3/3)\n",
00661         hostname, conn->remote_port);
00662 
00663   if(!connssl->cred)
00664     return CURLE_SSL_CONNECT_ERROR;
00665 
00666   /* check if the required context attributes are met */
00667   if(connssl->ret_flags != connssl->req_flags) {
00668     if(!(connssl->ret_flags & ISC_RET_SEQUENCE_DETECT))
00669       failf(data, "schannel: failed to setup sequence detection");
00670     if(!(connssl->ret_flags & ISC_RET_REPLAY_DETECT))
00671       failf(data, "schannel: failed to setup replay detection");
00672     if(!(connssl->ret_flags & ISC_RET_CONFIDENTIALITY))
00673       failf(data, "schannel: failed to setup confidentiality");
00674     if(!(connssl->ret_flags & ISC_RET_ALLOCATED_MEMORY))
00675       failf(data, "schannel: failed to setup memory allocation");
00676     if(!(connssl->ret_flags & ISC_RET_STREAM))
00677       failf(data, "schannel: failed to setup stream orientation");
00678     return CURLE_SSL_CONNECT_ERROR;
00679   }
00680 
00681 #ifdef HAS_ALPN
00682   if(connssl->use_alpn) {
00683     sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
00684       SECPKG_ATTR_APPLICATION_PROTOCOL, &alpn_result);
00685 
00686     if(sspi_status != SEC_E_OK) {
00687       failf(data, "schannel: failed to retrieve ALPN result");
00688       return CURLE_SSL_CONNECT_ERROR;
00689     }
00690 
00691     if(alpn_result.ProtoNegoStatus ==
00692        SecApplicationProtocolNegotiationStatus_Success) {
00693 
00694       infof(data, "schannel: ALPN, server accepted to use %.*s\n",
00695         alpn_result.ProtocolIdSize, alpn_result.ProtocolId);
00696 
00697 #ifdef USE_NGHTTP2
00698       if(alpn_result.ProtocolIdSize == NGHTTP2_PROTO_VERSION_ID_LEN &&
00699          !memcmp(NGHTTP2_PROTO_VERSION_ID, alpn_result.ProtocolId,
00700           NGHTTP2_PROTO_VERSION_ID_LEN)) {
00701         conn->negnpn = CURL_HTTP_VERSION_2;
00702       }
00703       else
00704 #endif
00705       if(alpn_result.ProtocolIdSize == ALPN_HTTP_1_1_LENGTH &&
00706          !memcmp(ALPN_HTTP_1_1, alpn_result.ProtocolId,
00707            ALPN_HTTP_1_1_LENGTH)) {
00708         conn->negnpn = CURL_HTTP_VERSION_1_1;
00709       }
00710     }
00711     else
00712       infof(data, "ALPN, server did not agree to a protocol\n");
00713   }
00714 #endif
00715 
00716   /* save the current session data for possible re-use */
00717   if(data->set.general_ssl.sessionid) {
00718     bool incache;
00719     struct curl_schannel_cred *old_cred = NULL;
00720 
00721     Curl_ssl_sessionid_lock(conn);
00722     incache = !(Curl_ssl_getsessionid(conn, (void **)&old_cred, NULL,
00723                                       sockindex));
00724     if(incache) {
00725       if(old_cred != connssl->cred) {
00726         infof(data, "schannel: old credential handle is stale, removing\n");
00727         /* we're not taking old_cred ownership here, no refcount++ is needed */
00728         Curl_ssl_delsessionid(conn, (void *)old_cred);
00729         incache = FALSE;
00730       }
00731     }
00732     if(!incache) {
00733       result = Curl_ssl_addsessionid(conn, (void *)connssl->cred,
00734                                      sizeof(struct curl_schannel_cred),
00735                                      sockindex);
00736       if(result) {
00737         Curl_ssl_sessionid_unlock(conn);
00738         failf(data, "schannel: failed to store credential handle");
00739         return result;
00740       }
00741       else {
00742         /* this cred session is now also referenced by sessionid cache */
00743         connssl->cred->refcount++;
00744         infof(data, "schannel: stored credential handle in session cache\n");
00745       }
00746     }
00747     Curl_ssl_sessionid_unlock(conn);
00748   }
00749 
00750   if(data->set.ssl.certinfo) {
00751     sspi_status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
00752       SECPKG_ATTR_REMOTE_CERT_CONTEXT, &ccert_context);
00753 
00754     if((sspi_status != SEC_E_OK) || (ccert_context == NULL)) {
00755       failf(data, "schannel: failed to retrieve remote cert context");
00756       return CURLE_SSL_CONNECT_ERROR;
00757     }
00758 
00759     result = Curl_ssl_init_certinfo(data, 1);
00760     if(!result) {
00761       if(((ccert_context->dwCertEncodingType & X509_ASN_ENCODING) != 0) &&
00762          (ccert_context->cbCertEncoded > 0)) {
00763 
00764         const char *beg = (const char *) ccert_context->pbCertEncoded;
00765         const char *end = beg + ccert_context->cbCertEncoded;
00766         result = Curl_extract_certinfo(conn, 0, beg, end);
00767       }
00768     }
00769     CertFreeCertificateContext(ccert_context);
00770     if(result)
00771       return result;
00772   }
00773 
00774   connssl->connecting_state = ssl_connect_done;
00775 
00776   return CURLE_OK;
00777 }
00778 
00779 static CURLcode
00780 schannel_connect_common(struct connectdata *conn, int sockindex,
00781                         bool nonblocking, bool *done)
00782 {
00783   CURLcode result;
00784   struct Curl_easy *data = conn->data;
00785   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
00786   curl_socket_t sockfd = conn->sock[sockindex];
00787   time_t timeout_ms;
00788   int what;
00789 
00790   /* check if the connection has already been established */
00791   if(ssl_connection_complete == connssl->state) {
00792     *done = TRUE;
00793     return CURLE_OK;
00794   }
00795 
00796   if(ssl_connect_1 == connssl->connecting_state) {
00797     /* check out how much more time we're allowed */
00798     timeout_ms = Curl_timeleft(data, NULL, TRUE);
00799 
00800     if(timeout_ms < 0) {
00801       /* no need to continue if time already is up */
00802       failf(data, "SSL/TLS connection timeout");
00803       return CURLE_OPERATION_TIMEDOUT;
00804     }
00805 
00806     result = schannel_connect_step1(conn, sockindex);
00807     if(result)
00808       return result;
00809   }
00810 
00811   while(ssl_connect_2 == connssl->connecting_state ||
00812         ssl_connect_2_reading == connssl->connecting_state ||
00813         ssl_connect_2_writing == connssl->connecting_state) {
00814 
00815     /* check out how much more time we're allowed */
00816     timeout_ms = Curl_timeleft(data, NULL, TRUE);
00817 
00818     if(timeout_ms < 0) {
00819       /* no need to continue if time already is up */
00820       failf(data, "SSL/TLS connection timeout");
00821       return CURLE_OPERATION_TIMEDOUT;
00822     }
00823 
00824     /* if ssl is expecting something, check if it's available. */
00825     if(connssl->connecting_state == ssl_connect_2_reading
00826        || connssl->connecting_state == ssl_connect_2_writing) {
00827 
00828       curl_socket_t writefd = ssl_connect_2_writing ==
00829         connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
00830       curl_socket_t readfd = ssl_connect_2_reading ==
00831         connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
00832 
00833       what = Curl_socket_check(readfd, CURL_SOCKET_BAD, writefd,
00834                                nonblocking ? 0 : timeout_ms);
00835       if(what < 0) {
00836         /* fatal error */
00837         failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
00838         return CURLE_SSL_CONNECT_ERROR;
00839       }
00840       else if(0 == what) {
00841         if(nonblocking) {
00842           *done = FALSE;
00843           return CURLE_OK;
00844         }
00845         else {
00846           /* timeout */
00847           failf(data, "SSL/TLS connection timeout");
00848           return CURLE_OPERATION_TIMEDOUT;
00849         }
00850       }
00851       /* socket is readable or writable */
00852     }
00853 
00854     /* Run transaction, and return to the caller if it failed or if
00855      * this connection is part of a multi handle and this loop would
00856      * execute again. This permits the owner of a multi handle to
00857      * abort a connection attempt before step2 has completed while
00858      * ensuring that a client using select() or epoll() will always
00859      * have a valid fdset to wait on.
00860      */
00861     result = schannel_connect_step2(conn, sockindex);
00862     if(result || (nonblocking &&
00863                   (ssl_connect_2 == connssl->connecting_state ||
00864                    ssl_connect_2_reading == connssl->connecting_state ||
00865                    ssl_connect_2_writing == connssl->connecting_state)))
00866       return result;
00867 
00868   } /* repeat step2 until all transactions are done. */
00869 
00870   if(ssl_connect_3 == connssl->connecting_state) {
00871     result = schannel_connect_step3(conn, sockindex);
00872     if(result)
00873       return result;
00874   }
00875 
00876   if(ssl_connect_done == connssl->connecting_state) {
00877     connssl->state = ssl_connection_complete;
00878     conn->recv[sockindex] = schannel_recv;
00879     conn->send[sockindex] = schannel_send;
00880     *done = TRUE;
00881   }
00882   else
00883     *done = FALSE;
00884 
00885   /* reset our connection state machine */
00886   connssl->connecting_state = ssl_connect_1;
00887 
00888   return CURLE_OK;
00889 }
00890 
00891 static ssize_t
00892 schannel_send(struct connectdata *conn, int sockindex,
00893               const void *buf, size_t len, CURLcode *err)
00894 {
00895   ssize_t written = -1;
00896   size_t data_len = 0;
00897   unsigned char *data = NULL;
00898   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
00899   SecBuffer outbuf[4];
00900   SecBufferDesc outbuf_desc;
00901   SECURITY_STATUS sspi_status = SEC_E_OK;
00902   CURLcode result;
00903 
00904   /* check if the maximum stream sizes were queried */
00905   if(connssl->stream_sizes.cbMaximumMessage == 0) {
00906     sspi_status = s_pSecFn->QueryContextAttributes(
00907       &connssl->ctxt->ctxt_handle,
00908       SECPKG_ATTR_STREAM_SIZES,
00909       &connssl->stream_sizes);
00910     if(sspi_status != SEC_E_OK) {
00911       *err = CURLE_SEND_ERROR;
00912       return -1;
00913     }
00914   }
00915 
00916   /* check if the buffer is longer than the maximum message length */
00917   if(len > connssl->stream_sizes.cbMaximumMessage) {
00918     *err = CURLE_SEND_ERROR;
00919     return -1;
00920   }
00921 
00922   /* calculate the complete message length and allocate a buffer for it */
00923   data_len = connssl->stream_sizes.cbHeader + len +
00924     connssl->stream_sizes.cbTrailer;
00925   data = (unsigned char *) malloc(data_len);
00926   if(data == NULL) {
00927     *err = CURLE_OUT_OF_MEMORY;
00928     return -1;
00929   }
00930 
00931   /* setup output buffers (header, data, trailer, empty) */
00932   InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
00933                 data, connssl->stream_sizes.cbHeader);
00934   InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
00935                 data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
00936   InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
00937                 data + connssl->stream_sizes.cbHeader + len,
00938                 connssl->stream_sizes.cbTrailer);
00939   InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
00940   InitSecBufferDesc(&outbuf_desc, outbuf, 4);
00941 
00942   /* copy data into output buffer */
00943   memcpy(outbuf[1].pvBuffer, buf, len);
00944 
00945   /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
00946   sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
00947                                          &outbuf_desc, 0);
00948 
00949   /* check if the message was encrypted */
00950   if(sspi_status == SEC_E_OK) {
00951     written = 0;
00952 
00953     /* send the encrypted message including header, data and trailer */
00954     len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
00955 
00956     /*
00957       It's important to send the full message which includes the header,
00958       encrypted payload, and trailer.  Until the client receives all the
00959       data a coherent message has not been delivered and the client
00960       can't read any of it.
00961 
00962       If we wanted to buffer the unwritten encrypted bytes, we would
00963       tell the client that all data it has requested to be sent has been
00964       sent. The unwritten encrypted bytes would be the first bytes to
00965       send on the next invocation.
00966       Here's the catch with this - if we tell the client that all the
00967       bytes have been sent, will the client call this method again to
00968       send the buffered data?  Looking at who calls this function, it
00969       seems the answer is NO.
00970     */
00971 
00972     /* send entire message or fail */
00973     while(len > (size_t)written) {
00974       ssize_t this_write;
00975       time_t timeleft;
00976       int what;
00977 
00978       this_write = 0;
00979 
00980       timeleft = Curl_timeleft(conn->data, NULL, FALSE);
00981       if(timeleft < 0) {
00982         /* we already got the timeout */
00983         failf(conn->data, "schannel: timed out sending data "
00984               "(bytes sent: %zd)", written);
00985         *err = CURLE_OPERATION_TIMEDOUT;
00986         written = -1;
00987         break;
00988       }
00989 
00990       what = SOCKET_WRITABLE(conn->sock[sockindex], timeleft);
00991       if(what < 0) {
00992         /* fatal error */
00993         failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
00994         *err = CURLE_SEND_ERROR;
00995         written = -1;
00996         break;
00997       }
00998       else if(0 == what) {
00999         failf(conn->data, "schannel: timed out sending data "
01000               "(bytes sent: %zd)", written);
01001         *err = CURLE_OPERATION_TIMEDOUT;
01002         written = -1;
01003         break;
01004       }
01005       /* socket is writable */
01006 
01007       result = Curl_write_plain(conn, conn->sock[sockindex], data + written,
01008                                 len - written, &this_write);
01009       if(result == CURLE_AGAIN)
01010         continue;
01011       else if(result != CURLE_OK) {
01012         *err = result;
01013         written = -1;
01014         break;
01015       }
01016 
01017       written += this_write;
01018     }
01019   }
01020   else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
01021     *err = CURLE_OUT_OF_MEMORY;
01022   }
01023   else{
01024     *err = CURLE_SEND_ERROR;
01025   }
01026 
01027   Curl_safefree(data);
01028 
01029   if(len == (size_t)written)
01030     /* Encrypted message including header, data and trailer entirely sent.
01031        The return value is the number of unencrypted bytes that were sent. */
01032     written = outbuf[1].cbBuffer;
01033 
01034   return written;
01035 }
01036 
01037 static ssize_t
01038 schannel_recv(struct connectdata *conn, int sockindex,
01039               char *buf, size_t len, CURLcode *err)
01040 {
01041   size_t size = 0;
01042   ssize_t nread = -1;
01043   struct Curl_easy *data = conn->data;
01044   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
01045   unsigned char *reallocated_buffer;
01046   size_t reallocated_length;
01047   bool done = FALSE;
01048   SecBuffer inbuf[4];
01049   SecBufferDesc inbuf_desc;
01050   SECURITY_STATUS sspi_status = SEC_E_OK;
01051   /* we want the length of the encrypted buffer to be at least large enough
01052      that it can hold all the bytes requested and some TLS record overhead. */
01053   size_t min_encdata_length = len + CURL_SCHANNEL_BUFFER_FREE_SIZE;
01054 
01055   /****************************************************************************
01056    * Don't return or set connssl->recv_unrecoverable_err unless in the cleanup.
01057    * The pattern for return error is set *err, optional infof, goto cleanup.
01058    *
01059    * Our priority is to always return as much decrypted data to the caller as
01060    * possible, even if an error occurs. The state of the decrypted buffer must
01061    * always be valid. Transfer of decrypted data to the caller's buffer is
01062    * handled in the cleanup.
01063    */
01064 
01065   infof(data, "schannel: client wants to read %zu bytes\n", len);
01066   *err = CURLE_OK;
01067 
01068   if(len && len <= connssl->decdata_offset) {
01069     infof(data, "schannel: enough decrypted data is already available\n");
01070     goto cleanup;
01071   }
01072   else if(connssl->recv_unrecoverable_err) {
01073     *err = connssl->recv_unrecoverable_err;
01074     infof(data, "schannel: an unrecoverable error occurred in a prior call\n");
01075     goto cleanup;
01076   }
01077   else if(connssl->recv_sspi_close_notify) {
01078     /* once a server has indicated shutdown there is no more encrypted data */
01079     infof(data, "schannel: server indicated shutdown in a prior call\n");
01080     goto cleanup;
01081   }
01082   else if(!len) {
01083     /* It's debatable what to return when !len. Regardless we can't return
01084     immediately because there may be data to decrypt (in the case we want to
01085     decrypt all encrypted cached data) so handle !len later in cleanup.
01086     */
01087     ; /* do nothing */
01088   }
01089   else if(!connssl->recv_connection_closed) {
01090     /* increase enc buffer in order to fit the requested amount of data */
01091     size = connssl->encdata_length - connssl->encdata_offset;
01092     if(size < CURL_SCHANNEL_BUFFER_FREE_SIZE ||
01093        connssl->encdata_length < min_encdata_length) {
01094       reallocated_length = connssl->encdata_offset +
01095                            CURL_SCHANNEL_BUFFER_FREE_SIZE;
01096       if(reallocated_length < min_encdata_length) {
01097         reallocated_length = min_encdata_length;
01098       }
01099       reallocated_buffer = realloc(connssl->encdata_buffer,
01100                                    reallocated_length);
01101       if(reallocated_buffer == NULL) {
01102         *err = CURLE_OUT_OF_MEMORY;
01103         failf(data, "schannel: unable to re-allocate memory");
01104         goto cleanup;
01105       }
01106 
01107       connssl->encdata_buffer = reallocated_buffer;
01108       connssl->encdata_length = reallocated_length;
01109       size = connssl->encdata_length - connssl->encdata_offset;
01110       infof(data, "schannel: encdata_buffer resized %zu\n",
01111             connssl->encdata_length);
01112     }
01113 
01114     infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
01115           connssl->encdata_offset, connssl->encdata_length);
01116 
01117     /* read encrypted data from socket */
01118     *err = Curl_read_plain(conn->sock[sockindex],
01119                            (char *)(connssl->encdata_buffer +
01120                                     connssl->encdata_offset),
01121                            size, &nread);
01122     if(*err) {
01123       nread = -1;
01124       if(*err == CURLE_AGAIN)
01125         infof(data, "schannel: Curl_read_plain returned CURLE_AGAIN\n");
01126       else if(*err == CURLE_RECV_ERROR)
01127         infof(data, "schannel: Curl_read_plain returned CURLE_RECV_ERROR\n");
01128       else
01129         infof(data, "schannel: Curl_read_plain returned error %d\n", *err);
01130     }
01131     else if(nread == 0) {
01132       connssl->recv_connection_closed = true;
01133       infof(data, "schannel: server closed the connection\n");
01134     }
01135     else if(nread > 0) {
01136       connssl->encdata_offset += (size_t)nread;
01137       infof(data, "schannel: encrypted data got %zd\n", nread);
01138     }
01139   }
01140 
01141   infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
01142         connssl->encdata_offset, connssl->encdata_length);
01143 
01144   /* decrypt loop */
01145   while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
01146         (!len || connssl->decdata_offset < len ||
01147          connssl->recv_connection_closed)) {
01148     /* prepare data buffer for DecryptMessage call */
01149     InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
01150                   curlx_uztoul(connssl->encdata_offset));
01151 
01152     /* we need 3 more empty input buffers for possible output */
01153     InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
01154     InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
01155     InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
01156     InitSecBufferDesc(&inbuf_desc, inbuf, 4);
01157 
01158     /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx
01159        */
01160     sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
01161                                            &inbuf_desc, 0, NULL);
01162 
01163     /* check if everything went fine (server may want to renegotiate
01164        or shutdown the connection context) */
01165     if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
01166        sspi_status == SEC_I_CONTEXT_EXPIRED) {
01167       /* check for successfully decrypted data, even before actual
01168          renegotiation or shutdown of the connection context */
01169       if(inbuf[1].BufferType == SECBUFFER_DATA) {
01170         infof(data, "schannel: decrypted data length: %lu\n",
01171               inbuf[1].cbBuffer);
01172 
01173         /* increase buffer in order to fit the received amount of data */
01174         size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
01175                inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
01176         if(connssl->decdata_length - connssl->decdata_offset < size ||
01177            connssl->decdata_length < len) {
01178           /* increase internal decrypted data buffer */
01179           reallocated_length = connssl->decdata_offset + size;
01180           /* make sure that the requested amount of data fits */
01181           if(reallocated_length < len) {
01182             reallocated_length = len;
01183           }
01184           reallocated_buffer = realloc(connssl->decdata_buffer,
01185                                        reallocated_length);
01186           if(reallocated_buffer == NULL) {
01187             *err = CURLE_OUT_OF_MEMORY;
01188             failf(data, "schannel: unable to re-allocate memory");
01189             goto cleanup;
01190           }
01191           connssl->decdata_buffer = reallocated_buffer;
01192           connssl->decdata_length = reallocated_length;
01193         }
01194 
01195         /* copy decrypted data to internal buffer */
01196         size = inbuf[1].cbBuffer;
01197         if(size) {
01198           memcpy(connssl->decdata_buffer + connssl->decdata_offset,
01199                  inbuf[1].pvBuffer, size);
01200           connssl->decdata_offset += size;
01201         }
01202 
01203         infof(data, "schannel: decrypted data added: %zu\n", size);
01204         infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
01205               connssl->decdata_offset, connssl->decdata_length);
01206       }
01207 
01208       /* check for remaining encrypted data */
01209       if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
01210         infof(data, "schannel: encrypted data length: %lu\n",
01211               inbuf[3].cbBuffer);
01212 
01213         /* check if the remaining data is less than the total amount
01214          * and therefore begins after the already processed data
01215          */
01216         if(connssl->encdata_offset > inbuf[3].cbBuffer) {
01217           /* move remaining encrypted data forward to the beginning of
01218              buffer */
01219           memmove(connssl->encdata_buffer,
01220                   (connssl->encdata_buffer + connssl->encdata_offset) -
01221                   inbuf[3].cbBuffer, inbuf[3].cbBuffer);
01222           connssl->encdata_offset = inbuf[3].cbBuffer;
01223         }
01224 
01225         infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
01226               connssl->encdata_offset, connssl->encdata_length);
01227       }
01228       else {
01229         /* reset encrypted buffer offset, because there is no data remaining */
01230         connssl->encdata_offset = 0;
01231       }
01232 
01233       /* check if server wants to renegotiate the connection context */
01234       if(sspi_status == SEC_I_RENEGOTIATE) {
01235         infof(data, "schannel: remote party requests renegotiation\n");
01236         if(*err && *err != CURLE_AGAIN) {
01237           infof(data, "schannel: can't renogotiate, an error is pending\n");
01238           goto cleanup;
01239         }
01240         if(connssl->encdata_offset) {
01241           *err = CURLE_RECV_ERROR;
01242           infof(data, "schannel: can't renogotiate, "
01243                       "encrypted data available\n");
01244           goto cleanup;
01245         }
01246         /* begin renegotiation */
01247         infof(data, "schannel: renegotiating SSL/TLS connection\n");
01248         connssl->state = ssl_connection_negotiating;
01249         connssl->connecting_state = ssl_connect_2_writing;
01250         *err = schannel_connect_common(conn, sockindex, FALSE, &done);
01251         if(*err) {
01252           infof(data, "schannel: renegotiation failed\n");
01253           goto cleanup;
01254         }
01255         /* now retry receiving data */
01256         sspi_status = SEC_E_OK;
01257         infof(data, "schannel: SSL/TLS connection renegotiated\n");
01258         continue;
01259       }
01260       /* check if the server closed the connection */
01261       else if(sspi_status == SEC_I_CONTEXT_EXPIRED) {
01262         /* In Windows 2000 SEC_I_CONTEXT_EXPIRED (close_notify) is not
01263            returned so we have to work around that in cleanup. */
01264         connssl->recv_sspi_close_notify = true;
01265         if(!connssl->recv_connection_closed) {
01266           connssl->recv_connection_closed = true;
01267           infof(data, "schannel: server closed the connection\n");
01268         }
01269         goto cleanup;
01270       }
01271     }
01272     else if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
01273       if(!*err)
01274         *err = CURLE_AGAIN;
01275       infof(data, "schannel: failed to decrypt data, need more data\n");
01276       goto cleanup;
01277     }
01278     else {
01279       *err = CURLE_RECV_ERROR;
01280       infof(data, "schannel: failed to read data from server: %s\n",
01281             Curl_sspi_strerror(conn, sspi_status));
01282       goto cleanup;
01283     }
01284   }
01285 
01286   infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
01287         connssl->encdata_offset, connssl->encdata_length);
01288 
01289   infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
01290         connssl->decdata_offset, connssl->decdata_length);
01291 
01292 cleanup:
01293   /* Warning- there is no guarantee the encdata state is valid at this point */
01294   infof(data, "schannel: schannel_recv cleanup\n");
01295 
01296   /* Error if the connection has closed without a close_notify.
01297   Behavior here is a matter of debate. We don't want to be vulnerable to a
01298   truncation attack however there's some browser precedent for ignoring the
01299   close_notify for compatibility reasons.
01300   Additionally, Windows 2000 (v5.0) is a special case since it seems it doesn't
01301   return close_notify. In that case if the connection was closed we assume it
01302   was graceful (close_notify) since there doesn't seem to be a way to tell.
01303   */
01304   if(len && !connssl->decdata_offset && connssl->recv_connection_closed &&
01305      !connssl->recv_sspi_close_notify) {
01306     bool isWin2k = Curl_verify_windows_version(5, 0, PLATFORM_WINNT,
01307                                                VERSION_EQUAL);
01308 
01309     if(isWin2k && sspi_status == SEC_E_OK)
01310       connssl->recv_sspi_close_notify = true;
01311     else {
01312       *err = CURLE_RECV_ERROR;
01313       infof(data, "schannel: server closed abruptly (missing close_notify)\n");
01314     }
01315   }
01316 
01317   /* Any error other than CURLE_AGAIN is an unrecoverable error. */
01318   if(*err && *err != CURLE_AGAIN)
01319       connssl->recv_unrecoverable_err = *err;
01320 
01321   size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
01322   if(size) {
01323     memcpy(buf, connssl->decdata_buffer, size);
01324     memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
01325             connssl->decdata_offset - size);
01326     connssl->decdata_offset -= size;
01327 
01328     infof(data, "schannel: decrypted data returned %zu\n", size);
01329     infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
01330           connssl->decdata_offset, connssl->decdata_length);
01331     *err = CURLE_OK;
01332     return (ssize_t)size;
01333   }
01334 
01335   if(!*err && !connssl->recv_connection_closed)
01336       *err = CURLE_AGAIN;
01337 
01338   /* It's debatable what to return when !len. We could return whatever error we
01339   got from decryption but instead we override here so the return is consistent.
01340   */
01341   if(!len)
01342     *err = CURLE_OK;
01343 
01344   return *err ? -1 : 0;
01345 }
01346 
01347 CURLcode
01348 Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
01349                                   bool *done)
01350 {
01351   return schannel_connect_common(conn, sockindex, TRUE, done);
01352 }
01353 
01354 CURLcode
01355 Curl_schannel_connect(struct connectdata *conn, int sockindex)
01356 {
01357   CURLcode result;
01358   bool done = FALSE;
01359 
01360   result = schannel_connect_common(conn, sockindex, FALSE, &done);
01361   if(result)
01362     return result;
01363 
01364   DEBUGASSERT(done);
01365 
01366   return CURLE_OK;
01367 }
01368 
01369 bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
01370 {
01371   const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
01372 
01373   if(connssl->use) /* SSL/TLS is in use */
01374     return (connssl->encdata_offset > 0 ||
01375             connssl->decdata_offset > 0) ? TRUE : FALSE;
01376   else
01377     return FALSE;
01378 }
01379 
01380 void Curl_schannel_close(struct connectdata *conn, int sockindex)
01381 {
01382   if(conn->ssl[sockindex].use)
01383     /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
01384     Curl_ssl_shutdown(conn, sockindex);
01385 }
01386 
01387 int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
01388 {
01389   /* See https://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
01390    * Shutting Down an Schannel Connection
01391    */
01392   struct Curl_easy *data = conn->data;
01393   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
01394   const char * const hostname = SSL_IS_PROXY() ? conn->http_proxy.host.name :
01395     conn->host.name;
01396 
01397   infof(data, "schannel: shutting down SSL/TLS connection with %s port %hu\n",
01398         hostname, conn->remote_port);
01399 
01400   if(connssl->cred && connssl->ctxt) {
01401     SecBufferDesc BuffDesc;
01402     SecBuffer Buffer;
01403     SECURITY_STATUS sspi_status;
01404     SecBuffer outbuf;
01405     SecBufferDesc outbuf_desc;
01406     CURLcode result;
01407     TCHAR *host_name;
01408     DWORD dwshut = SCHANNEL_SHUTDOWN;
01409 
01410     InitSecBuffer(&Buffer, SECBUFFER_TOKEN, &dwshut, sizeof(dwshut));
01411     InitSecBufferDesc(&BuffDesc, &Buffer, 1);
01412 
01413     sspi_status = s_pSecFn->ApplyControlToken(&connssl->ctxt->ctxt_handle,
01414                                               &BuffDesc);
01415 
01416     if(sspi_status != SEC_E_OK)
01417       failf(data, "schannel: ApplyControlToken failure: %s",
01418             Curl_sspi_strerror(conn, sspi_status));
01419 
01420     host_name = Curl_convert_UTF8_to_tchar(hostname);
01421     if(!host_name)
01422       return CURLE_OUT_OF_MEMORY;
01423 
01424     /* setup output buffer */
01425     InitSecBuffer(&outbuf, SECBUFFER_EMPTY, NULL, 0);
01426     InitSecBufferDesc(&outbuf_desc, &outbuf, 1);
01427 
01428     sspi_status = s_pSecFn->InitializeSecurityContext(
01429       &connssl->cred->cred_handle,
01430       &connssl->ctxt->ctxt_handle,
01431       host_name,
01432       connssl->req_flags,
01433       0,
01434       0,
01435       NULL,
01436       0,
01437       &connssl->ctxt->ctxt_handle,
01438       &outbuf_desc,
01439       &connssl->ret_flags,
01440       &connssl->ctxt->time_stamp);
01441 
01442     Curl_unicodefree(host_name);
01443 
01444     if((sspi_status == SEC_E_OK) || (sspi_status == SEC_I_CONTEXT_EXPIRED)) {
01445       /* send close message which is in output buffer */
01446       ssize_t written;
01447       result = Curl_write_plain(conn, conn->sock[sockindex], outbuf.pvBuffer,
01448                                 outbuf.cbBuffer, &written);
01449 
01450       s_pSecFn->FreeContextBuffer(outbuf.pvBuffer);
01451       if((result != CURLE_OK) || (outbuf.cbBuffer != (size_t) written)) {
01452         infof(data, "schannel: failed to send close msg: %s"
01453               " (bytes written: %zd)\n", curl_easy_strerror(result), written);
01454       }
01455     }
01456   }
01457 
01458   /* free SSPI Schannel API security context handle */
01459   if(connssl->ctxt) {
01460     infof(data, "schannel: clear security context handle\n");
01461     s_pSecFn->DeleteSecurityContext(&connssl->ctxt->ctxt_handle);
01462     Curl_safefree(connssl->ctxt);
01463   }
01464 
01465   /* free SSPI Schannel API credential handle */
01466   if(connssl->cred) {
01467     Curl_ssl_sessionid_lock(conn);
01468     Curl_schannel_session_free(connssl->cred);
01469     Curl_ssl_sessionid_unlock(conn);
01470     connssl->cred = NULL;
01471   }
01472 
01473   /* free internal buffer for received encrypted data */
01474   if(connssl->encdata_buffer != NULL) {
01475     Curl_safefree(connssl->encdata_buffer);
01476     connssl->encdata_length = 0;
01477     connssl->encdata_offset = 0;
01478   }
01479 
01480   /* free internal buffer for received decrypted data */
01481   if(connssl->decdata_buffer != NULL) {
01482     Curl_safefree(connssl->decdata_buffer);
01483     connssl->decdata_length = 0;
01484     connssl->decdata_offset = 0;
01485   }
01486 
01487   return CURLE_OK;
01488 }
01489 
01490 void Curl_schannel_session_free(void *ptr)
01491 {
01492   /* this is expected to be called under sessionid lock */
01493   struct curl_schannel_cred *cred = ptr;
01494 
01495   cred->refcount--;
01496   if(cred->refcount == 0) {
01497     s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
01498     Curl_safefree(cred);
01499   }
01500 }
01501 
01502 int Curl_schannel_init(void)
01503 {
01504   return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
01505 }
01506 
01507 void Curl_schannel_cleanup(void)
01508 {
01509   Curl_sspi_global_cleanup();
01510 }
01511 
01512 size_t Curl_schannel_version(char *buffer, size_t size)
01513 {
01514   size = snprintf(buffer, size, "WinSSL");
01515 
01516   return size;
01517 }
01518 
01519 int Curl_schannel_random(unsigned char *entropy, size_t length)
01520 {
01521   HCRYPTPROV hCryptProv = 0;
01522 
01523   if(!CryptAcquireContext(&hCryptProv, NULL, NULL, PROV_RSA_FULL,
01524                           CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
01525     return 1;
01526 
01527   if(!CryptGenRandom(hCryptProv, (DWORD)length, entropy)) {
01528     CryptReleaseContext(hCryptProv, 0UL);
01529     return 1;
01530   }
01531 
01532   CryptReleaseContext(hCryptProv, 0UL);
01533   return 0;
01534 }
01535 
01536 #ifdef _WIN32_WCE
01537 static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
01538 {
01539   SECURITY_STATUS status;
01540   struct Curl_easy *data = conn->data;
01541   struct ssl_connect_data *connssl = &conn->ssl[sockindex];
01542   CURLcode result = CURLE_OK;
01543   CERT_CONTEXT *pCertContextServer = NULL;
01544   const CERT_CHAIN_CONTEXT *pChainContext = NULL;
01545   const char * const conn_hostname = SSL_IS_PROXY() ?
01546     conn->http_proxy.host.name :
01547     conn->host.name;
01548 
01549   status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
01550                                             SECPKG_ATTR_REMOTE_CERT_CONTEXT,
01551                                             &pCertContextServer);
01552 
01553   if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
01554     failf(data, "schannel: Failed to read remote certificate context: %s",
01555           Curl_sspi_strerror(conn, status));
01556     result = CURLE_PEER_FAILED_VERIFICATION;
01557   }
01558 
01559   if(result == CURLE_OK) {
01560     CERT_CHAIN_PARA ChainPara;
01561     memset(&ChainPara, 0, sizeof(ChainPara));
01562     ChainPara.cbSize = sizeof(ChainPara);
01563 
01564     if(!CertGetCertificateChain(NULL,
01565                                 pCertContextServer,
01566                                 NULL,
01567                                 pCertContextServer->hCertStore,
01568                                 &ChainPara,
01569                                 (data->set.ssl.no_revoke ? 0 :
01570                                  CERT_CHAIN_REVOCATION_CHECK_CHAIN),
01571                                 NULL,
01572                                 &pChainContext)) {
01573       failf(data, "schannel: CertGetCertificateChain failed: %s",
01574             Curl_sspi_strerror(conn, GetLastError()));
01575       pChainContext = NULL;
01576       result = CURLE_PEER_FAILED_VERIFICATION;
01577     }
01578 
01579     if(result == CURLE_OK) {
01580       CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
01581       DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED);
01582       dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
01583       if(dwTrustErrorMask) {
01584         if(dwTrustErrorMask & CERT_TRUST_IS_REVOKED)
01585           failf(data, "schannel: CertGetCertificateChain trust error"
01586                 " CERT_TRUST_IS_REVOKED");
01587         else if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
01588           failf(data, "schannel: CertGetCertificateChain trust error"
01589                 " CERT_TRUST_IS_PARTIAL_CHAIN");
01590         else if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
01591           failf(data, "schannel: CertGetCertificateChain trust error"
01592                 " CERT_TRUST_IS_UNTRUSTED_ROOT");
01593         else if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
01594           failf(data, "schannel: CertGetCertificateChain trust error"
01595                 " CERT_TRUST_IS_NOT_TIME_VALID");
01596         else
01597           failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
01598                 dwTrustErrorMask);
01599         result = CURLE_PEER_FAILED_VERIFICATION;
01600       }
01601     }
01602   }
01603 
01604   if(result == CURLE_OK) {
01605     if(conn->ssl_config.verifyhost) {
01606       TCHAR cert_hostname_buff[256];
01607       DWORD len;
01608 
01609       /* TODO: Fix this for certificates with multiple alternative names.
01610       Right now we're only asking for the first preferred alternative name.
01611       Instead we'd need to do all via CERT_NAME_SEARCH_ALL_NAMES_FLAG
01612       (if WinCE supports that?) and run this section in a loop for each.
01613       https://msdn.microsoft.com/en-us/library/windows/desktop/aa376086.aspx
01614       curl: (51) schannel: CertGetNameString() certificate hostname
01615       (.google.com) did not match connection (google.com)
01616       */
01617       len = CertGetNameString(pCertContextServer,
01618                               CERT_NAME_DNS_TYPE,
01619                               CERT_NAME_DISABLE_IE4_UTF8_FLAG,
01620                               NULL,
01621                               cert_hostname_buff,
01622                               256);
01623       if(len > 0) {
01624         const char *cert_hostname;
01625 
01626         /* Comparing the cert name and the connection hostname encoded as UTF-8
01627          * is acceptable since both values are assumed to use ASCII
01628          * (or some equivalent) encoding
01629          */
01630         cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname_buff);
01631         if(!cert_hostname) {
01632           result = CURLE_OUT_OF_MEMORY;
01633         }
01634         else{
01635           int match_result;
01636 
01637           match_result = Curl_cert_hostcheck(cert_hostname, conn->host.name);
01638           if(match_result == CURL_HOST_MATCH) {
01639             infof(data,
01640                   "schannel: connection hostname (%s) validated "
01641                   "against certificate name (%s)\n",
01642                   conn->host.name,
01643                   cert_hostname);
01644             result = CURLE_OK;
01645           }
01646           else{
01647             failf(data,
01648                   "schannel: connection hostname (%s) "
01649                   "does not match certificate name (%s)",
01650                   conn->host.name,
01651                   cert_hostname);
01652             result = CURLE_PEER_FAILED_VERIFICATION;
01653           }
01654           Curl_unicodefree(cert_hostname);
01655         }
01656       }
01657       else {
01658         failf(data,
01659               "schannel: CertGetNameString did not provide any "
01660               "certificate name information");
01661         result = CURLE_PEER_FAILED_VERIFICATION;
01662       }
01663     }
01664   }
01665 
01666   if(pChainContext)
01667     CertFreeCertificateChain(pChainContext);
01668 
01669   if(pCertContextServer)
01670     CertFreeCertificateContext(pCertContextServer);
01671 
01672   return result;
01673 }
01674 #endif /* _WIN32_WCE */
01675 
01676 #endif /* USE_SCHANNEL */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:06