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