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