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