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
85static Curl_recv schannel_recv;
86static Curl_send schannel_send;
87
88#ifdef _WIN32_WCE
89static CURLcode verify_certificate(struct connectdata *conn, int sockindex);
90#endif
91
92static 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
100static 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
108static CURLcode
109schannel_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
279static CURLcode
280schannel_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
484static CURLcode
485schannel_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      infof(data, "schannel: stored credential handle in session cache\n");
538    }
539  }
540
541  connssl->connecting_state = ssl_connect_done;
542
543  return CURLE_OK;
544}
545
546static CURLcode
547schannel_connect_common(struct connectdata *conn, int sockindex,
548                        bool nonblocking, bool *done)
549{
550  CURLcode retcode;
551  struct SessionHandle *data = conn->data;
552  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
553  curl_socket_t sockfd = conn->sock[sockindex];
554  long timeout_ms;
555  int what;
556
557  /* check if the connection has already been established */
558  if(ssl_connection_complete == connssl->state) {
559    *done = TRUE;
560    return CURLE_OK;
561  }
562
563  if(ssl_connect_1 == connssl->connecting_state) {
564    /* check out how much more time we're allowed */
565    timeout_ms = Curl_timeleft(data, NULL, TRUE);
566
567    if(timeout_ms < 0) {
568      /* no need to continue if time already is up */
569      failf(data, "SSL/TLS connection timeout");
570      return CURLE_OPERATION_TIMEDOUT;
571    }
572
573    retcode = schannel_connect_step1(conn, sockindex);
574    if(retcode)
575      return retcode;
576  }
577
578  while(ssl_connect_2 == connssl->connecting_state ||
579        ssl_connect_2_reading == connssl->connecting_state ||
580        ssl_connect_2_writing == connssl->connecting_state) {
581
582    /* check out how much more time we're allowed */
583    timeout_ms = Curl_timeleft(data, NULL, TRUE);
584
585    if(timeout_ms < 0) {
586      /* no need to continue if time already is up */
587      failf(data, "SSL/TLS connection timeout");
588      return CURLE_OPERATION_TIMEDOUT;
589    }
590
591    /* if ssl is expecting something, check if it's available. */
592    if(connssl->connecting_state == ssl_connect_2_reading
593       || connssl->connecting_state == ssl_connect_2_writing) {
594
595      curl_socket_t writefd = ssl_connect_2_writing ==
596        connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
597      curl_socket_t readfd = ssl_connect_2_reading ==
598        connssl->connecting_state ? sockfd : CURL_SOCKET_BAD;
599
600      what = Curl_socket_ready(readfd, writefd, nonblocking ? 0 : timeout_ms);
601      if(what < 0) {
602        /* fatal error */
603        failf(data, "select/poll on SSL/TLS socket, errno: %d", SOCKERRNO);
604        return CURLE_SSL_CONNECT_ERROR;
605      }
606      else if(0 == what) {
607        if(nonblocking) {
608          *done = FALSE;
609          return CURLE_OK;
610        }
611        else {
612          /* timeout */
613          failf(data, "SSL/TLS connection timeout");
614          return CURLE_OPERATION_TIMEDOUT;
615        }
616      }
617      /* socket is readable or writable */
618    }
619
620    /* Run transaction, and return to the caller if it failed or if
621     * this connection is part of a multi handle and this loop would
622     * execute again. This permits the owner of a multi handle to
623     * abort a connection attempt before step2 has completed while
624     * ensuring that a client using select() or epoll() will always
625     * have a valid fdset to wait on.
626     */
627    retcode = schannel_connect_step2(conn, sockindex);
628    if(retcode || (nonblocking &&
629                   (ssl_connect_2 == connssl->connecting_state ||
630                    ssl_connect_2_reading == connssl->connecting_state ||
631                    ssl_connect_2_writing == connssl->connecting_state)))
632      return retcode;
633
634  } /* repeat step2 until all transactions are done. */
635
636  if(ssl_connect_3 == connssl->connecting_state) {
637    retcode = schannel_connect_step3(conn, sockindex);
638    if(retcode)
639      return retcode;
640  }
641
642  if(ssl_connect_done == connssl->connecting_state) {
643    connssl->state = ssl_connection_complete;
644    conn->recv[sockindex] = schannel_recv;
645    conn->send[sockindex] = schannel_send;
646    *done = TRUE;
647  }
648  else
649    *done = FALSE;
650
651  /* reset our connection state machine */
652  connssl->connecting_state = ssl_connect_1;
653
654  return CURLE_OK;
655}
656
657static ssize_t
658schannel_send(struct connectdata *conn, int sockindex,
659              const void *buf, size_t len, CURLcode *err)
660{
661  ssize_t written = -1;
662  size_t data_len = 0;
663  unsigned char *data = NULL;
664  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
665  SecBuffer outbuf[4];
666  SecBufferDesc outbuf_desc;
667  SECURITY_STATUS sspi_status = SEC_E_OK;
668  CURLcode code;
669
670  /* check if the maximum stream sizes were queried */
671  if(connssl->stream_sizes.cbMaximumMessage == 0) {
672    sspi_status = s_pSecFn->QueryContextAttributes(
673                              &connssl->ctxt->ctxt_handle,
674                              SECPKG_ATTR_STREAM_SIZES,
675                              &connssl->stream_sizes);
676    if(sspi_status != SEC_E_OK) {
677      *err = CURLE_SEND_ERROR;
678      return -1;
679    }
680  }
681
682  /* check if the buffer is longer than the maximum message length */
683  if(len > connssl->stream_sizes.cbMaximumMessage) {
684    *err = CURLE_SEND_ERROR;
685    return -1;
686  }
687
688  /* calculate the complete message length and allocate a buffer for it */
689  data_len = connssl->stream_sizes.cbHeader + len +
690              connssl->stream_sizes.cbTrailer;
691  data = (unsigned char*) malloc(data_len);
692  if(data == NULL) {
693    *err = CURLE_OUT_OF_MEMORY;
694    return -1;
695  }
696
697  /* setup output buffers (header, data, trailer, empty) */
698  InitSecBuffer(&outbuf[0], SECBUFFER_STREAM_HEADER,
699                data, connssl->stream_sizes.cbHeader);
700  InitSecBuffer(&outbuf[1], SECBUFFER_DATA,
701                data + connssl->stream_sizes.cbHeader, curlx_uztoul(len));
702  InitSecBuffer(&outbuf[2], SECBUFFER_STREAM_TRAILER,
703                data + connssl->stream_sizes.cbHeader + len,
704                connssl->stream_sizes.cbTrailer);
705  InitSecBuffer(&outbuf[3], SECBUFFER_EMPTY, NULL, 0);
706  InitSecBufferDesc(&outbuf_desc, outbuf, 4);
707
708  /* copy data into output buffer */
709  memcpy(outbuf[1].pvBuffer, buf, len);
710
711  /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375390.aspx */
712  sspi_status = s_pSecFn->EncryptMessage(&connssl->ctxt->ctxt_handle, 0,
713                                         &outbuf_desc, 0);
714
715  /* check if the message was encrypted */
716  if(sspi_status == SEC_E_OK) {
717    written = 0;
718
719    /* send the encrypted message including header, data and trailer */
720    len = outbuf[0].cbBuffer + outbuf[1].cbBuffer + outbuf[2].cbBuffer;
721
722    /*
723       It's important to send the full message which includes the header,
724       encrypted payload, and trailer.  Until the client receives all the
725       data a coherent message has not been delivered and the client
726       can't read any of it.
727
728       If we wanted to buffer the unwritten encrypted bytes, we would
729       tell the client that all data it has requested to be sent has been
730       sent. The unwritten encrypted bytes would be the first bytes to
731       send on the next invocation.
732       Here's the catch with this - if we tell the client that all the
733       bytes have been sent, will the client call this method again to
734       send the buffered data?  Looking at who calls this function, it
735       seems the answer is NO.
736    */
737
738    /* send entire message or fail */
739    while(len > (size_t)written) {
740      ssize_t this_write;
741      long timeleft;
742      int what;
743
744      this_write = 0;
745
746      timeleft = Curl_timeleft(conn->data, NULL, TRUE);
747      if(timeleft < 0) {
748        /* we already got the timeout */
749        failf(conn->data, "schannel: timed out sending data "
750              "(bytes sent: %zd)", written);
751        *err = CURLE_OPERATION_TIMEDOUT;
752        written = -1;
753        break;
754      }
755
756      what = Curl_socket_ready(CURL_SOCKET_BAD, conn->sock[sockindex],
757                               timeleft);
758      if(what < 0) {
759        /* fatal error */
760        failf(conn->data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
761        *err = CURLE_SEND_ERROR;
762        written = -1;
763        break;
764      }
765      else if(0 == what) {
766        failf(conn->data, "schannel: timed out sending data "
767              "(bytes sent: %zd)", written);
768        *err = CURLE_OPERATION_TIMEDOUT;
769        written = -1;
770        break;
771      }
772      /* socket is writable */
773
774      code = Curl_write_plain(conn, conn->sock[sockindex], data + written,
775                              len - written, &this_write);
776      if(code == CURLE_AGAIN)
777        continue;
778      else if(code != CURLE_OK) {
779        *err = code;
780        written = -1;
781        break;
782      }
783
784      written += this_write;
785    }
786  }
787  else if(sspi_status == SEC_E_INSUFFICIENT_MEMORY) {
788    *err = CURLE_OUT_OF_MEMORY;
789  }
790  else{
791    *err = CURLE_SEND_ERROR;
792  }
793
794  Curl_safefree(data);
795
796  if(len == (size_t)written)
797    /* Encrypted message including header, data and trailer entirely sent.
798       The return value is the number of unencrypted bytes that were sent. */
799    written = outbuf[1].cbBuffer;
800
801  return written;
802}
803
804static ssize_t
805schannel_recv(struct connectdata *conn, int sockindex,
806              char *buf, size_t len, CURLcode *err)
807{
808  size_t size = 0;
809  ssize_t nread = 0, ret = -1;
810  CURLcode retcode;
811  struct SessionHandle *data = conn->data;
812  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
813  bool done = FALSE;
814  SecBuffer inbuf[4];
815  SecBufferDesc inbuf_desc;
816  SECURITY_STATUS sspi_status = SEC_E_OK;
817
818  infof(data, "schannel: client wants to read %zu bytes\n", len);
819  *err = CURLE_OK;
820
821  /* buffer to store previously received and decrypted data */
822  if(connssl->decdata_buffer == NULL) {
823    connssl->decdata_offset = 0;
824    connssl->decdata_length = CURL_SCHANNEL_BUFFER_INIT_SIZE;
825    connssl->decdata_buffer = malloc(connssl->decdata_length);
826    if(connssl->decdata_buffer == NULL) {
827      failf(data, "schannel: unable to allocate memory");
828      *err = CURLE_OUT_OF_MEMORY;
829      return -1;
830    }
831  }
832
833  /* increase buffer in order to fit the requested amount of data */
834  while(connssl->encdata_length - connssl->encdata_offset <
835        CURL_SCHANNEL_BUFFER_FREE_SIZE || connssl->encdata_length < len) {
836    /* increase internal encrypted data buffer */
837    connssl->encdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
838    connssl->encdata_buffer = realloc(connssl->encdata_buffer,
839                                      connssl->encdata_length);
840
841    if(connssl->encdata_buffer == NULL) {
842      failf(data, "schannel: unable to re-allocate memory");
843      *err = CURLE_OUT_OF_MEMORY;
844      return -1;
845    }
846  }
847
848  /* read encrypted data from socket */
849  infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
850        connssl->encdata_offset, connssl->encdata_length);
851  size = connssl->encdata_length - connssl->encdata_offset;
852  if(size > 0) {
853    *err = Curl_read_plain(conn->sock[sockindex],
854                  (char *) (connssl->encdata_buffer + connssl->encdata_offset),
855                           size, &nread);
856    /* check for received data */
857    if(*err != CURLE_OK)
858      ret = -1;
859    else {
860      if(nread > 0)
861        /* increase encrypted data buffer offset */
862        connssl->encdata_offset += nread;
863      ret = nread;
864    }
865    infof(data, "schannel: encrypted data got %zd\n", ret);
866  }
867
868  infof(data, "schannel: encrypted data buffer: offset %zu length %zu\n",
869        connssl->encdata_offset, connssl->encdata_length);
870
871  /* check if we still have some data in our buffers */
872  while(connssl->encdata_offset > 0 && sspi_status == SEC_E_OK &&
873        connssl->decdata_offset < len) {
874    /* prepare data buffer for DecryptMessage call */
875    InitSecBuffer(&inbuf[0], SECBUFFER_DATA, connssl->encdata_buffer,
876                  curlx_uztoul(connssl->encdata_offset));
877
878    /* we need 3 more empty input buffers for possible output */
879    InitSecBuffer(&inbuf[1], SECBUFFER_EMPTY, NULL, 0);
880    InitSecBuffer(&inbuf[2], SECBUFFER_EMPTY, NULL, 0);
881    InitSecBuffer(&inbuf[3], SECBUFFER_EMPTY, NULL, 0);
882
883    InitSecBufferDesc(&inbuf_desc, inbuf, 4);
884
885    /* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375348.aspx */
886    sspi_status = s_pSecFn->DecryptMessage(&connssl->ctxt->ctxt_handle,
887                                           &inbuf_desc, 0, NULL);
888
889    /* check if we need more data */
890    if(sspi_status == SEC_E_INCOMPLETE_MESSAGE) {
891      infof(data, "schannel: failed to decrypt data, need more data\n");
892      *err = CURLE_AGAIN;
893      return -1;
894    }
895
896    /* check if everything went fine (server may want to renegotiate
897       context) */
898    if(sspi_status == SEC_E_OK || sspi_status == SEC_I_RENEGOTIATE ||
899                                  sspi_status == SEC_I_CONTEXT_EXPIRED) {
900      /* check for successfully decrypted data */
901      if(inbuf[1].BufferType == SECBUFFER_DATA) {
902        infof(data, "schannel: decrypted data length: %lu\n",
903              inbuf[1].cbBuffer);
904
905        /* increase buffer in order to fit the received amount of data */
906        size = inbuf[1].cbBuffer > CURL_SCHANNEL_BUFFER_FREE_SIZE ?
907               inbuf[1].cbBuffer : CURL_SCHANNEL_BUFFER_FREE_SIZE;
908        while(connssl->decdata_length - connssl->decdata_offset < size ||
909              connssl->decdata_length < len) {
910          /* increase internal decrypted data buffer */
911          connssl->decdata_length *= CURL_SCHANNEL_BUFFER_STEP_FACTOR;
912          connssl->decdata_buffer = realloc(connssl->decdata_buffer,
913                                            connssl->decdata_length);
914
915          if(connssl->decdata_buffer == NULL) {
916            failf(data, "schannel: unable to re-allocate memory");
917            *err = CURLE_OUT_OF_MEMORY;
918            return -1;
919          }
920        }
921
922        /* copy decrypted data to internal buffer */
923        size = inbuf[1].cbBuffer;
924        if(size > 0) {
925          memcpy(connssl->decdata_buffer + connssl->decdata_offset,
926                 inbuf[1].pvBuffer, size);
927          connssl->decdata_offset += size;
928        }
929
930        infof(data, "schannel: decrypted data added: %zu\n", size);
931        infof(data, "schannel: decrypted data cached: offset %zu length %zu\n",
932              connssl->decdata_offset, connssl->decdata_length);
933      }
934
935      /* check for remaining encrypted data */
936      if(inbuf[3].BufferType == SECBUFFER_EXTRA && inbuf[3].cbBuffer > 0) {
937        infof(data, "schannel: encrypted data length: %lu\n",
938              inbuf[3].cbBuffer);
939
940        /* check if the remaining data is less than the total amount
941         * and therefore begins after the already processed data
942        */
943        if(connssl->encdata_offset > inbuf[3].cbBuffer) {
944          /* move remaining encrypted data forward to the beginning of
945             buffer */
946          memmove(connssl->encdata_buffer,
947                  (connssl->encdata_buffer + connssl->encdata_offset) -
948                    inbuf[3].cbBuffer, inbuf[3].cbBuffer);
949          connssl->encdata_offset = inbuf[3].cbBuffer;
950        }
951
952        infof(data, "schannel: encrypted data cached: offset %zu length %zu\n",
953              connssl->encdata_offset, connssl->encdata_length);
954      }
955      else{
956        /* reset encrypted buffer offset, because there is no data remaining */
957        connssl->encdata_offset = 0;
958      }
959    }
960
961    /* check if server wants to renegotiate the connection context */
962    if(sspi_status == SEC_I_RENEGOTIATE) {
963      infof(data, "schannel: remote party requests SSL/TLS renegotiation\n");
964
965      /* begin renegotiation */
966      infof(data, "schannel: renegotiating SSL/TLS connection\n");
967      connssl->state = ssl_connection_negotiating;
968      connssl->connecting_state = ssl_connect_2_writing;
969      retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
970      if(retcode)
971        *err = retcode;
972      else {
973        infof(data, "schannel: SSL/TLS connection renegotiated\n");
974        /* now retry receiving data */
975        return schannel_recv(conn, sockindex, buf, len, err);
976      }
977    }
978  }
979
980  infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
981        connssl->decdata_offset, connssl->decdata_length);
982
983  /* copy requested decrypted data to supplied buffer */
984  size = len < connssl->decdata_offset ? len : connssl->decdata_offset;
985  if(size > 0) {
986    memcpy(buf, connssl->decdata_buffer, size);
987    ret = size;
988
989    /* move remaining decrypted data forward to the beginning of buffer */
990    memmove(connssl->decdata_buffer, connssl->decdata_buffer + size,
991            connssl->decdata_offset - size);
992    connssl->decdata_offset -= size;
993
994    infof(data, "schannel: decrypted data returned %zd\n", size);
995    infof(data, "schannel: decrypted data buffer: offset %zu length %zu\n",
996          connssl->decdata_offset, connssl->decdata_length);
997  }
998
999  /* check if the server closed the connection */
1000  if(ret <= 0 && ( /* special check for Windows 2000 Professional */
1001      sspi_status == SEC_I_CONTEXT_EXPIRED || (sspi_status == SEC_E_OK &&
1002        connssl->encdata_offset > 0 && connssl->encdata_buffer[0] == 0x15))) {
1003    infof(data, "schannel: server closed the connection\n");
1004    *err = CURLE_OK;
1005    return 0;
1006  }
1007
1008  /* check if something went wrong and we need to return an error */
1009  if(ret < 0 && sspi_status != SEC_E_OK) {
1010    infof(data, "schannel: failed to read data from server: %s\n",
1011          Curl_sspi_strerror(conn, sspi_status));
1012    *err = CURLE_RECV_ERROR;
1013    return -1;
1014  }
1015
1016  return ret;
1017}
1018
1019CURLcode
1020Curl_schannel_connect_nonblocking(struct connectdata *conn, int sockindex,
1021                                  bool *done)
1022{
1023  return schannel_connect_common(conn, sockindex, TRUE, done);
1024}
1025
1026CURLcode
1027Curl_schannel_connect(struct connectdata *conn, int sockindex)
1028{
1029  CURLcode retcode;
1030  bool done = FALSE;
1031
1032  retcode = schannel_connect_common(conn, sockindex, FALSE, &done);
1033  if(retcode)
1034    return retcode;
1035
1036  DEBUGASSERT(done);
1037
1038  return CURLE_OK;
1039}
1040
1041bool Curl_schannel_data_pending(const struct connectdata *conn, int sockindex)
1042{
1043  const struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1044
1045  if(connssl->use) /* SSL/TLS is in use */
1046    return (connssl->encdata_offset > 0 ||
1047            connssl->decdata_offset > 0 ) ? TRUE : FALSE;
1048  else
1049    return FALSE;
1050}
1051
1052void Curl_schannel_close(struct connectdata *conn, int sockindex)
1053{
1054  if(conn->ssl[sockindex].use)
1055    /* if the SSL/TLS channel hasn't been shut down yet, do that now. */
1056    Curl_ssl_shutdown(conn, sockindex);
1057}
1058
1059int Curl_schannel_shutdown(struct connectdata *conn, int sockindex)
1060{
1061  /* See http://msdn.microsoft.com/en-us/library/windows/desktop/aa380138.aspx
1062   * Shutting Down an Schannel Connection
1063   */
1064  struct SessionHandle *data = conn->data;
1065  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1066  struct curl_schannel_cred *cached_cred = NULL;
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 refcount is zero, check if we have not cached it */
1145      if(connssl->cred->refcount == 0) {
1146        if(Curl_ssl_getsessionid(conn, (void**)&cached_cred, NULL)) {
1147          cached_cred = NULL;
1148        }
1149        /* if the handle was not cached, it is stale to be freed */
1150        if(connssl->cred != cached_cred) {
1151          infof(data, "schannel: clear credential handle\n");
1152          s_pSecFn->FreeCredentialsHandle(&connssl->cred->cred_handle);
1153          Curl_safefree(connssl->cred);
1154        }
1155      }
1156    }
1157  }
1158
1159  /* free internal buffer for received encrypted data */
1160  if(connssl->encdata_buffer != NULL) {
1161    Curl_safefree(connssl->encdata_buffer);
1162    connssl->encdata_length = 0;
1163    connssl->encdata_offset = 0;
1164  }
1165
1166  /* free internal buffer for received decrypted data */
1167  if(connssl->decdata_buffer != NULL) {
1168    Curl_safefree(connssl->decdata_buffer);
1169    connssl->decdata_length = 0;
1170    connssl->decdata_offset = 0;
1171  }
1172
1173  return CURLE_OK;
1174}
1175
1176void Curl_schannel_session_free(void *ptr)
1177{
1178  struct curl_schannel_cred *cred = ptr;
1179
1180  if(cred && cred->refcount == 0) {
1181    s_pSecFn->FreeCredentialsHandle(&cred->cred_handle);
1182    Curl_safefree(cred);
1183  }
1184}
1185
1186int Curl_schannel_init(void)
1187{
1188  return (Curl_sspi_global_init() == CURLE_OK ? 1 : 0);
1189}
1190
1191void Curl_schannel_cleanup(void)
1192{
1193  Curl_sspi_global_cleanup();
1194}
1195
1196size_t Curl_schannel_version(char *buffer, size_t size)
1197{
1198  size = snprintf(buffer, size, "WinSSL");
1199
1200  return size;
1201}
1202
1203#ifdef _WIN32_WCE
1204static CURLcode verify_certificate(struct connectdata *conn, int sockindex)
1205{
1206  SECURITY_STATUS status;
1207  struct SessionHandle *data = conn->data;
1208  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
1209  CURLcode result = CURLE_OK;
1210  CERT_CONTEXT *pCertContextServer = NULL;
1211  const CERT_CHAIN_CONTEXT *pChainContext = NULL;
1212
1213  status = s_pSecFn->QueryContextAttributes(&connssl->ctxt->ctxt_handle,
1214                                            SECPKG_ATTR_REMOTE_CERT_CONTEXT,
1215                                            &pCertContextServer);
1216
1217  if((status != SEC_E_OK) || (pCertContextServer == NULL)) {
1218    failf(data, "schannel: Failed to read remote certificate context: %s",
1219          Curl_sspi_strerror(conn, status));
1220    result = CURLE_PEER_FAILED_VERIFICATION;
1221  }
1222
1223  if(result == CURLE_OK) {
1224    CERT_CHAIN_PARA ChainPara;
1225    memset(&ChainPara, 0, sizeof(ChainPara));
1226    ChainPara.cbSize = sizeof(ChainPara);
1227
1228    if(!CertGetCertificateChain(NULL,
1229                                pCertContextServer,
1230                                NULL,
1231                                pCertContextServer->hCertStore,
1232                                &ChainPara,
1233                                0,
1234                                NULL,
1235                                &pChainContext)) {
1236      failf(data, "schannel: CertGetCertificateChain failed: %s",
1237            Curl_sspi_strerror(conn, GetLastError()));
1238      pChainContext = NULL;
1239      result = CURLE_PEER_FAILED_VERIFICATION;
1240    }
1241
1242    if(result == CURLE_OK) {
1243      CERT_SIMPLE_CHAIN *pSimpleChain = pChainContext->rgpChain[0];
1244      DWORD dwTrustErrorMask = ~(DWORD)(CERT_TRUST_IS_NOT_TIME_NESTED|
1245                                 CERT_TRUST_REVOCATION_STATUS_UNKNOWN);
1246      dwTrustErrorMask &= pSimpleChain->TrustStatus.dwErrorStatus;
1247      if(dwTrustErrorMask) {
1248        if(dwTrustErrorMask & CERT_TRUST_IS_PARTIAL_CHAIN)
1249          failf(data, "schannel: CertGetCertificateChain trust error"
1250                      " CERT_TRUST_IS_PARTIAL_CHAIN");
1251        if(dwTrustErrorMask & CERT_TRUST_IS_UNTRUSTED_ROOT)
1252          failf(data, "schannel: CertGetCertificateChain trust error"
1253                      " CERT_TRUST_IS_UNTRUSTED_ROOT");
1254        if(dwTrustErrorMask & CERT_TRUST_IS_NOT_TIME_VALID)
1255          failf(data, "schannel: CertGetCertificateChain trust error"
1256                      " CERT_TRUST_IS_NOT_TIME_VALID");
1257        failf(data, "schannel: CertGetCertificateChain error mask: 0x%08x",
1258              dwTrustErrorMask);
1259        result = CURLE_PEER_FAILED_VERIFICATION;
1260      }
1261    }
1262  }
1263
1264  if(result == CURLE_OK) {
1265    if(data->set.ssl.verifyhost) {
1266      TCHAR cert_hostname_buff[128];
1267      xcharp_u hostname;
1268      xcharp_u cert_hostname;
1269      DWORD len;
1270
1271      cert_hostname.const_tchar_ptr = cert_hostname_buff;
1272      hostname.tchar_ptr = Curl_convert_UTF8_to_tchar(conn->host.name);
1273
1274      len = CertGetNameString(pCertContextServer,
1275                              CERT_NAME_DNS_TYPE,
1276                              0,
1277                              NULL,
1278                              cert_hostname.tchar_ptr,
1279                              128);
1280      if(len > 0 && *cert_hostname.tchar_ptr == '*') {
1281        /* this is a wildcard cert.  try matching the last len - 1 chars */
1282        int hostname_len = strlen(conn->host.name);
1283        cert_hostname.tchar_ptr++;
1284        if(_tcsicmp(cert_hostname.const_tchar_ptr,
1285                    hostname.const_tchar_ptr + hostname_len - len + 2) != 0)
1286          result = CURLE_PEER_FAILED_VERIFICATION;
1287      }
1288      else if(len == 0 || _tcsicmp(hostname.const_tchar_ptr,
1289                                   cert_hostname.const_tchar_ptr) != 0) {
1290        result = CURLE_PEER_FAILED_VERIFICATION;
1291      }
1292      if(result == CURLE_PEER_FAILED_VERIFICATION) {
1293        char *_cert_hostname;
1294        _cert_hostname = Curl_convert_tchar_to_UTF8(cert_hostname.tchar_ptr);
1295        failf(data, "schannel: CertGetNameString() certificate hostname "
1296              "(%s) did not match connection (%s)",
1297              _cert_hostname, conn->host.name);
1298        Curl_unicodefree(_cert_hostname);
1299      }
1300      Curl_unicodefree(hostname.tchar_ptr);
1301    }
1302  }
1303
1304  if(pChainContext)
1305    CertFreeCertificateChain(pChainContext);
1306
1307  if(pCertContextServer)
1308    CertFreeCertificateContext(pCertContextServer);
1309
1310  return result;
1311}
1312#endif /* _WIN32_WCE */
1313
1314#endif /* USE_SCHANNEL */
1315