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