7c78464d8a1590d4639b45460c499bab9e23cf4b
[platform/upstream/curl.git] / lib / cyassl.c
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
56 static Curl_recv cyassl_recv;
57 static Curl_send cyassl_send;
58
59
60 static 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  */
75 static CURLcode
76 cyassl_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
224 static CURLcode
225 cyassl_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
300 static CURLcode
301 cyassl_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
338 static 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
368 void Curl_cyassl_close_all(struct SessionHandle *data)
369 {
370   (void)data;
371 }
372
373 void 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
388 static 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
421 void Curl_cyassl_session_free(void *ptr)
422 {
423   (void)ptr;
424   /* CyaSSL reuses sessions on own, no free */
425 }
426
427
428 size_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
438 int Curl_cyassl_init(void)
439 {
440   if(CyaSSL_Init() == 0)
441     return 1;
442
443   return -1;
444 }
445
446
447 bool 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  */
460 int 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
473 static CURLcode
474 cyassl_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
586 CURLcode
587 Curl_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
595 CURLcode
596 Curl_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