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