From 51427e1947ddc07b4ce8ad9dcb04846125170f83 Mon Sep 17 00:00:00 2001 From: Hoi-Ho Chan Date: Wed, 5 May 2010 22:30:46 +0200 Subject: PolarSSL: initial support added This is Hoi-Ho Chan's patch with some minor fixes by me. There are some potential issues in this, but none worse than we can sort out on the list and over time. --- lib/Makefile.inc | 4 +- lib/curl_config.h.cmake | 3 + lib/http.c | 12 ++ lib/polarssl.c | 391 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/polarssl.h | 67 +++++++++ lib/setup.h | 2 +- lib/sslgen.c | 2 + lib/urldata.h | 17 +++ 8 files changed, 495 insertions(+), 3 deletions(-) create mode 100644 lib/polarssl.c create mode 100644 lib/polarssl.h (limited to 'lib') diff --git a/lib/Makefile.inc b/lib/Makefile.inc index f90e4dce0..e35e8bb57 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -12,7 +12,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ strdup.c socks.c ssh.c nss.c qssl.c rawstr.c curl_addrinfo.c \ socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c \ curl_memrchr.c imap.c pop3.c smtp.c pingpong.c rtsp.c curl_threads.c \ - warnless.c hmac.c + warnless.c hmac.c polarssl.c HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ @@ -25,4 +25,4 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h nssg.h \ curl_base64.h rawstr.h curl_addrinfo.h curl_sspi.h slist.h nonblock.h \ curl_memrchr.h imap.h pop3.h smtp.h pingpong.h rtsp.h curl_threads.h \ - warnless.h curl_hmac.h + warnless.h curl_hmac.h polarssl.h diff --git a/lib/curl_config.h.cmake b/lib/curl_config.h.cmake index fd6b32aa8..14d2719d3 100644 --- a/lib/curl_config.h.cmake +++ b/lib/curl_config.h.cmake @@ -889,6 +889,9 @@ /* if GnuTLS is enabled */ #cmakedefine USE_GNUTLS ${USE_GNUTLS} +/* if PolarSSL is enabled */ +#cmakedefine USE_POLARSSL ${USE_POLARSSL} + /* if libSSH2 is in use */ #cmakedefine USE_LIBSSH2 ${USE_LIBSSH2} diff --git a/lib/http.c b/lib/http.c index cfc09cd5e..fc01ee889 100644 --- a/lib/http.c +++ b/lib/http.c @@ -1865,6 +1865,18 @@ static int https_getsock(struct connectdata *conn, (void)numsocks; return GETSOCK_BLANK; } +#else +#ifdef USE_POLARSSL +static int https_getsock(struct connectdata *conn, + curl_socket_t *socks, + int numsocks) +{ + (void)conn; + (void)socks; + (void)numsocks; + return GETSOCK_BLANK; +} +#endif #endif #endif #endif diff --git a/lib/polarssl.c b/lib/polarssl.c new file mode 100644 index 000000000..95baa1320 --- /dev/null +++ b/lib/polarssl.c @@ -0,0 +1,391 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Hoi-Ho Chan, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ***************************************************************************/ + +/* + * Source file for all PolarSSL-specific code for the TLS/SSL layer. No code + * but sslgen.c should ever call or use these functions. + * + */ + +#include "setup.h" +#ifdef USE_POLARSSL + +#include +#include +#include +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "urldata.h" +#include "sendf.h" +#include "inet_pton.h" +#include "polarssl.h" +#include "sslgen.h" +#include "parsedate.h" +#include "connect.h" /* for the connect timeout */ +#include "select.h" +#include "rawstr.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include +#include "curl_memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* Define this to enable lots of debugging for PolarSSL */ +#undef POLARSSL_DEBUG + +#ifdef POLARSSL_DEBUG +static void polarssl_debug(void *context, int level, char *line) +{ + struct SessionHandle *data = NULL; + + if(!context) + return; + + data = (struct SessionHandle *)context; + + infof(data, "%s", line); +} +#else +#endif + +/* + * This function loads all the client/CA certificates and CRLs. Setup the TLS + * layer and do all necessary magic. + */ +CURLcode +Curl_polarssl_connect(struct connectdata *conn, + int sockindex) +{ + struct SessionHandle *data = conn->data; + bool sni = TRUE; /* default is SNI enabled */ + int ret = -1; +#ifdef ENABLE_IPV6 + struct in6_addr addr; +#else + struct in_addr addr; +#endif + void *old_session = NULL; + size_t old_session_size = 0; +#if defined(HAVE_POLARSSL_GPL) + char buffer[1024]; +#endif + + if(conn->ssl[sockindex].state == ssl_connection_complete) + return CURLE_OK; + + /* PolarSSL only supports SSLv3 and TLSv1 */ + if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { + failf(data, "PolarSSL does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + } else if(data->set.ssl.version == CURL_SSLVERSION_SSLv3) { + sni = FALSE; /* SSLv3 has no SNI */ + } + + havege_init(&conn->ssl[sockindex].hs); + + /* Load the trusted CA */ + memset(&conn->ssl[sockindex].cacert, 0, sizeof(x509_cert)); + + if(data->set.str[STRING_SSL_CAFILE]) { + ret = x509parse_crtfile(&conn->ssl[sockindex].cacert, + data->set.str[STRING_SSL_CAFILE]); + + if(ret) { + failf(data, "Error reading ca cert file %s: -0x%04X", + data->set.str[STRING_SSL_CAFILE], -ret); + + if(data->set.ssl.verifypeer) + return CURLE_SSL_CACERT_BADFILE; + } + } + + /* Load the client certificate */ + memset(&conn->ssl[sockindex].clicert, 0, sizeof(x509_cert)); + + if(data->set.str[STRING_CERT]) { +#if !defined(HAVE_POLARSSL_GPL) + /* FIXME: PolarSSL has a bug where we need to import it twice */ + ret = x509parse_crtfile(&conn->ssl[sockindex].clicert, + data->set.str[STRING_CERT]); +#endif + ret = x509parse_crtfile(&conn->ssl[sockindex].clicert, + data->set.str[STRING_CERT]); + + if(ret) { + failf(data, "Error reading client cert file %s: -0x%04X", + data->set.str[STRING_CERT], -ret); + return CURLE_SSL_CERTPROBLEM; + } + } + + /* Load the client private key */ + if(data->set.str[STRING_KEY]) { + ret = x509parse_keyfile(&conn->ssl[sockindex].rsa, + data->set.str[STRING_KEY], + data->set.str[STRING_KEY_PASSWD]); + + if(ret) { + failf(data, "Error reading private key %s: -0x%04X", + data->set.str[STRING_KEY], -ret); + return CURLE_SSL_CERTPROBLEM; + } + } + +#if defined(HAVE_POLARSSL_GPL) + /* Load the CRL */ + memset(&conn->ssl[sockindex].crl, 0, sizeof(x509_crl)); + + if(data->set.str[STRING_SSL_CRLFILE]) { + ret = x509parse_crlfile(&conn->ssl[sockindex].crl, + data->set.str[STRING_SSL_CRLFILE]); + + if(ret) { + failf(data, "Error reading CRL file %s: -0x%04X", + data->set.str[STRING_SSL_CRLFILE], -ret); + return CURLE_SSL_CRL_BADFILE; + } + } + +#endif + infof(data, "PolarSSL: Connected to %s:%d\n", + conn->host.name, conn->remote_port); + + havege_init(&conn->ssl[sockindex].hs); + + if(ssl_init(&conn->ssl[sockindex].ssl)) { + failf(data, "PolarSSL: ssl_init failed"); + return CURLE_SSL_CONNECT_ERROR; + } + + ssl_set_endpoint(&conn->ssl[sockindex].ssl, SSL_IS_CLIENT); + ssl_set_authmode(&conn->ssl[sockindex].ssl, SSL_VERIFY_OPTIONAL); + + ssl_set_rng(&conn->ssl[sockindex].ssl, havege_rand, + &conn->ssl[sockindex].hs); + ssl_set_bio(&conn->ssl[sockindex].ssl, + net_recv, &conn->sock[sockindex], + net_send, &conn->sock[sockindex]); + + ssl_set_ciphers(&conn->ssl[sockindex].ssl, ssl_default_ciphers); + + if(!Curl_ssl_getsessionid(conn, &old_session, &old_session_size)) { + memcpy(&conn->ssl[sockindex].ssn, old_session, old_session_size); + infof(data, "PolarSSL re-using session\n"); + } + + ssl_set_session(&conn->ssl[sockindex].ssl, 1, 600, + &conn->ssl[sockindex].ssn); + + ssl_set_ca_chain(&conn->ssl[sockindex].ssl, +#if defined(HAVE_POLARSSL_GPL) + &conn->ssl[sockindex].cacert, + &conn->ssl[sockindex].crl, + conn->host.name); +#else + &conn->ssl[sockindex].cacert, conn->host.name); +#endif + + ssl_set_own_cert(&conn->ssl[sockindex].ssl, + &conn->ssl[sockindex].clicert, &conn->ssl[sockindex].rsa); + + if(!Curl_inet_pton(AF_INET, conn->host.name, &addr) && +#ifdef ENABLE_IPV6 + !Curl_inet_pton(AF_INET6, conn->host.name, &addr) && +#endif + sni && ssl_set_hostname(&conn->ssl[sockindex].ssl, conn->host.name)) { + infof(data, "WARNING: failed to configure " + "server name indication (SNI) TLS extension\n"); + } + + infof(data, "PolarSSL: performing SSL/TLS handshake...\n"); + +#ifdef POLARSSL_DEBUG + ssl_set_dbg(&conn->ssl[sockindex].ssl, polarssl_debug, data); +#endif + + do { + if (!(ret = ssl_handshake(&conn->ssl[sockindex].ssl))) { + break; + } else if(ret != POLARSSL_ERR_NET_TRY_AGAIN) { + failf(data, "ssl_handshake returned -0x%04X", -ret); + return CURLE_SSL_CONNECT_ERROR; + } else { + /* wait for data from server... */ + long timeout_ms = Curl_timeleft(conn, NULL, TRUE); + + if(timeout_ms < 0) { + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + + switch(Curl_socket_ready(conn->sock[sockindex], + CURL_SOCKET_BAD, timeout_ms)) { + case 0: + failf(data, "SSL handshake timeout"); + return CURLE_OPERATION_TIMEDOUT; + break; + case CURL_CSELECT_IN: + continue; + break; + default: + return CURLE_SSL_CONNECT_ERROR; + break; + } + } + } while (1); + + infof(data, "PolarSSL: Handshake complete, cipher is %s\n", + ssl_get_cipher(&conn->ssl[sockindex].ssl)); + + ret = ssl_get_verify_result(&conn->ssl[sockindex].ssl); + + if(ret && data->set.ssl.verifypeer) { + if(ret & BADCERT_EXPIRED) + failf(data, "Cert verify failed: BADCERT_EXPIRED\n"); + + if(ret & BADCERT_REVOKED) + failf(data, "Cert verify failed: BADCERT_REVOKED"); + + if(ret & BADCERT_CN_MISMATCH) + failf(data, "Cert verify failed: BADCERT_CN_MISMATCH"); + + if(ret & BADCERT_NOT_TRUSTED) + failf(data, "Cert verify failed: BADCERT_NOT_TRUSTED"); + + return CURLE_SSL_CACERT; + } + + if(conn->ssl[sockindex].ssl.peer_cert) { + /* If the session was resumed, there will be no peer certs */ +#if !defined(HAVE_POLARSSL_GPL) + char *buffer = x509parse_cert_info("* ", conn->ssl[sockindex].ssl.peer_cert); + + if(buffer) +#else + memset(buffer, 0, sizeof(buffer)); + + if(x509parse_cert_info(buffer, sizeof(buffer), (char *)"* ", + conn->ssl[sockindex].ssl.peer_cert) != -1) +#endif + infof(data, "Dumping cert info:\n%s\n", buffer); + } + + conn->ssl[sockindex].state = ssl_connection_complete; + + /* Save the current session data for possible re-use */ + { + void *new_session = malloc(sizeof(conn->ssl[sockindex].ssn)); + + if(new_session) { + memcpy(new_session, &conn->ssl[sockindex].ssn, + sizeof(conn->ssl[sockindex].ssn)); + + if(old_session) + Curl_ssl_delsessionid(conn, old_session); + + return Curl_ssl_addsessionid(conn, new_session, + sizeof(conn->ssl[sockindex].ssn)); + } + } + + return CURLE_OK; +} + +/* for documentation see Curl_ssl_send() in sslgen.h */ +ssize_t Curl_polarssl_send(struct connectdata *conn, + int sockindex, + const void *mem, + size_t len, + int *curlcode) +{ + int ret = -1; + + ret = ssl_write(&conn->ssl[sockindex].ssl, + (unsigned char *)mem, len); + + if(ret < 0) { + *curlcode = (ret == POLARSSL_ERR_NET_TRY_AGAIN) ? -1 : CURLE_SEND_ERROR; + ret = -1; + } + + return ret; +} + +void Curl_polarssl_close_all(struct SessionHandle *data) +{ + (void)data; +} + +void Curl_polarssl_close(struct connectdata *conn, int sockindex) +{ + rsa_free(&conn->ssl[sockindex].rsa); + x509_free(&conn->ssl[sockindex].clicert); + x509_free(&conn->ssl[sockindex].cacert); +#if defined(HAVE_POLARSSL_GPL) + x509_crl_free(&conn->ssl[sockindex].crl); +#endif + ssl_free(&conn->ssl[sockindex].ssl); +} + +/* for documentation see Curl_ssl_recv() in sslgen.h */ +ssize_t Curl_polarssl_recv(struct connectdata *conn, + int num, + char *buf, + size_t buffersize, + int *curlcode) +{ + int ret = -1; + ssize_t len = -1; + + memset(buf, 0, buffersize); + ret = ssl_read(&conn->ssl[num].ssl, (unsigned char *)buf, buffersize); + + if(ret <= 0) { + *curlcode = (ret == POLARSSL_ERR_NET_TRY_AGAIN) ? -1 : CURLE_RECV_ERROR; + return -1; + } + + len = ret; + + return len; +} + +void Curl_polarssl_session_free(void *ptr) +{ + free(ptr); +} + +size_t Curl_polarssl_version(char *buffer, size_t size) +{ + return snprintf(buffer, size, "PolarSSL"); +} + +#endif diff --git a/lib/polarssl.h b/lib/polarssl.h new file mode 100644 index 000000000..7d406ef70 --- /dev/null +++ b/lib/polarssl.h @@ -0,0 +1,67 @@ +#ifndef __POLARSSL_H +#define __POLARSSL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2010, Hoi-Ho Chan, + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: polarssl.h,v 1.10 2009-02-12 20:48:43 danf Exp $ + ***************************************************************************/ + +#ifdef USE_POLARSSL + +CURLcode Curl_polarssl_connect(struct connectdata *conn, int sockindex); + +/* tell PolarSSL to close down all open information regarding connections (and + thus session ID caching etc) */ +void Curl_polarssl_close_all(struct SessionHandle *data); + + /* close a SSL connection */ +void Curl_polarssl_close(struct connectdata *conn, int sockindex); + +/* return number of sent (non-SSL) bytes */ +ssize_t Curl_polarssl_send(struct connectdata *conn, int sockindex, + const void *mem, size_t len, int *curlcode); +ssize_t Curl_polarssl_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + int *curlcode); +void Curl_polarssl_session_free(void *ptr); +size_t Curl_polarssl_version(char *buffer, size_t size); +int Curl_polarssl_shutdown(struct connectdata *conn, int sockindex); + +/* API setup for PolarSSL */ +#define curlssl_init() (1) +#define curlssl_cleanup() +#define curlssl_connect Curl_polarssl_connect +#define curlssl_session_free(x) Curl_polarssl_session_free(x) +#define curlssl_close_all Curl_polarssl_close_all +#define curlssl_close Curl_polarssl_close +#define curlssl_shutdown(x,y) 0 +#define curlssl_set_engine(x,y) (x=x, y=y, CURLE_FAILED_INIT) +#define curlssl_set_engine_default(x) (x=x, CURLE_FAILED_INIT) +#define curlssl_engines_list(x) (x=x, (struct curl_slist *)NULL) +#define curlssl_send Curl_polarssl_send +#define curlssl_recv Curl_polarssl_recv +#define curlssl_version Curl_polarssl_version +#define curlssl_check_cxn(x) (x=x, -1) +#define curlssl_data_pending(x,y) (x=x, y=y, 0) + +#endif /* USE_POLARSSL */ +#endif diff --git a/lib/setup.h b/lib/setup.h index ca98ad7e0..5fae0926b 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -525,7 +525,7 @@ int netware_init(void); #define LIBIDN_REQUIRED_VERSION "0.4.1" -#if defined(USE_GNUTLS) || defined(USE_SSLEAY) || defined(USE_NSS) || defined(USE_QSOSSL) +#if defined(USE_GNUTLS) || defined(USE_SSLEAY) || defined(USE_NSS) || defined(USE_QSOSSL) || defined(USE_POLARSSL) #define USE_SSL /* SSL support has been enabled */ #endif diff --git a/lib/sslgen.c b/lib/sslgen.c index df2a4075a..24b6dcbd6 100644 --- a/lib/sslgen.c +++ b/lib/sslgen.c @@ -31,6 +31,7 @@ Curl_ossl_ - prefix for OpenSSL ones Curl_gtls_ - prefix for GnuTLS ones Curl_nss_ - prefix for NSS ones + Curl_polarssl_ - prefix for PolarSSL ones Note that this source code uses curlssl_* functions, and they are all defines/macros #defined by the lib-specific header files. @@ -55,6 +56,7 @@ #include "gtls.h" /* GnuTLS versions */ #include "nssg.h" /* NSS versions */ #include "qssl.h" /* QSOSSL versions */ +#include "polarssl.h" /* PolarSSL versions */ #include "sendf.h" #include "rawstr.h" #include "url.h" diff --git a/lib/urldata.h b/lib/urldata.h index 42065d184..7e3afee7c 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -102,6 +102,11 @@ #include #endif +#ifdef USE_POLARSSL +#include +#include +#endif + #ifdef USE_NSS #include #include @@ -233,6 +238,18 @@ struct ssl_connect_data { gnutls_certificate_credentials cred; ssl_connect_state connecting_state; #endif /* USE_GNUTLS */ +#ifdef USE_POLARSSL + havege_state hs; + ssl_context ssl; + ssl_session ssn; + int server_fd; + x509_cert cacert; + x509_cert clicert; +#if defined(HAVE_POLARSSL_GPL) + x509_crl crl; +#endif + rsa_context rsa; +#endif /* USE_POLARSSL */ #ifdef USE_NSS PRFileDesc *handle; char *client_nickname; -- cgit v1.2.3