1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 ***************************************************************************/
22
23/*
24 * Source file for all CyaSSL-specific code for the TLS/SSL layer. No code
25 * but sslgen.c should ever call or use these functions.
26 *
27 */
28
29#include "curl_setup.h"
30
31#ifdef USE_CYASSL
32
33#ifdef HAVE_LIMITS_H
34#include <limits.h>
35#endif
36
37#include "urldata.h"
38#include "sendf.h"
39#include "inet_pton.h"
40#include "cyassl.h"
41#include "sslgen.h"
42#include "parsedate.h"
43#include "connect.h" /* for the connect timeout */
44#include "select.h"
45#include "rawstr.h"
46
47#define _MPRINTF_REPLACE /* use our functions only */
48#include <curl/mprintf.h>
49#include "curl_memory.h"
50/* The last #include file should be: */
51#include "memdebug.h"
52#include <cyassl/ssl.h>
53#include <cyassl/error.h>
54
55
56static Curl_recv cyassl_recv;
57static Curl_send cyassl_send;
58
59
60static int do_file_type(const char *type)
61{
62  if(!type || !type[0])
63    return SSL_FILETYPE_PEM;
64  if(Curl_raw_equal(type, "PEM"))
65    return SSL_FILETYPE_PEM;
66  if(Curl_raw_equal(type, "DER"))
67    return SSL_FILETYPE_ASN1;
68  return -1;
69}
70
71/*
72 * This function loads all the client/CA certificates and CRLs. Setup the TLS
73 * layer and do all necessary magic.
74 */
75static CURLcode
76cyassl_connect_step1(struct connectdata *conn,
77                     int sockindex)
78{
79  struct SessionHandle *data = conn->data;
80  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
81  SSL_METHOD* req_method = NULL;
82  void* ssl_sessionid = NULL;
83  curl_socket_t sockfd = conn->sock[sockindex];
84
85  if(conssl->state == ssl_connection_complete)
86    return CURLE_OK;
87
88  /* CyaSSL doesn't support SSLv2 */
89  if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) {
90    failf(data, "CyaSSL does not support SSLv2");
91    return CURLE_SSL_CONNECT_ERROR;
92  }
93
94  /* check to see if we've been told to use an explicit SSL/TLS version */
95  switch(data->set.ssl.version) {
96  case CURL_SSLVERSION_DEFAULT:
97    /* we try to figure out version */
98    req_method = SSLv23_client_method();
99    break;
100  case CURL_SSLVERSION_TLSv1:
101    req_method = TLSv1_client_method();
102    break;
103  case CURL_SSLVERSION_SSLv3:
104    req_method = SSLv3_client_method();
105    break;
106  default:
107    req_method = TLSv1_client_method();
108  }
109
110  if(!req_method) {
111    failf(data, "SSL: couldn't create a method!");
112    return CURLE_OUT_OF_MEMORY;
113  }
114
115  if(conssl->ctx)
116    SSL_CTX_free(conssl->ctx);
117  conssl->ctx = SSL_CTX_new(req_method);
118
119  if(!conssl->ctx) {
120    failf(data, "SSL: couldn't create a context!");
121    return CURLE_OUT_OF_MEMORY;
122  }
123
124#ifndef NO_FILESYSTEM
125  /* load trusted cacert */
126  if(data->set.str[STRING_SSL_CAFILE]) {
127    if(!SSL_CTX_load_verify_locations(conssl->ctx,
128                                      data->set.str[STRING_SSL_CAFILE],
129                                      data->set.str[STRING_SSL_CAPATH])) {
130      if(data->set.ssl.verifypeer) {
131        /* Fail if we insiste on successfully verifying the server. */
132        failf(data,"error setting certificate verify locations:\n"
133              "  CAfile: %s\n  CApath: %s",
134              data->set.str[STRING_SSL_CAFILE]?
135              data->set.str[STRING_SSL_CAFILE]: "none",
136              data->set.str[STRING_SSL_CAPATH]?
137              data->set.str[STRING_SSL_CAPATH] : "none");
138        return CURLE_SSL_CACERT_BADFILE;
139      }
140      else {
141        /* Just continue with a warning if no strict  certificate
142           verification is required. */
143        infof(data, "error setting certificate verify locations,"
144              " continuing anyway:\n");
145      }
146    }
147    else {
148      /* Everything is fine. */
149      infof(data, "successfully set certificate verify locations:\n");
150    }
151    infof(data,
152          "  CAfile: %s\n"
153          "  CApath: %s\n",
154          data->set.str[STRING_SSL_CAFILE] ? data->set.str[STRING_SSL_CAFILE]:
155          "none",
156          data->set.str[STRING_SSL_CAPATH] ? data->set.str[STRING_SSL_CAPATH]:
157          "none");
158  }
159
160  /* Load the client certificate, and private key */
161  if(data->set.str[STRING_CERT] && data->set.str[STRING_KEY]) {
162    int file_type = do_file_type(data->set.str[STRING_CERT_TYPE]);
163
164    if(SSL_CTX_use_certificate_file(conssl->ctx, data->set.str[STRING_CERT],
165                                     file_type) != 1) {
166      failf(data, "unable to use client certificate (no key or wrong pass"
167            " phrase?)");
168      return CURLE_SSL_CONNECT_ERROR;
169    }
170
171    file_type = do_file_type(data->set.str[STRING_KEY_TYPE]);
172    if(SSL_CTX_use_PrivateKey_file(conssl->ctx, data->set.str[STRING_KEY],
173                                    file_type) != 1) {
174      failf(data, "unable to set private key");
175      return CURLE_SSL_CONNECT_ERROR;
176    }
177  }
178#else
179  if(CyaSSL_no_filesystem_verify(conssl->ctx)!= SSL_SUCCESS) {
180    return CURLE_SSL_CONNECT_ERROR;
181  }
182#endif /* NO_FILESYSTEM */
183
184  /* SSL always tries to verify the peer, this only says whether it should
185   * fail to connect if the verification fails, or if it should continue
186   * anyway. In the latter case the result of the verification is checked with
187   * SSL_get_verify_result() below. */
188  SSL_CTX_set_verify(conssl->ctx,
189                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
190                     NULL);
191
192  /* Let's make an SSL structure */
193  if(conssl->handle)
194    SSL_free(conssl->handle);
195  conssl->handle = SSL_new(conssl->ctx);
196  if(!conssl->handle) {
197    failf(data, "SSL: couldn't create a context (handle)!");
198    return CURLE_OUT_OF_MEMORY;
199  }
200
201  /* Check if there's a cached ID we can/should use here! */
202  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
203    /* we got a session id, use it! */
204    if(!SSL_set_session(conssl->handle, ssl_sessionid)) {
205      failf(data, "SSL: SSL_set_session failed: %s",
206            ERR_error_string(SSL_get_error(conssl->handle, 0),NULL));
207      return CURLE_SSL_CONNECT_ERROR;
208    }
209    /* Informational message */
210    infof (data, "SSL re-using session ID\n");
211  }
212
213  /* pass the raw socket into the SSL layer */
214  if(!SSL_set_fd(conssl->handle, (int)sockfd)) {
215    failf(data, "SSL: SSL_set_fd failed");
216    return CURLE_SSL_CONNECT_ERROR;
217  }
218
219  conssl->connecting_state = ssl_connect_2;
220  return CURLE_OK;
221}
222
223
224static CURLcode
225cyassl_connect_step2(struct connectdata *conn,
226                     int sockindex)
227{
228  int ret = -1;
229  struct SessionHandle *data = conn->data;
230  struct ssl_connect_data* conssl = &conn->ssl[sockindex];
231
232  infof(data, "CyaSSL: Connecting to %s:%d\n",
233        conn->host.name, conn->remote_port);
234
235  conn->recv[sockindex] = cyassl_recv;
236  conn->send[sockindex] = cyassl_send;
237
238  /* Enable RFC2818 checks */
239  if(data->set.ssl.verifyhost) {
240    ret = CyaSSL_check_domain_name(conssl->handle, conn->host.name);
241    if(ret == SSL_FAILURE)
242      return CURLE_OUT_OF_MEMORY;
243  }
244
245  ret = SSL_connect(conssl->handle);
246  if(ret != 1) {
247    char error_buffer[80];
248    int  detail = SSL_get_error(conssl->handle, ret);
249
250    if(SSL_ERROR_WANT_READ == detail) {
251      conssl->connecting_state = ssl_connect_2_reading;
252      return CURLE_OK;
253    }
254    else if(SSL_ERROR_WANT_WRITE == detail) {
255      conssl->connecting_state = ssl_connect_2_writing;
256      return CURLE_OK;
257    }
258    /* There is no easy way to override only the CN matching.
259     * This will enable the override of both mismatching SubjectAltNames
260     * as also mismatching CN fields */
261    else if(DOMAIN_NAME_MISMATCH == detail) {
262#if 1
263      failf(data, "\tsubject alt name(s) or common name do not match \"%s\"\n",
264            conn->host.dispname);
265      return CURLE_PEER_FAILED_VERIFICATION;
266#else
267      /* When the CyaSSL_check_domain_name() is used and you desire to continue
268       * on a DOMAIN_NAME_MISMATCH, i.e. 'data->set.ssl.verifyhost == 0',
269       * CyaSSL version 2.4.0 will fail with an INCOMPLETE_DATA error. The only
270       * way to do this is currently to switch the CyaSSL_check_domain_name()
271       * in and out based on the 'data->set.ssl.verifyhost' value. */
272      if(data->set.ssl.verifyhost) {
273        failf(data,
274              "\tsubject alt name(s) or common name do not match \"%s\"\n",
275              conn->host.dispname);
276        return CURLE_PEER_FAILED_VERIFICATION;
277      }
278      else {
279        infof(data,
280              "\tsubject alt name(s) and/or common name do not match \"%s\"\n",
281              conn->host.dispname);
282        return CURLE_OK;
283      }
284#endif
285    }
286    else {
287      failf(data, "SSL_connect failed with error %d: %s", detail,
288          ERR_error_string(detail, error_buffer));
289      return CURLE_SSL_CONNECT_ERROR;
290    }
291  }
292
293  conssl->connecting_state = ssl_connect_3;
294  infof(data, "SSL connected\n");
295
296  return CURLE_OK;
297}
298
299
300static CURLcode
301cyassl_connect_step3(struct connectdata *conn,
302                     int sockindex)
303{
304  CURLcode retcode = CURLE_OK;
305  void *old_ssl_sessionid=NULL;
306  struct SessionHandle *data = conn->data;
307  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
308  int incache;
309  SSL_SESSION *our_ssl_sessionid;
310
311  DEBUGASSERT(ssl_connect_3 == connssl->connecting_state);
312
313  our_ssl_sessionid = SSL_get_session(connssl->handle);
314
315  incache = !(Curl_ssl_getsessionid(conn, &old_ssl_sessionid, NULL));
316  if(incache) {
317    if(old_ssl_sessionid != our_ssl_sessionid) {
318      infof(data, "old SSL session ID is stale, removing\n");
319      Curl_ssl_delsessionid(conn, old_ssl_sessionid);
320      incache = FALSE;
321    }
322  }
323  if(!incache) {
324    retcode = Curl_ssl_addsessionid(conn, our_ssl_sessionid,
325                                    0 /* unknown size */);
326    if(retcode) {
327      failf(data, "failed to store ssl session");
328      return retcode;
329    }
330  }
331
332  connssl->connecting_state = ssl_connect_done;
333
334  return retcode;
335}
336
337
338static ssize_t cyassl_send(struct connectdata *conn,
339                           int sockindex,
340                           const void *mem,
341                           size_t len,
342                           CURLcode *curlcode)
343{
344  char error_buffer[80];
345  int  memlen = (len > (size_t)INT_MAX) ? INT_MAX : (int)len;
346  int  rc     = SSL_write(conn->ssl[sockindex].handle, mem, memlen);
347
348  if(rc < 0) {
349    int err = SSL_get_error(conn->ssl[sockindex].handle, rc);
350
351    switch(err) {
352    case SSL_ERROR_WANT_READ:
353    case SSL_ERROR_WANT_WRITE:
354      /* there's data pending, re-invoke SSL_write() */
355      *curlcode = CURLE_AGAIN;
356      return -1;
357    default:
358      failf(conn->data, "SSL write: %s, errno %d",
359            ERR_error_string(err, error_buffer),
360            SOCKERRNO);
361      *curlcode = CURLE_SEND_ERROR;
362      return -1;
363    }
364  }
365  return rc;
366}
367
368void Curl_cyassl_close_all(struct SessionHandle *data)
369{
370  (void)data;
371}
372
373void Curl_cyassl_close(struct connectdata *conn, int sockindex)
374{
375  struct ssl_connect_data *conssl = &conn->ssl[sockindex];
376
377  if(conssl->handle) {
378    (void)SSL_shutdown(conssl->handle);
379    SSL_free (conssl->handle);
380    conssl->handle = NULL;
381  }
382  if(conssl->ctx) {
383    SSL_CTX_free (conssl->ctx);
384    conssl->ctx = NULL;
385  }
386}
387
388static ssize_t cyassl_recv(struct connectdata *conn,
389                           int num,
390                           char *buf,
391                           size_t buffersize,
392                           CURLcode *curlcode)
393{
394  char error_buffer[80];
395  int  buffsize = (buffersize > (size_t)INT_MAX) ? INT_MAX : (int)buffersize;
396  int  nread    = SSL_read(conn->ssl[num].handle, buf, buffsize);
397
398  if(nread < 0) {
399    int err = SSL_get_error(conn->ssl[num].handle, nread);
400
401    switch(err) {
402    case SSL_ERROR_ZERO_RETURN: /* no more data */
403      break;
404    case SSL_ERROR_WANT_READ:
405    case SSL_ERROR_WANT_WRITE:
406      /* there's data pending, re-invoke SSL_read() */
407      *curlcode = CURLE_AGAIN;
408      return -1;
409    default:
410      failf(conn->data, "SSL read: %s, errno %d",
411            ERR_error_string(err, error_buffer),
412            SOCKERRNO);
413      *curlcode = CURLE_RECV_ERROR;
414      return -1;
415    }
416  }
417  return nread;
418}
419
420
421void Curl_cyassl_session_free(void *ptr)
422{
423  (void)ptr;
424  /* CyaSSL reuses sessions on own, no free */
425}
426
427
428size_t Curl_cyassl_version(char *buffer, size_t size)
429{
430#ifdef CYASSL_VERSION
431  return snprintf(buffer, size, "CyaSSL/%s", CYASSL_VERSION);
432#else
433  return snprintf(buffer, size, "CyaSSL/%s", "<1.8.8");
434#endif
435}
436
437
438int Curl_cyassl_init(void)
439{
440  if(CyaSSL_Init() == 0)
441    return 1;
442
443  return -1;
444}
445
446
447bool Curl_cyassl_data_pending(const struct connectdata* conn, int connindex)
448{
449  if(conn->ssl[connindex].handle)   /* SSL is in use */
450    return (0 != SSL_pending(conn->ssl[connindex].handle)) ? TRUE : FALSE;
451  else
452    return FALSE;
453}
454
455
456/*
457 * This function is called to shut down the SSL layer but keep the
458 * socket open (CCC - Clear Command Channel)
459 */
460int Curl_cyassl_shutdown(struct connectdata *conn, int sockindex)
461{
462  int retval = 0;
463  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
464
465  if(connssl->handle) {
466    SSL_free (connssl->handle);
467    connssl->handle = NULL;
468  }
469  return retval;
470}
471
472
473static CURLcode
474cyassl_connect_common(struct connectdata *conn,
475                      int sockindex,
476                      bool nonblocking,
477                      bool *done)
478{
479  CURLcode retcode;
480  struct SessionHandle *data = conn->data;
481  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
482  curl_socket_t sockfd = conn->sock[sockindex];
483  long timeout_ms;
484  int what;
485
486  /* check if the connection has already been established */
487  if(ssl_connection_complete == connssl->state) {
488    *done = TRUE;
489    return CURLE_OK;
490  }
491
492  if(ssl_connect_1==connssl->connecting_state) {
493    /* Find out how much more time we're allowed */
494    timeout_ms = Curl_timeleft(data, NULL, TRUE);
495
496    if(timeout_ms < 0) {
497      /* no need to continue if time already is up */
498      failf(data, "SSL connection timeout");
499      return CURLE_OPERATION_TIMEDOUT;
500    }
501    retcode = cyassl_connect_step1(conn, sockindex);
502    if(retcode)
503      return retcode;
504  }
505
506  while(ssl_connect_2 == connssl->connecting_state ||
507        ssl_connect_2_reading == connssl->connecting_state ||
508        ssl_connect_2_writing == connssl->connecting_state) {
509
510    /* check allowed time left */
511    timeout_ms = Curl_timeleft(data, NULL, TRUE);
512
513    if(timeout_ms < 0) {
514      /* no need to continue if time already is up */
515      failf(data, "SSL connection timeout");
516      return CURLE_OPERATION_TIMEDOUT;
517    }
518
519    /* if ssl is expecting something, check if it's available. */
520    if(connssl->connecting_state == ssl_connect_2_reading
521       || connssl->connecting_state == ssl_connect_2_writing) {
522
523      curl_socket_t writefd = ssl_connect_2_writing==
524        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
525      curl_socket_t readfd = ssl_connect_2_reading==
526        connssl->connecting_state?sockfd:CURL_SOCKET_BAD;
527
528      what = Curl_socket_ready(readfd, writefd, nonblocking?0:timeout_ms);
529      if(what < 0) {
530        /* fatal error */
531        failf(data, "select/poll on SSL socket, errno: %d", SOCKERRNO);
532        return CURLE_SSL_CONNECT_ERROR;
533      }
534      else if(0 == what) {
535        if(nonblocking) {
536          *done = FALSE;
537          return CURLE_OK;
538        }
539        else {
540          /* timeout */
541          failf(data, "SSL connection timeout");
542          return CURLE_OPERATION_TIMEDOUT;
543        }
544      }
545      /* socket is readable or writable */
546    }
547
548    /* Run transaction, and return to the caller if it failed or if
549     * this connection is part of a multi handle and this loop would
550     * execute again. This permits the owner of a multi handle to
551     * abort a connection attempt before step2 has completed while
552     * ensuring that a client using select() or epoll() will always
553     * have a valid fdset to wait on.
554     */
555    retcode = cyassl_connect_step2(conn, sockindex);
556    if(retcode || (nonblocking &&
557                   (ssl_connect_2 == connssl->connecting_state ||
558                    ssl_connect_2_reading == connssl->connecting_state ||
559                    ssl_connect_2_writing == connssl->connecting_state)))
560      return retcode;
561
562  } /* repeat step2 until all transactions are done. */
563
564  if(ssl_connect_3==connssl->connecting_state) {
565    retcode = cyassl_connect_step3(conn, sockindex);
566    if(retcode)
567      return retcode;
568  }
569
570  if(ssl_connect_done==connssl->connecting_state) {
571    connssl->state = ssl_connection_complete;
572    conn->recv[sockindex] = cyassl_recv;
573    conn->send[sockindex] = cyassl_send;
574    *done = TRUE;
575  }
576  else
577    *done = FALSE;
578
579  /* Reset our connect state machine */
580  connssl->connecting_state = ssl_connect_1;
581
582  return CURLE_OK;
583}
584
585
586CURLcode
587Curl_cyassl_connect_nonblocking(struct connectdata *conn,
588                                int sockindex,
589                                bool *done)
590{
591  return cyassl_connect_common(conn, sockindex, TRUE, done);
592}
593
594
595CURLcode
596Curl_cyassl_connect(struct connectdata *conn,
597                    int sockindex)
598{
599  CURLcode retcode;
600  bool done = FALSE;
601
602  retcode = cyassl_connect_common(conn, sockindex, FALSE, &done);
603  if(retcode)
604    return retcode;
605
606  DEBUGASSERT(done);
607
608  return CURLE_OK;
609}
610
611#endif
612