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