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