diff --git a/lib/Makefile.inc b/lib/Makefile.inc index c54d72f1f..3ab3d1f9d 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -8,7 +8,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ content_encoding.c share.c http_digest.c md5.c http_negotiate.c \ http_ntlm.c inet_pton.c strtoofft.c strerror.c hostares.c hostasyn.c \ hostip4.c hostip6.c hostsyn.c hostthre.c inet_ntop.c parsedate.c \ - select.c + select.c gtls.c sslgen.c HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ @@ -17,4 +17,6 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h base64.h hostip.h \ http_chunks.h strtok.h connect.h llist.h hash.h content_encoding.h \ share.h md5.h http_digest.h http_negotiate.h http_ntlm.h ca-bundle.h \ inet_pton.h strtoofft.h strerror.h inet_ntop.h curlx.h memory.h \ - setup.h transfer.h select.h easyif.h multiif.h parsedate.h + setup.h transfer.h select.h easyif.h multiif.h parsedate.h sslgen.h \ + gtls.h + diff --git a/lib/TODO.gnutls b/lib/TODO.gnutls new file mode 100644 index 000000000..2a64454aa --- /dev/null +++ b/lib/TODO.gnutls @@ -0,0 +1,21 @@ +Things to fix for the GnuTLS support +==================================== + +* set LD_LIBRARY_PATH in configure when the GnuTLS lib was found, to fix link + problems that othwerwise might happen within configure. Compare with OpenSSL + stuff. + +* make the configure --with-ssl option first check for OpenSSL and then for + GnuTLS if OpenSSL wasn't detected. + +* Get NTLM working using the functions provided by libgcrypt, since GnuTLS + already depends on that to function. Not strictly SSL/TLS related, but + hey... Another option is to get available DES and MD4 source code from the + cryptopp library. They are fine license-wise, but are C++. + +* SSL engine stuff? + + SRP for TLS + +* Work out a common method with Peter Sylvester's OpenSSL-patch for SRP + on the TLS to provide name and password diff --git a/lib/easy.c b/lib/easy.c index 500114d60..449e78bf1 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -75,7 +75,7 @@ #include "urldata.h" #include #include "transfer.h" -#include "ssluse.h" +#include "sslgen.h" #include "url.h" #include "getinfo.h" #include "hostip.h" @@ -201,7 +201,7 @@ CURLcode curl_global_init(long flags) Curl_ccalloc = (curl_calloc_callback)calloc; if (flags & CURL_GLOBAL_SSL) - if (!Curl_SSL_init()) + if (!Curl_ssl_init()) return CURLE_FAILED_INIT; if (flags & CURL_GLOBAL_WIN32) @@ -266,7 +266,7 @@ void curl_global_cleanup(void) Curl_global_host_cache_dtor(); if (init_flags & CURL_GLOBAL_SSL) - Curl_SSL_cleanup(); + Curl_ssl_cleanup(); if (init_flags & CURL_GLOBAL_WIN32) win32_cleanup(); diff --git a/lib/ftp.c b/lib/ftp.c index f87aae478..65d4d3693 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -87,7 +87,7 @@ #include "strtoofft.h" #include "strequal.h" -#include "ssluse.h" +#include "sslgen.h" #include "connect.h" #include "strerror.h" #include "memory.h" @@ -2013,7 +2013,7 @@ static CURLcode ftp_state_stor_resp(struct connectdata *conn, do the TLS stuff */ infof(data, "Doing the SSL/TLS handshake on the data stream\n"); /* BLOCKING */ - result = Curl_SSLConnect(conn, SECONDARYSOCKET); + result = Curl_ssl_connect(conn, SECONDARYSOCKET); if(result) return result; } @@ -2121,7 +2121,7 @@ static CURLcode ftp_state_get_resp(struct connectdata *conn, /* since we only have a plaintext TCP connection here, we must now do the TLS stuff */ infof(data, "Doing the SSL/TLS handshake on the data stream\n"); - result = Curl_SSLConnect(conn, SECONDARYSOCKET); + result = Curl_ssl_connect(conn, SECONDARYSOCKET); if(result) return result; } @@ -2373,8 +2373,8 @@ static CURLcode ftp_statemach_act(struct connectdata *conn) */ if((ftpcode == 234) || (ftpcode == 334)) { - /* Curl_SSLConnect is BLOCKING */ - result = Curl_SSLConnect(conn, FIRSTSOCKET); + /* Curl_ssl_connect is BLOCKING */ + result = Curl_ssl_connect(conn, FIRSTSOCKET); if(CURLE_OK == result) { conn->protocol |= PROT_FTPS; conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */ @@ -2748,7 +2748,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn, /* BLOCKING */ /* FTPS is simply ftp with SSL for the control channel */ /* now, perform the SSL initialization for this socket */ - result = Curl_SSLConnect(conn, FIRSTSOCKET); + result = Curl_ssl_connect(conn, FIRSTSOCKET); if(result) return result; } diff --git a/lib/getinfo.c b/lib/getinfo.c index f33b0d61a..76d8cf33d 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -33,7 +33,7 @@ #include #include #include "memory.h" -#include "ssluse.h" +#include "sslgen.h" /* Make this the last #include */ #include "memdebug.h" @@ -182,7 +182,7 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) *param_longp = data->info.numconnects; break; case CURLINFO_SSL_ENGINES: - *param_slistp = Curl_SSL_engines_list(data); + *param_slistp = Curl_ssl_engines_list(data); break; default: return CURLE_BAD_FUNCTION_ARGUMENT; diff --git a/lib/gtls.c b/lib/gtls.c new file mode 100644 index 000000000..f3ab78c3c --- /dev/null +++ b/lib/gtls.c @@ -0,0 +1,464 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * + * 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$ + ***************************************************************************/ + +/* + * Source file for all GnuTLS-specific code for the TLS/SSL layer. No code + * but sslgen.c should ever call or use these functions. + * + * Note: don't use the GnuTLS' *_t variable type names in this source code, + * since they were not present in 1.0.X. + */ + +#include "setup.h" +#ifdef USE_GNUTLS +#include +#include + +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include "urldata.h" +#include "sendf.h" +#include "gtls.h" +#include "sslgen.h" +#include "parsedate.h" +#include "connect.h" /* for the connect timeout */ +#include "select.h" +#define _MPRINTF_REPLACE /* use our functions only */ +#include +#include "memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* Global GnuTLS init, called from Curl_ssl_init() */ +int Curl_gtls_init(void) +{ + gnutls_global_init(); + return 1; +} + +int Curl_gtls_cleanup(void) +{ + gnutls_global_deinit(); + return 1; +} + +static void showtime(struct SessionHandle *data, + const char *text, + time_t stamp) +{ + struct tm *tm; +#ifdef HAVE_GMTIME_R + struct tm buffer; + tm = (struct tm *)gmtime_r(&stamp, &buffer); +#else + tm = gmtime(&stamp); +#endif + snprintf(data->state.buffer, + BUFSIZE, + "\t %s: %s, %02d %s %4d %02d:%02d:%02d GMT\n", + text, + Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); + infof(data, "%s", data->state.buffer); +} + + +/* + * This function is called after the TCP connect has completed. Setup the TLS + * layer and do all necessary magic. + */ +CURLcode +Curl_gtls_connect(struct connectdata *conn, + int sockindex) + +{ + const int cert_type_priority[3] = { GNUTLS_CRT_X509, 0 }; + struct SessionHandle *data = conn->data; + gnutls_session session; + int rc; + unsigned int cert_list_size; + const gnutls_datum *chainp; + unsigned int verify_status; + gnutls_x509_crt x509_cert; + char certbuf[256]; /* big enough? */ + size_t size; + unsigned int algo; + unsigned int bits; + time_t clock; + const char *ptr; + void *ssl_sessionid; + size_t ssl_idsize; + + /* GnuTLS only supports TLSv1 (and SSLv3?) */ + if(data->set.ssl.version == CURL_SSLVERSION_SSLv2) { + failf(data, "GnuTLS does not support SSLv2"); + return CURLE_SSL_CONNECT_ERROR; + } + + /* allocate a cred struct */ + rc = gnutls_certificate_allocate_credentials(&conn->ssl[sockindex].cred); + if(rc < 0) { + failf(data, "gnutls_cert_all_cred() failed: %d", rc); + return CURLE_SSL_CONNECT_ERROR; + } + + /* set the trusted CA cert bundle file */ + rc = gnutls_certificate_set_x509_trust_file(conn->ssl[sockindex].cred, + data->set.ssl.CAfile, + GNUTLS_X509_FMT_PEM); + + /* Initialize TLS session as a client */ + rc = gnutls_init(&conn->ssl[sockindex].session, GNUTLS_CLIENT); + if(rc) { + failf(data, "gnutls_init() failed: %d", rc); + return CURLE_SSL_CONNECT_ERROR; + } + + /* convenient assign */ + session = conn->ssl[sockindex].session; + + /* Use default priorities */ + rc = gnutls_set_default_priority(session); + if(rc < 0) + return CURLE_SSL_CONNECT_ERROR; + + /* Sets the priority on the certificate types supported by gnutls. Priority + is higher for types specified before others. After specifying the types + you want, you must append a 0. */ + rc = gnutls_certificate_type_set_priority(session, cert_type_priority); + if(rc < 0) + return CURLE_SSL_CONNECT_ERROR; + + /* put the anonymous credentials to the current session */ + rc = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, + conn->ssl[sockindex].cred); + + /* set the connection handle (file descriptor for the socket) */ + gnutls_transport_set_ptr(session, + (gnutls_transport_ptr)conn->sock[sockindex]); + + /* This might be a reconnect, so we check for a session ID in the cache + to speed up things */ + + if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, &ssl_idsize)) { + /* we got a session id, use it! */ + gnutls_session_set_data(session, ssl_sessionid, ssl_idsize); + + /* Informational message */ + infof (data, "SSL re-using session ID\n"); + } + + do { + rc = gnutls_handshake(session); + + if((rc == GNUTLS_E_AGAIN) || (rc == GNUTLS_E_INTERRUPTED)) { + long timeout_ms; + long has_passed; + + if(data->set.timeout || data->set.connecttimeout) { + /* get the most strict timeout of the ones converted to milliseconds */ + if(data->set.timeout && + (data->set.timeout>data->set.connecttimeout)) + timeout_ms = data->set.timeout*1000; + else + timeout_ms = data->set.connecttimeout*1000; + } + else + timeout_ms = DEFAULT_CONNECT_TIMEOUT; + + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); + + /* subtract the passed time */ + timeout_ms -= has_passed; + + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } + + rc = Curl_select(conn->sock[sockindex], + conn->sock[sockindex], (int)timeout_ms); + if(rc > 0) + /* reabable or writable, go loop*/ + continue; + else if(0 == rc) { + /* timeout */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEDOUT; + } + else { + /* anything that gets here is fatally bad */ + failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); + return CURLE_SSL_CONNECT_ERROR; + } + } + else + break; + } while(1); + + if (rc < 0) { + failf(data, "gnutls_handshake() failed: %d", rc); + /* gnutls_perror(ret); */ + return CURLE_SSL_CONNECT_ERROR; + } + + /* This function will return the peer's raw certificate (chain) as sent by + the peer. These certificates are in raw format (DER encoded for + X.509). In case of a X.509 then a certificate list may be present. The + first certificate in the list is the peer's certificate, following the + issuer's certificate, then the issuer's issuer etc. */ + + chainp = gnutls_certificate_get_peers(session, &cert_list_size); + if(!chainp) { + if(data->set.ssl.verifyhost) { + failf(data, "failed to get server cert"); + return CURLE_SSL_PEER_CERTIFICATE; + } + infof(data, "\t common name: WARNING couldn't obtain\n"); + } + + /* This function will try to verify the peer's certificate and return its + status (trusted, invalid etc.). The value of status should be one or more + of the gnutls_certificate_status_t enumerated elements bitwise or'd. To + avoid denial of service attacks some default upper limits regarding the + certificate key size and chain size are set. To override them use + gnutls_certificate_set_verify_limits(). */ + + rc = gnutls_certificate_verify_peers2(session, &verify_status); + if (rc < 0) { + failf(data, "server cert verify failed: %d", rc); + return CURLE_SSL_CONNECT_ERROR; + } + + /* verify_status is a bitmask of gnutls_certificate_status bits */ + if(verify_status & GNUTLS_CERT_INVALID) { + if (data->set.ssl.verifypeer) { + failf(data, "server certificate verification failed. CAfile: %s", + data->set.ssl.CAfile?data->set.ssl.CAfile:"none"); + return CURLE_SSL_CACERT; + } + else + infof(data, "\t server certificate verification FAILED\n"); + } + else + infof(data, "\t server certificate verification OK\n"); + + /* initialize an X.509 certificate structure. */ + gnutls_x509_crt_init(&x509_cert); + + /* convert the given DER or PEM encoded Certificate to the native + gnutls_x509_crt_t format */ + gnutls_x509_crt_import(x509_cert, chainp, GNUTLS_X509_FMT_DER); + + size=sizeof(certbuf); + rc = gnutls_x509_crt_get_dn_by_oid(x509_cert, GNUTLS_OID_X520_COMMON_NAME, + 0, /* the first and only one */ + TRUE, /* give to me raw please */ + certbuf, + &size); + + /* This function will check if the given certificate's subject matches the + given hostname. This is a basic implementation of the matching described + in RFC2818 (HTTPS), which takes into account wildcards, and the subject + alternative name PKIX extension. Returns non zero on success, and zero on + failure. */ + rc = gnutls_x509_crt_check_hostname(x509_cert, conn->host.name); + + if(!rc) { + if (data->set.ssl.verifyhost > 1) { + failf(data, "SSL: certificate subject name (%s) does not match " + "target host name '%s'", certbuf, conn->host.dispname); + gnutls_x509_crt_deinit(x509_cert); + return CURLE_SSL_PEER_CERTIFICATE; + } + else + infof(data, "\t common name: %s (does not match '%s')\n", + certbuf, conn->host.dispname); + } + else + infof(data, "\t common name: %s (matched)\n", certbuf); + + /* Show: + + - ciphers used + - subject + - start date + - expire date + - common name + - issuer + + */ + + /* public key algorithm's parameters */ + algo = gnutls_x509_crt_get_pk_algorithm(x509_cert, &bits); + infof(data, "\t certificate public key: %s\n", + gnutls_pk_algorithm_get_name(algo)); + + /* version of the X.509 certificate. */ + infof(data, "\t certificate version: #%d\n", + gnutls_x509_crt_get_version(x509_cert)); + + + size = sizeof(certbuf); + gnutls_x509_crt_get_dn(x509_cert, certbuf, &size); + infof(data, "\t subject: %s\n", certbuf); + + clock = gnutls_x509_crt_get_activation_time(x509_cert); + showtime(data, "start date", clock); + + clock = gnutls_x509_crt_get_expiration_time(x509_cert); + showtime(data, "expire date", clock); + + size = sizeof(certbuf); + gnutls_x509_crt_get_issuer_dn(x509_cert, certbuf, &size); + infof(data, "\t issuer: %s\n", certbuf); + + gnutls_x509_crt_deinit(x509_cert); + + /* compression algorithm (if any) */ + ptr = gnutls_compression_get_name(gnutls_compression_get(session)); + /* the *_get_name() says "NULL" if GNUTLS_COMP_NULL is returned */ + infof(data, "\t compression: %s\n", ptr); + + /* the name of the cipher used. ie 3DES. */ + ptr = gnutls_cipher_get_name(gnutls_cipher_get(session)); + infof(data, "\t cipher: %s\n", ptr); + + /* the MAC algorithms name. ie SHA1 */ + ptr = gnutls_mac_get_name(gnutls_mac_get(session)); + infof(data, "\t MAC: %s\n", ptr); + + if(!ssl_sessionid) { + /* this session was not previously in the cache, add it now */ + + /* get the session ID data size */ + gnutls_session_get_data(session, NULL, &ssl_idsize); + ssl_sessionid = malloc(ssl_idsize); /* get a buffer for it */ + + if(ssl_sessionid) { + /* extract session ID to the allocated buffer */ + gnutls_session_get_data(session, ssl_sessionid, &ssl_idsize); + + /* store this session id */ + return Curl_ssl_addsessionid(conn, ssl_sessionid, ssl_idsize); + } + } + + return CURLE_OK; +} + + +/* return number of sent (non-SSL) bytes */ +int Curl_gtls_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len) +{ + int rc; + rc = gnutls_record_send(conn->ssl[sockindex].session, mem, len); + + return rc; +} + +void Curl_gtls_close_all(struct SessionHandle *data) +{ + /* FIX: make the OpenSSL code more generic and use parts of it here */ + (void)data; +} + +static void close_one(struct connectdata *conn, + int index) +{ + gnutls_bye(conn->ssl[index].session, GNUTLS_SHUT_RDWR); + gnutls_deinit(conn->ssl[index].session); + gnutls_certificate_free_credentials(conn->ssl[index].cred); +} + +void Curl_gtls_close(struct connectdata *conn) +{ + if(conn->ssl[0].use) + close_one(conn, 0); + if(conn->ssl[1].use) + close_one(conn, 1); +} + +/* + * If the read would block we return -1 and set 'wouldblock' to TRUE. + * Otherwise we return the amount of data read. Other errors should return -1 + * and set 'wouldblock' to FALSE. + */ +ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock) +{ + ssize_t ret; + + ret = gnutls_record_recv(conn->ssl[num].session, buf, buffersize); + if((ret == GNUTLS_E_AGAIN) || (ret == GNUTLS_E_INTERRUPTED)) { + *wouldblock = TRUE; + return -1; + } + + *wouldblock = FALSE; + if (!ret) { + failf(conn->data, "Peer closed the TLS connection"); + return -1; + } + + if (ret < 0) { + failf(conn->data, "GnuTLS recv error (%d): %s", + (int)ret, gnutls_strerror(ret)); + return -1; + } + + return ret; +} + +void Curl_gtls_session_free(void *ptr) +{ + free(ptr); +} + +size_t Curl_gtls_version(char *buffer, size_t size) +{ + return snprintf(buffer, size, " GnuTLS/%s", gnutls_check_version(NULL)); +} + +#endif /* USE_GNUTLS */ diff --git a/lib/gtls.h b/lib/gtls.h new file mode 100644 index 000000000..2632b9686 --- /dev/null +++ b/lib/gtls.h @@ -0,0 +1,45 @@ +#ifndef __GTLS_H +#define __GTLS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * + * 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$ + ***************************************************************************/ +int Curl_gtls_init(void); +int Curl_gtls_cleanup(void); +CURLcode Curl_gtls_connect(struct connectdata *conn, int sockindex); + +/* tell GnuTLS to close down all open information regarding connections (and + thus session ID caching etc) */ +void Curl_gtls_close_all(struct SessionHandle *data); +void Curl_gtls_close(struct connectdata *conn); /* close a SSL connection */ + +/* return number of sent (non-SSL) bytes */ +int Curl_gtls_send(struct connectdata *conn, int sockindex, + void *mem, size_t len); +ssize_t Curl_gtls_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock); +void Curl_gtls_session_free(void *ptr); +size_t Curl_gtls_version(char *buffer, size_t size); + +#endif diff --git a/lib/http.c b/lib/http.c index c689b6596..392e70657 100644 --- a/lib/http.c +++ b/lib/http.c @@ -85,7 +85,7 @@ #include "base64.h" #include "cookie.h" #include "strequal.h" -#include "ssluse.h" +#include "sslgen.h" #include "http_digest.h" #include "http_ntlm.h" #include "http_negotiate.h" @@ -416,7 +416,7 @@ Curl_http_output_auth(struct connectdata *conn, /* Send proxy authentication header if needed */ if (conn->bits.httpproxy && (conn->bits.tunnel_proxy == proxytunnel)) { -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) +#ifdef USE_NTLM if(authproxy->picked == CURLAUTH_NTLM) { auth=(char *)"NTLM"; result = Curl_output_ntlm(conn, TRUE); @@ -484,7 +484,7 @@ Curl_http_output_auth(struct connectdata *conn, } else #endif -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) +#ifdef USE_NTLM if(authhost->picked == CURLAUTH_NTLM) { auth=(char *)"NTLM"; result = Curl_output_ntlm(conn, FALSE); @@ -597,7 +597,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, } else #endif -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) +#ifdef USE_NTLM /* NTLM support requires the SSL crypto libs */ if(checkprefix("NTLM", start)) { *availp |= CURLAUTH_NTLM; @@ -1268,8 +1268,8 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) } if(conn->protocol & PROT_HTTPS) { - /* now, perform the SSL initialization for this socket */ - result = Curl_SSLConnect(conn, FIRSTSOCKET); + /* perform SSL initialization for this socket */ + result = Curl_ssl_connect(conn, FIRSTSOCKET); if(result) return result; } diff --git a/lib/http_ntlm.c b/lib/http_ntlm.c index 73f3d3d96..835acdeb8 100644 --- a/lib/http_ntlm.c +++ b/lib/http_ntlm.c @@ -30,8 +30,7 @@ */ #ifndef CURL_DISABLE_HTTP -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) -/* We need OpenSSL for the crypto lib to provide us with MD4 and DES */ +#ifdef USE_NTLM /* -- WIN32 approved -- */ #include @@ -769,5 +768,5 @@ Curl_ntlm_cleanup(struct connectdata *conn) #endif } -#endif /* USE_SSLEAY */ +#endif /* USE_NTLM */ #endif /* !CURL_DISABLE_HTTP */ diff --git a/lib/http_ntlm.h b/lib/http_ntlm.h index 84feee25f..a2e1fc307 100644 --- a/lib/http_ntlm.h +++ b/lib/http_ntlm.h @@ -39,6 +39,9 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, bool proxy, char *header); CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy); void Curl_ntlm_cleanup(struct connectdata *conn); +#if !defined(USE_SSLEAY) && !defined(USE_WINDOWS_SSPI) +#define Curl_ntlm_cleanup(x) +#endif /* Flag bits definitions based on http://davenport.sourceforge.net/ntlm.html */ diff --git a/lib/krb4.h b/lib/krb4.h index 9780a2bca..c45c7c5f4 100644 --- a/lib/krb4.h +++ b/lib/krb4.h @@ -1,10 +1,10 @@ #ifndef __KRB4_H #define __KRB4_H /*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. @@ -12,7 +12,7 @@ * 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. diff --git a/lib/sendf.c b/lib/sendf.c index 735ab4606..4469da798 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -44,13 +44,18 @@ #include "urldata.h" #include "sendf.h" #include "connect.h" /* for the Curl_ourerrno() proto */ +#include "sslgen.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include #ifdef HAVE_KRB4 #include "krb4.h" +#else +#define Curl_sec_write(a,b,c,d) -1 +#define Curl_sec_read(a,b,c,d) -1 #endif + #include #include "memory.h" #include "strerror.h" @@ -233,63 +238,18 @@ CURLcode Curl_write(struct connectdata *conn, { ssize_t bytes_written; CURLcode retcode; - -#ifdef USE_SSLEAY - /* Set 'num' to 0 or 1, depending on which socket that has been sent here. - If it is the second socket, we set num to 1. Otherwise to 0. This lets - us use the correct ssl handle. */ int num = (sockfd == conn->sock[SECONDARYSOCKET]); - /* SSL_write() is said to return 'int' while write() and send() returns - 'size_t' */ - if (conn->ssl[num].use) { - int err; - char error_buffer[120]; /* OpenSSL documents that this must be at least - 120 bytes long. */ - unsigned long sslerror; - int rc = SSL_write(conn->ssl[num].handle, mem, (int)len); - if(rc < 0) { - err = SSL_get_error(conn->ssl[num].handle, rc); - - switch(err) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* The operation did not complete; the same TLS/SSL I/O function - should be called again later. This is basicly an EWOULDBLOCK - equivalent. */ - *written = 0; - return CURLE_OK; - case SSL_ERROR_SYSCALL: - failf(conn->data, "SSL_write() returned SYSCALL, errno = %d\n", - Curl_ourerrno()); - return CURLE_SEND_ERROR; - case SSL_ERROR_SSL: - /* A failure in the SSL library occurred, usually a protocol error. - The OpenSSL error queue contains more information on the error. */ - sslerror = ERR_get_error(); - failf(conn->data, "SSL_write() error: %s\n", - ERR_error_string(sslerror, error_buffer)); - return CURLE_SEND_ERROR; - } - /* a true error */ - failf(conn->data, "SSL_write() return error %d\n", err); - return CURLE_SEND_ERROR; - } - bytes_written = rc; - } + if (conn->ssl[num].use) + /* only TRUE if SSL enabled */ + bytes_written = Curl_ssl_send(conn, num, mem, len); else { -#else - (void)conn; -#endif -#ifdef HAVE_KRB4 - if(conn->sec_complete) { + if(conn->sec_complete) + /* only TRUE if krb4 enabled */ bytes_written = Curl_sec_write(conn, sockfd, mem, len); - } else -#endif /* HAVE_KRB4 */ - { bytes_written = (ssize_t)swrite(sockfd, mem, len); - } + if(-1 == bytes_written) { int err = Curl_ourerrno(); @@ -311,10 +271,7 @@ CURLcode Curl_write(struct connectdata *conn, failf(conn->data, "Send failure: %s", Curl_strerror(conn, err)); } -#ifdef USE_SSLEAY } -#endif - *written = bytes_written; retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR; @@ -376,7 +333,7 @@ int Curl_read(struct connectdata *conn, /* connection data */ ssize_t *n) /* amount bytes read */ { ssize_t nread; -#ifdef USE_SSLEAY + /* Set 'num' to 0 or 1, depending on which socket that has been sent here. If it is the second socket, we set num to 1. Otherwise to 0. This lets us use the correct ssl handle. */ @@ -384,45 +341,17 @@ int Curl_read(struct connectdata *conn, /* connection data */ *n=0; /* reset amount to zero */ - if (conn->ssl[num].use) { - nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, (int)buffersize); + if(conn->ssl[num].use) { + nread = Curl_ssl_recv(conn, num, buf, buffersize); - if(nread < 0) { - /* failed SSL_read */ - int err = SSL_get_error(conn->ssl[num].handle, (int)nread); - - switch(err) { - case SSL_ERROR_NONE: /* this is not an error */ - case SSL_ERROR_ZERO_RETURN: /* no more data */ - break; - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - /* there's data pending, re-invoke SSL_read() */ - return -1; /* basicly EWOULDBLOCK */ - default: - /* openssl/ssl.h says "look at error stack/return value/errno" */ - { - char error_buffer[120]; /* OpenSSL documents that this must be at - least 120 bytes long. */ - unsigned long sslerror = ERR_get_error(); - failf(conn->data, "SSL read: %s, errno %d", - ERR_error_string(sslerror, error_buffer), - Curl_ourerrno() ); - } - return CURLE_RECV_ERROR; - } - } + if(nread == -1) + return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */ } else { -#else - (void)conn; -#endif *n=0; /* reset amount to zero */ -#ifdef HAVE_KRB4 if(conn->sec_complete) nread = Curl_sec_read(conn, sockfd, buf, buffersize); else -#endif nread = sread(sockfd, buf, buffersize); if(-1 == nread) { @@ -434,10 +363,7 @@ int Curl_read(struct connectdata *conn, /* connection data */ #endif return -1; } - -#ifdef USE_SSLEAY } -#endif /* USE_SSLEAY */ *n = nread; return CURLE_OK; } diff --git a/lib/setup.h b/lib/setup.h index 623d9d430..a248e0669 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -280,4 +280,12 @@ typedef int curl_socket_t; #define HAVE_INET_NTOA_R_2_ARGS 1 #endif +#if defined(USE_GNUTLS) || defined(USE_SSLEAY) +#define USE_SSL /* Either OpenSSL || GnuTLS */ +#endif + +#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) +#define USE_NTLM +#endif + #endif /* __CONFIG_H */ diff --git a/lib/sslgen.c b/lib/sslgen.c new file mode 100644 index 000000000..fc1db457e --- /dev/null +++ b/lib/sslgen.c @@ -0,0 +1,535 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * + * 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$ + ***************************************************************************/ + +/* This file is for "generic" SSL functions that all libcurl internals should + use. It is responsible for calling the proper 'ossl' function in ssluse.c + (OpenSSL based) or the 'gtsl' function in gtsl.c (GnuTLS based). + + SSL-functions in libcurl should call functions in this source file, and not + to any specific SSL-layer. + + Curl_ssl_ - prefix for generic ones + Curl_ossl_ - prefix for OpenSSL ones + Curl_gtls_ - prefix for GnuTLS ones + + "SSL/TLS Strong Encryption: An Introduction" + http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html +*/ + +#include "setup.h" +#include +#include +#include +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#include "urldata.h" +#define SSLGEN_C +#include "sslgen.h" /* generic SSL protos etc */ +#include "ssluse.h" /* OpenSSL versions */ +#include "gtls.h" /* GnuTLS versions */ +#include "sendf.h" +#include "strequal.h" +#include "url.h" +#include "memory.h" +/* The last #include file should be: */ +#include "memdebug.h" + +/* "global" init done? */ +static bool init_ssl=FALSE; + +static bool safe_strequal(char* str1, char* str2); + +static bool safe_strequal(char* str1, char* str2) +{ + if(str1 && str2) + /* both pointers point to something then compare them */ + return strequal(str1, str2); + else + /* if both pointers are NULL then treat them as equal */ + return (!str1 && !str2); +} + +bool +Curl_ssl_config_matches(struct ssl_config_data* data, + struct ssl_config_data* needle) +{ + if((data->version == needle->version) && + (data->verifypeer == needle->verifypeer) && + (data->verifyhost == needle->verifyhost) && + safe_strequal(data->CApath, needle->CApath) && + safe_strequal(data->CAfile, needle->CAfile) && + safe_strequal(data->random_file, needle->random_file) && + safe_strequal(data->egdsocket, needle->egdsocket) && + safe_strequal(data->cipher_list, needle->cipher_list)) + return TRUE; + + return FALSE; +} + +bool +Curl_clone_ssl_config(struct ssl_config_data *source, + struct ssl_config_data *dest) +{ + dest->verifyhost = source->verifyhost; + dest->verifypeer = source->verifypeer; + dest->version = source->version; + + if(source->CAfile) { + dest->CAfile = strdup(source->CAfile); + if(!dest->CAfile) + return FALSE; + } + + if(source->CApath) { + dest->CApath = strdup(source->CApath); + if(!dest->CApath) + return FALSE; + } + + if(source->cipher_list) { + dest->cipher_list = strdup(source->cipher_list); + if(!dest->cipher_list) + return FALSE; + } + + if(source->egdsocket) { + dest->egdsocket = strdup(source->egdsocket); + if(!dest->egdsocket) + return FALSE; + } + + if(source->random_file) { + dest->random_file = strdup(source->random_file); + if(!dest->random_file) + return FALSE; + } + + return TRUE; +} + +void Curl_free_ssl_config(struct ssl_config_data* sslc) +{ + if(sslc->CAfile) + free(sslc->CAfile); + + if(sslc->CApath) + free(sslc->CApath); + + if(sslc->cipher_list) + free(sslc->cipher_list); + + if(sslc->egdsocket) + free(sslc->egdsocket); + + if(sslc->random_file) + free(sslc->random_file); +} + +/** + * Global SSL init + * + * @retval 0 error initializing SSL + * @retval 1 SSL initialized successfully + */ +int Curl_ssl_init(void) +{ + /* make sure this is only done once */ + if(init_ssl) + return 1; + init_ssl = TRUE; /* never again */ + +#ifdef USE_SSLEAY + return Curl_ossl_init(); +#else +#ifdef USE_GNUTLS + return Curl_gtls_init(); +#else + /* no SSL support */ +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + + return 1; +} + + +/* Global cleanup */ +void Curl_ssl_cleanup(void) +{ + if(init_ssl) { + /* only cleanup if we did a previous init */ +#ifdef USE_SSLEAY + Curl_ossl_cleanup(); +#else +#ifdef USE_GNUTLS + Curl_gtls_cleanup(); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + init_ssl = FALSE; + } +} + +CURLcode +Curl_ssl_connect(struct connectdata *conn, int sockindex) +{ +#ifdef USE_SSL + /* mark this is being ssl enabled from here on. */ + conn->ssl[sockindex].use = TRUE; + +#ifdef USE_SSLEAY + return Curl_ossl_connect(conn, sockindex); +#else +#ifdef USE_GNUTLS + return Curl_gtls_connect(conn, sockindex); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + +#else + /* without SSL */ + (void)conn; + (void)sockindex; + return CURLE_OK; +#endif /* USE_SSL */ +} + +#ifdef USE_SSL + +/* + * Check if there's a session ID for the given connection in the cache, and if + * there's one suitable, it is provided. Returns TRUE when no entry matched. + */ +int Curl_ssl_getsessionid(struct connectdata *conn, + void **ssl_sessionid, + size_t *idsize) /* set 0 if unknown */ +{ + struct curl_ssl_session *check; + struct SessionHandle *data = conn->data; + long i; + + for(i=0; i< data->set.ssl.numsessions; i++) { + check = &data->state.session[i]; + if(!check->sessionid) + /* not session ID means blank entry */ + continue; + if(curl_strequal(conn->host.name, check->name) && + (conn->remote_port == check->remote_port) && + Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) { + /* yes, we have a session ID! */ + data->state.sessionage++; /* increase general age */ + check->age = data->state.sessionage; /* set this as used in this age */ + *ssl_sessionid = check->sessionid; + if(idsize) + *idsize = check->idsize; + return FALSE; + } + } + *ssl_sessionid = NULL; + return TRUE; +} + +/* + * Kill a single session ID entry in the cache. + */ +static int kill_session(struct curl_ssl_session *session) +{ + if(session->sessionid) { + /* defensive check */ + + /* free the ID the SSL-layer specific way */ +#ifdef USE_SSLEAY + Curl_ossl_session_free(session->sessionid); +#else + Curl_gtls_session_free(session->sessionid); +#endif + session->sessionid=NULL; + session->age = 0; /* fresh */ + + Curl_free_ssl_config(&session->ssl_config); + + Curl_safefree(session->name); + session->name = NULL; /* no name */ + + return 0; /* ok */ + } + else + return 1; +} + +/* + * Store session id in the session cache. The ID passed on to this function + * must already have been extracted and allocated the proper way for the SSL + * layer. Curl_XXXX_session_free() will be called to free/kill the session ID + * later on. + */ +CURLcode Curl_ssl_addsessionid(struct connectdata *conn, + void *ssl_sessionid, + size_t idsize) +{ + int i; + struct SessionHandle *data=conn->data; /* the mother of all structs */ + struct curl_ssl_session *store = &data->state.session[0]; + long oldest_age=data->state.session[0].age; /* zero if unused */ + char *clone_host; + + clone_host = strdup(conn->host.name); + if(!clone_host) + return CURLE_OUT_OF_MEMORY; /* bail out */ + + /* Now we should add the session ID and the host name to the cache, (remove + the oldest if necessary) */ + + /* find an empty slot for us, or find the oldest */ + for(i=1; (iset.ssl.numsessions) && + data->state.session[i].sessionid; i++) { + if(data->state.session[i].age < oldest_age) { + oldest_age = data->state.session[i].age; + store = &data->state.session[i]; + } + } + if(i == data->set.ssl.numsessions) + /* cache is full, we must "kill" the oldest entry! */ + kill_session(store); + else + store = &data->state.session[i]; /* use this slot */ + + /* now init the session struct wisely */ + store->sessionid = ssl_sessionid; + store->idsize = idsize; + store->age = data->state.sessionage; /* set current age */ + store->name = clone_host; /* clone host name */ + store->remote_port = conn->remote_port; /* port number */ + + if (!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) + return CURLE_OUT_OF_MEMORY; + + return CURLE_OK; +} + + +#endif + +void Curl_ssl_close_all(struct SessionHandle *data) +{ +#ifdef USE_SSL + int i; + /* kill the session ID cache */ + if(data->state.session) { + for(i=0; i< data->set.ssl.numsessions; i++) + /* the single-killer function handles empty table slots */ + kill_session(&data->state.session[i]); + + /* free the cache data */ + free(data->state.session); + data->state.session = NULL; + } +#ifdef USE_SSLEAY + Curl_ossl_close_all(data); +#else +#ifdef USE_GNUTLS + Curl_gtls_close_all(data); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +#else /* USE_SSL */ + (void)data; +#endif /* USE_SSL */ +} + +void Curl_ssl_close(struct connectdata *conn) +{ + if(conn->ssl[FIRSTSOCKET].use) { +#ifdef USE_SSLEAY + Curl_ossl_close(conn); +#else +#ifdef USE_GNUTLS + Curl_gtls_close(conn); +#else + (void)conn; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + } +} + +/* Selects an (Open)SSL crypto engine + */ +CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine) +{ +#ifdef USE_SSLEAY + return Curl_ossl_set_engine(data, engine); +#else +#ifdef USE_GNUTLS + /* FIX: add code here */ + (void)data; + (void)engine; +#else + (void)data; + (void)engine; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + return CURLE_FAILED_INIT; +} + +/* Selects an (Open?)SSL crypto engine + */ +CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data) +{ +#ifdef USE_SSLEAY + return Curl_ossl_set_engine_default(data); +#else +#ifdef USE_GNUTLS + /* FIX: add code here */ + (void)data; +#else + (void)data; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + return CURLE_FAILED_INIT; +} + +/* Return list of OpenSSL crypto engine names. */ +struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data) +{ +#ifdef USE_SSLEAY + return Curl_ossl_engines_list(data); +#else +#ifdef USE_GNUTLS + /* FIX: add code here? */ + (void)data; + return NULL; +#else + (void)data; + return NULL; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + +/* return number of sent (non-SSL) bytes */ +int Curl_ssl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len) +{ +#ifdef USE_SSLEAY + return Curl_ossl_send(conn, sockindex, mem, len); +#else +#ifdef USE_GNUTLS + return Curl_gtls_send(conn, sockindex, mem, len); +#else + (void)conn; + (void)sockindex; + (void)mem; + (void)len; + return 0; +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + +/* return number of received (decrypted) bytes */ + +/* + * If the read would block (EWOULDBLOCK) we return -1. Otherwise we return + * a regular CURLcode value. + */ +int Curl_ssl_recv(struct connectdata *conn, /* connection data */ + int sockindex, /* socketindex */ + char *mem, /* store read data here */ + size_t len) /* max amount to read */ +{ +#ifdef USE_SSL + ssize_t nread; + bool block = FALSE; + +#ifdef USE_SSLEAY + nread = Curl_ossl_recv(conn, sockindex, mem, len, &block); +#else +#ifdef USE_GNUTLS + nread = Curl_gtls_recv(conn, sockindex, mem, len, &block); +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ + if(nread == -1) { + infof(conn->data, "Curl_xxx_rcvs returned -1, block = %s\n", + block?"TRUE":"FALSE"); + if(!block) + return 0; /* this is a true error, not EWOULDBLOCK */ + else + return -1; + } + + return nread; + +#else /* USE_SSL */ + (void)conn; + (void)sockindex; + (void)mem; + (void)len; + return 0; +#endif /* USE_SSL */ +} + + +/* + * This sets up a session ID cache to the specified size. Make sure this code + * is agnostic to what underlying SSL technology we use. + */ +CURLcode Curl_ssl_initsessions(struct SessionHandle *data, long amount) +{ + struct curl_ssl_session *session; + + if(data->state.session) + /* this is just a precaution to prevent multiple inits */ + return CURLE_OK; + + session = (struct curl_ssl_session *) + malloc(amount * sizeof(struct curl_ssl_session)); + if(!session) + return CURLE_OUT_OF_MEMORY; + + /* "blank out" the newly allocated memory */ + memset(session, 0, amount * sizeof(struct curl_ssl_session)); + + /* store the info in the SSL section */ + data->set.ssl.numsessions = amount; + data->state.session = session; + data->state.sessionage = 1; /* this is brand new */ + + return CURLE_OK; +} + +size_t Curl_ssl_version(char *buffer, size_t size) +{ +#ifdef USE_SSLEAY + return Curl_ossl_version(buffer, size); +#else +#ifdef USE_GNUTLS + return Curl_gtls_version(buffer, size); +#else + (void)buffer; + (void)size; + return 0; /* no SSL support */ +#endif /* USE_GNUTLS */ +#endif /* USE_SSLEAY */ +} + diff --git a/lib/sslgen.h b/lib/sslgen.h new file mode 100644 index 000000000..3e1dfa0a6 --- /dev/null +++ b/lib/sslgen.h @@ -0,0 +1,72 @@ +#ifndef __SSLGEN_H +#define __SSLGEN_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2005, Daniel Stenberg, , et al. + * + * 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$ + ***************************************************************************/ + +bool Curl_ssl_config_matches(struct ssl_config_data* data, + struct ssl_config_data* needle); +bool Curl_clone_ssl_config(struct ssl_config_data* source, + struct ssl_config_data* dest); +void Curl_free_ssl_config(struct ssl_config_data* sslc); + +int Curl_ssl_init(void); +void Curl_ssl_cleanup(void); +CURLcode Curl_ssl_connect(struct connectdata *conn, int sockindex); +void Curl_ssl_close(struct connectdata *conn); +/* tell the SSL stuff to close down all open information regarding + connections (and thus session ID caching etc) */ +void Curl_ssl_close_all(struct SessionHandle *data); +CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine); +/* Sets engine as default for all SSL operations */ +CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data); +int Curl_ssl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len); +int Curl_ssl_recv(struct connectdata *conn, /* connection data */ + int sockindex, /* socketindex */ + char *mem, /* store read data here */ + size_t len); /* max amount to read */ + +/* init the SSL session ID cache */ +CURLcode Curl_ssl_initsessions(struct SessionHandle *, long); +/* extract a session ID */ +int Curl_ssl_getsessionid(struct connectdata *conn, + void **ssl_sessionid, + size_t *idsize) /* set 0 if unknown */; +/* add a new session ID */ +CURLcode Curl_ssl_addsessionid(struct connectdata *conn, + void *ssl_sessionid, + size_t idsize); + + +struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data); + +size_t Curl_ssl_version(char *buffer, size_t size); + +#if !defined(USE_SSL) && !defined(SSLGEN_C) +/* set up blank macros for none-SSL builds */ +#define Curl_ssl_close_all(x) +#endif + +#endif diff --git a/lib/ssluse.c b/lib/ssluse.c index 531b55fa0..d54a92b5d 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -22,8 +22,13 @@ ***************************************************************************/ /* - * The original SSL code for curl was written by - * Linas Vepstas and Sampo Kellomaki + * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code + * but sslgen.c should ever call or use these functions. + */ + +/* + * The original SSLeay-using code for curl was written by Linas Vepstas and + * Sampo Kellomaki 1998. */ #include "setup.h" @@ -47,6 +52,7 @@ #include "connect.h" /* Curl_ourerrno() proto */ #include "strequal.h" #include "select.h" +#include "sslgen.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include @@ -447,8 +453,8 @@ int cert_stuff(struct connectdata *conn, #endif case SSL_FILETYPE_PKCS12: if(!cert_done) { - failf(data, "file type P12 for private key not supported\n"); - return 0; + failf(data, "file type P12 for private key not supported\n"); + return 0; } break; default: @@ -519,27 +525,20 @@ static char *SSL_strerror(unsigned long error, char *buf, size_t size) return (buf); } -/* "global" init done? */ -static int init_ssl=0; - /* we have the "SSL is seeded" boolean global for the application to prevent multiple time-consuming seedings in vain */ static bool ssl_seeded = FALSE; #endif /* USE_SSLEAY */ +#ifdef USE_SSLEAY /** * Global SSL init * * @retval 0 error initializing SSL * @retval 1 SSL initialized successfully */ -int Curl_SSL_init(void) +int Curl_ossl_init(void) { -#ifdef USE_SSLEAY - /* make sure this is only done once */ - if(init_ssl) - return 1; - #ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES ENGINE_load_builtin_engines(); #endif @@ -551,57 +550,39 @@ int Curl_SSL_init(void) if (!SSLeay_add_ssl_algorithms()) return 0; - init_ssl++; /* never again */ - -#else - /* SSL disabled, do nothing */ -#endif - return 1; } -/* Global cleanup */ -void Curl_SSL_cleanup(void) -{ +#endif /* USE_SSLEAY */ + #ifdef USE_SSLEAY - if(init_ssl) { - /* only cleanup if we did a previous init */ - /* Free the SSL error strings */ - ERR_free_strings(); +/* Global cleanup */ +void Curl_ossl_cleanup(void) +{ + /* Free the SSL error strings */ + ERR_free_strings(); - /* EVP_cleanup() removes all ciphers and digests from the - table. */ - EVP_cleanup(); + /* EVP_cleanup() removes all ciphers and digests from the + table. */ + EVP_cleanup(); #ifdef HAVE_ENGINE_cleanup - ENGINE_cleanup(); + ENGINE_cleanup(); #endif #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA - /* this function was not present in 0.9.6b, but was added sometimes - later */ - CRYPTO_cleanup_all_ex_data(); -#endif - - init_ssl=0; /* not inited any more */ - } -#else - /* SSL disabled, do nothing */ + /* this function was not present in 0.9.6b, but was added sometimes + later */ + CRYPTO_cleanup_all_ex_data(); #endif } -#ifndef USE_SSLEAY -void Curl_SSL_Close(struct connectdata *conn) -{ - (void)conn; -} -#endif - +#endif /* USE_SSLEAY */ /* Selects an OpenSSL crypto engine */ -CURLcode Curl_SSL_set_engine(struct SessionHandle *data, const char *engine) +CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine) { #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) ENGINE *e = ENGINE_by_id(engine); @@ -633,11 +614,12 @@ CURLcode Curl_SSL_set_engine(struct SessionHandle *data, const char *engine) #endif } -/* Sets above engine as default for all SSL operations +#ifdef USE_SSLEAY +/* Sets engine as default for all SSL operations */ -CURLcode Curl_SSL_set_engine_default(struct SessionHandle *data) +CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data) { -#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) +#ifdef HAVE_OPENSSL_ENGINE_H if (data->state.engine) { if (ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) { infof(data,"set default crypto engine %s\n", data->state.engine); @@ -650,12 +632,13 @@ CURLcode Curl_SSL_set_engine_default(struct SessionHandle *data) #else (void) data; #endif - return (CURLE_OK); + return CURLE_OK; } +#endif /* USE_SSLEAY */ /* Return list of OpenSSL crypto engine names. */ -struct curl_slist *Curl_SSL_engines_list(struct SessionHandle *data) +struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data) { struct curl_slist *list = NULL; #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) @@ -674,139 +657,50 @@ struct curl_slist *Curl_SSL_engines_list(struct SessionHandle *data) /* * This function is called when an SSL connection is closed. */ -void Curl_SSL_Close(struct connectdata *conn) +void Curl_ossl_close(struct connectdata *conn) { - if(conn->ssl[FIRSTSOCKET].use) { - int i; - /* - ERR_remove_state() frees the error queue associated with - thread pid. If pid == 0, the current thread will have its - error queue removed. + int i; + /* + ERR_remove_state() frees the error queue associated with + thread pid. If pid == 0, the current thread will have its + error queue removed. - Since error queue data structures are allocated - automatically for new threads, they must be freed when - threads are terminated in oder to avoid memory leaks. - */ - ERR_remove_state(0); + Since error queue data structures are allocated + automatically for new threads, they must be freed when + threads are terminated in oder to avoid memory leaks. + */ + ERR_remove_state(0); - for(i=0; i<2; i++) { - struct ssl_connect_data *connssl = &conn->ssl[i]; + for(i=0; i<2; i++) { + struct ssl_connect_data *connssl = &conn->ssl[i]; - if(connssl->handle) { - (void)SSL_shutdown(connssl->handle); - SSL_set_connect_state(connssl->handle); + if(connssl->handle) { + (void)SSL_shutdown(connssl->handle); + SSL_set_connect_state(connssl->handle); - SSL_free (connssl->handle); - connssl->handle = NULL; - } - if(connssl->ctx) { - SSL_CTX_free (connssl->ctx); - connssl->ctx = NULL; - } - connssl->use = FALSE; /* get back to ordinary socket usage */ + SSL_free (connssl->handle); + connssl->handle = NULL; } - } -} - - -/* - * This sets up a session cache to the specified size. - */ -CURLcode Curl_SSL_InitSessions(struct SessionHandle *data, long amount) -{ - struct curl_ssl_session *session; - - if(data->state.session) - /* this is just a precaution to prevent multiple inits */ - return CURLE_OK; - - session = (struct curl_ssl_session *) - malloc(amount * sizeof(struct curl_ssl_session)); - if(!session) - return CURLE_OUT_OF_MEMORY; - - /* "blank out" the newly allocated memory */ - memset(session, 0, amount * sizeof(struct curl_ssl_session)); - - /* store the info in the SSL section */ - data->set.ssl.numsessions = amount; - data->state.session = session; - data->state.sessionage = 1; /* this is brand new */ - - return CURLE_OK; -} - -/* - * Check if there's a session ID for the given connection in the cache, - * and if there's one suitable, it is returned. - */ -static int Get_SSL_Session(struct connectdata *conn, - SSL_SESSION **ssl_sessionid) -{ - struct curl_ssl_session *check; - struct SessionHandle *data = conn->data; - long i; - - for(i=0; i< data->set.ssl.numsessions; i++) { - check = &data->state.session[i]; - if(!check->sessionid) - /* not session ID means blank entry */ - continue; - if(curl_strequal(conn->host.name, check->name) && - (conn->remote_port == check->remote_port) && - Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) { - /* yes, we have a session ID! */ - data->state.sessionage++; /* increase general age */ - check->age = data->state.sessionage; /* set this as used in this age */ - *ssl_sessionid = check->sessionid; - return FALSE; + if(connssl->ctx) { + SSL_CTX_free (connssl->ctx); + connssl->ctx = NULL; } + connssl->use = FALSE; /* get back to ordinary socket usage */ } - *ssl_sessionid = (SSL_SESSION *)NULL; - return TRUE; } -/* - * Kill a single session ID entry in the cache. - */ -static int Kill_Single_Session(struct curl_ssl_session *session) +void Curl_ossl_session_free(void *ptr) { - if(session->sessionid) { - /* defensive check */ - - /* free the ID */ - SSL_SESSION_free(session->sessionid); - session->sessionid=NULL; - session->age = 0; /* fresh */ - - Curl_free_ssl_config(&session->ssl_config); - - Curl_safefree(session->name); - session->name = NULL; /* no name */ - - return 0; /* ok */ - } - else - return 1; + /* free the ID */ + SSL_SESSION_free(ptr); } /* * This function is called when the 'data' struct is going away. Close * down everything and free all resources! */ -int Curl_SSL_Close_All(struct SessionHandle *data) +int Curl_ossl_close_all(struct SessionHandle *data) { - int i; - - if(data->state.session) { - for(i=0; i< data->set.ssl.numsessions; i++) - /* the single-killer function handles empty table slots */ - Kill_Single_Session(&data->state.session[i]); - - /* free the cache data */ - free(data->state.session); - data->state.session = NULL; - } #ifdef HAVE_OPENSSL_ENGINE_H if(data->state.engine) { ENGINE_finish(data->state.engine); @@ -817,74 +711,6 @@ int Curl_SSL_Close_All(struct SessionHandle *data) return 0; } -/* - * Extract the session id and store it in the session cache. - */ -static CURLcode Store_SSL_Session(struct connectdata *conn, - struct ssl_connect_data *ssl) -{ - SSL_SESSION *ssl_sessionid; - int i; - struct SessionHandle *data=conn->data; /* the mother of all structs */ - struct curl_ssl_session *store = &data->state.session[0]; - long oldest_age=data->state.session[0].age; /* zero if unused */ - char *clone_host; - - clone_host = strdup(conn->host.name); - if(!clone_host) - return CURLE_OUT_OF_MEMORY; /* bail out */ - - /* ask OpenSSL, say please */ - -#ifdef HAVE_SSL_GET1_SESSION - ssl_sessionid = SSL_get1_session(ssl->handle); - - /* SSL_get1_session() will increment the reference - count and the session will stay in memory until explicitly freed with - SSL_SESSION_free(3), regardless of its state. - This function was introduced in openssl 0.9.5a. */ -#else - ssl_sessionid = SSL_get_session(ssl->handle); - - /* if SSL_get1_session() is unavailable, use SSL_get_session(). - This is an inferior option because the session can be flushed - at any time by openssl. It is included only so curl compiles - under versions of openssl < 0.9.5a. - - WARNING: How curl behaves if it's session is flushed is - untested. - */ -#endif - - /* Now we should add the session ID and the host name to the cache, (remove - the oldest if necessary) */ - - /* find an empty slot for us, or find the oldest */ - for(i=1; (iset.ssl.numsessions) && - data->state.session[i].sessionid; i++) { - if(data->state.session[i].age < oldest_age) { - oldest_age = data->state.session[i].age; - store = &data->state.session[i]; - } - } - if(i == data->set.ssl.numsessions) - /* cache is full, we must "kill" the oldest entry! */ - Kill_Single_Session(store); - else - store = &data->state.session[i]; /* use this slot */ - - /* now init the session struct wisely */ - store->sessionid = ssl_sessionid; - store->age = data->state.sessionage; /* set current age */ - store->name = clone_host; /* clone host name */ - store->remote_port = conn->remote_port; /* port number */ - - if (!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) - return CURLE_OUT_OF_MEMORY; - - return CURLE_OK; -} - static int Curl_ASN1_UTCTIME_output(struct connectdata *conn, const char *prefix, ASN1_UTCTIME *tm) @@ -1280,28 +1106,25 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type, } #endif +#ifdef USE_SSLEAY /* ====================================================== */ CURLcode -Curl_SSLConnect(struct connectdata *conn, - int sockindex) +Curl_ossl_connect(struct connectdata *conn, + int sockindex) { CURLcode retcode = CURLE_OK; -#ifdef USE_SSLEAY struct SessionHandle *data = conn->data; int err; long lerr; int what; char * str; SSL_METHOD *req_method; - SSL_SESSION *ssl_sessionid=NULL; + void *ssl_sessionid=NULL; ASN1_TIME *certdate; curl_socket_t sockfd = conn->sock[sockindex]; struct ssl_connect_data *connssl = &conn->ssl[sockindex]; - /* mark this is being ssl enabled from here on out. */ - connssl->use = TRUE; - if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) { /* Make funny stuff to get random input */ random_the_seed(data); @@ -1447,19 +1270,16 @@ Curl_SSLConnect(struct connectdata *conn, connssl->server_cert = 0x0; - if(!conn->bits.reuse) { - /* We're not re-using a connection, check if there's a cached ID we - can/should use here! */ - if(!Get_SSL_Session(conn, &ssl_sessionid)) { - /* we got a session id, use it! */ - if (!SSL_set_session(connssl->handle, ssl_sessionid)) { - failf(data, "SSL: SSL_set_session failed: %s", - ERR_error_string(ERR_get_error(),NULL)); - return CURLE_SSL_CONNECT_ERROR; - } - /* Informational message */ - infof (data, "SSL re-using session ID\n"); + /* Check if there's a cached ID we can/should use here! */ + if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) { + /* we got a session id, use it! */ + if (!SSL_set_session(connssl->handle, ssl_sessionid)) { + failf(data, "SSL: SSL_set_session failed: %s", + ERR_error_string(ERR_get_error(),NULL)); + return CURLE_SSL_CONNECT_ERROR; } + /* Informational message */ + infof (data, "SSL re-using session ID\n"); } /* pass the raw socket into the SSL layers */ @@ -1473,16 +1293,13 @@ Curl_SSLConnect(struct connectdata *conn, int writefd; int readfd; long timeout_ms; + long has_passed; /* Find out if any timeout is set. If not, use 300 seconds. Otherwise, figure out the most strict timeout of the two possible one and then how much time that has elapsed to know how much time we allow for the connect call */ if(data->set.timeout || data->set.connecttimeout) { - long has_passed; - - /* Evaluate in milliseconds how much time that has passed */ - has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); /* get the most strict timeout of the ones converted to milliseconds */ if(data->set.timeout && @@ -1490,20 +1307,22 @@ Curl_SSLConnect(struct connectdata *conn, timeout_ms = data->set.timeout*1000; else timeout_ms = data->set.connecttimeout*1000; - - /* subtract the passed time */ - timeout_ms -= has_passed; - - if(timeout_ms < 0) { - /* a precaution, no need to continue if time already is up */ - failf(data, "SSL connection timeout"); - return CURLE_OPERATION_TIMEOUTED; - } } else /* no particular time-out has been set */ timeout_ms= DEFAULT_CONNECT_TIMEOUT; + /* Evaluate in milliseconds how much time that has passed */ + has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle); + + /* subtract the passed time */ + timeout_ms -= has_passed; + + if(timeout_ms < 0) { + /* a precaution, no need to continue if time already is up */ + failf(data, "SSL connection timeout"); + return CURLE_OPERATION_TIMEOUTED; + } readfd = CURL_SOCKET_BAD; writefd = CURL_SOCKET_BAD; @@ -1586,12 +1405,7 @@ Curl_SSLConnect(struct connectdata *conn, return CURLE_OPERATION_TIMEDOUT; } else { -#if !defined(WIN32) && defined(EINTR) - /* For platforms without EINTR all errnos are bad */ - if (errno == EINTR) - continue; /* retry the select() */ -#endif - /* anything other than the unimportant EINTR is fatally bad */ + /* anything that gets here is fatally bad */ failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); return CURLE_SSL_CONNECT_ERROR; } @@ -1605,9 +1419,30 @@ Curl_SSLConnect(struct connectdata *conn, if(!ssl_sessionid) { /* Since this is not a cached session ID, then we want to stach this one in the cache! */ - retcode = Store_SSL_Session(conn, connssl); + SSL_SESSION *ssl_sessionid; +#ifdef HAVE_SSL_GET1_SESSION + ssl_sessionid = SSL_get1_session(connssl->handle); + + /* SSL_get1_session() will increment the reference + count and the session will stay in memory until explicitly freed with + SSL_SESSION_free(3), regardless of its state. + This function was introduced in openssl 0.9.5a. */ +#else + ssl_sessionid = SSL_get_session(connssl->handle); + + /* if SSL_get1_session() is unavailable, use SSL_get_session(). + This is an inferior option because the session can be flushed + at any time by openssl. It is included only so curl compiles + under versions of openssl < 0.9.5a. + + WARNING: How curl behaves if it's session is flushed is + untested. + */ +#endif + retcode = Curl_ssl_addsessionid(conn, ssl_sessionid, + 0 /* unknown size */); if(retcode) { - failf(data,"failure to store ssl session"); + failf(data, "failed to store ssl session"); return retcode; } } @@ -1686,9 +1521,145 @@ Curl_SSLConnect(struct connectdata *conn, X509_free(connssl->server_cert); connssl->server_cert = NULL; -#else /* USE_SSLEAY */ - (void)conn; - (void)sockindex; -#endif return retcode; } + +/* return number of sent (non-SSL) bytes */ +int Curl_ossl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len) +{ + /* SSL_write() is said to return 'int' while write() and send() returns + 'size_t' */ + int err; + char error_buffer[120]; /* OpenSSL documents that this must be at least 120 + bytes long. */ + unsigned long sslerror; + int rc = SSL_write(conn->ssl[sockindex].handle, mem, (int)len); + + if(rc < 0) { + err = SSL_get_error(conn->ssl[sockindex].handle, rc); + + switch(err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* The operation did not complete; the same TLS/SSL I/O function + should be called again later. This is basicly an EWOULDBLOCK + equivalent. */ + return 0; + case SSL_ERROR_SYSCALL: + failf(conn->data, "SSL_write() returned SYSCALL, errno = %d\n", + Curl_ourerrno()); + return -1; + case SSL_ERROR_SSL: + /* A failure in the SSL library occurred, usually a protocol error. + The OpenSSL error queue contains more information on the error. */ + sslerror = ERR_get_error(); + failf(conn->data, "SSL_write() error: %s\n", + ERR_error_string(sslerror, error_buffer)); + return -1; + } + /* a true error */ + failf(conn->data, "SSL_write() return error %d\n", err); + return -1; + } + return rc; /* number of bytes */ +} + +/* + * If the read would block we return -1 and set 'wouldblock' to TRUE. + * Otherwise we return the amount of data read. Other errors should return -1 + * and set 'wouldblock' to FALSE. + */ +ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock) +{ + char error_buffer[120]; /* OpenSSL documents that this must be at + least 120 bytes long. */ + unsigned long sslerror; + ssize_t nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, + (int)buffersize); + *wouldblock = FALSE; + if(nread < 0) { + /* failed SSL_read */ + int err = SSL_get_error(conn->ssl[num].handle, (int)nread); + + switch(err) { + case SSL_ERROR_NONE: /* this is not an error */ + case SSL_ERROR_ZERO_RETURN: /* no more data */ + break; + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + /* there's data pending, re-invoke SSL_read() */ + *wouldblock = TRUE; + return -1; /* basically EWOULDBLOCK */ + default: + /* openssl/ssl.h says "look at error stack/return value/errno" */ + sslerror = ERR_get_error(); + failf(conn->data, "SSL read: %s, errno %d", + ERR_error_string(sslerror, error_buffer), + Curl_ourerrno() ); + return -1; + } + } + return nread; +} + +size_t Curl_ossl_version(char *buffer, size_t size) +{ +#if (SSLEAY_VERSION_NUMBER >= 0x905000) + { + char sub[2]; + unsigned long ssleay_value; + sub[1]='\0'; + ssleay_value=SSLeay(); + if(ssleay_value < 0x906000) { + ssleay_value=SSLEAY_VERSION_NUMBER; + sub[0]='\0'; + } + else { + if(ssleay_value&0xff0) { + sub[0]=(char)((ssleay_value>>4)&0xff) + 'a' -1; + } + else + sub[0]='\0'; + } + + return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx%s", + (ssleay_value>>28)&0xf, + (ssleay_value>>20)&0xff, + (ssleay_value>>12)&0xff, + sub); + } + +#else /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */ + +#if (SSLEAY_VERSION_NUMBER >= 0x900000) + return snprintf(buffer, size, " OpenSSL/%lx.%lx.%lx", + (SSLEAY_VERSION_NUMBER>>28)&0xff, + (SSLEAY_VERSION_NUMBER>>20)&0xff, + (SSLEAY_VERSION_NUMBER>>12)&0xf); + +#else /* (SSLEAY_VERSION_NUMBER >= 0x900000) */ + { + char sub[2]; + sub[1]='\0'; + if(SSLEAY_VERSION_NUMBER&0x0f) { + sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1; + } + else + sub[0]='\0'; + + return snprintf(buffer, size, " SSL/%x.%x.%x%s", + (SSLEAY_VERSION_NUMBER>>12)&0xff, + (SSLEAY_VERSION_NUMBER>>8)&0xf, + (SSLEAY_VERSION_NUMBER>>4)&0xf, sub); + } +#endif /* (SSLEAY_VERSION_NUMBER >= 0x900000) */ +#endif /* SSLEAY_VERSION_NUMBER is less than 0.9.5 */ +} +#endif /* USE_SSLEAY */ diff --git a/lib/ssluse.h b/lib/ssluse.h index 25278eba6..00345317d 100644 --- a/lib/ssluse.h +++ b/lib/ssluse.h @@ -22,27 +22,43 @@ * * $Id$ ***************************************************************************/ + +/* + * This header should only be needed to get included by sslgen.c and ssluse.c + */ + #include "urldata.h" -CURLcode Curl_SSLConnect(struct connectdata *conn, int sockindex); - -int Curl_SSL_init(void); /* Global SSL init */ -void Curl_SSL_cleanup(void); /* Global SSL cleanup */ - -/* init the SSL session ID cache */ -CURLcode Curl_SSL_InitSessions(struct SessionHandle *, long); -void Curl_SSL_Close(struct connectdata *conn); /* close a SSL connection */ - -/* tell the SSL stuff to close down all open information regarding - connections (and thus session ID caching etc) */ -int Curl_SSL_Close_All(struct SessionHandle *data); - +CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex); +void Curl_ossl_close(struct connectdata *conn); /* close a SSL connection */ +/* tell OpenSSL to close down all open information regarding connections (and + thus session ID caching etc) */ +int Curl_ossl_close_all(struct SessionHandle *data); /* Sets an OpenSSL engine */ -CURLcode Curl_SSL_set_engine(struct SessionHandle *data, const char *engine); +CURLcode Curl_ossl_set_engine(struct SessionHandle *data, const char *engine); -/* Sets above engine as default for all SSL operations */ -CURLcode Curl_SSL_set_engine_default(struct SessionHandle *data); +/* function provided for the generic SSL-layer, called when a session id + should be freed */ +void Curl_ossl_session_free(void *ptr); + +/* Sets engine as default for all SSL operations */ +CURLcode Curl_ossl_set_engine_default(struct SessionHandle *data); /* Build list of OpenSSL engines */ -struct curl_slist *Curl_SSL_engines_list(struct SessionHandle *data); +struct curl_slist *Curl_ossl_engines_list(struct SessionHandle *data); + +int Curl_ossl_init(void); +void Curl_ossl_cleanup(void); + +int Curl_ossl_send(struct connectdata *conn, + int sockindex, + void *mem, + size_t len); +ssize_t Curl_ossl_recv(struct connectdata *conn, /* connection data */ + int num, /* socketindex */ + char *buf, /* store read data here */ + size_t buffersize, /* max amount to read */ + bool *wouldblock); + +size_t Curl_ossl_version(char *buffer, size_t size); #endif diff --git a/lib/transfer.c b/lib/transfer.c index 7142d517b..a8858f173 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -94,7 +94,7 @@ #include "http.h" #include "url.h" #include "getinfo.h" -#include "ssluse.h" +#include "sslgen.h" #include "http_digest.h" #include "http_ntlm.h" #include "http_negotiate.h" @@ -243,6 +243,7 @@ CURLcode Curl_readrewind(struct connectdata *conn) } #ifdef USE_SSLEAY +/* FIX: this is nasty OpenSSL-specific code that really shouldn't be here */ static int data_pending(struct connectdata *conn) { if(conn->ssl[FIRSTSOCKET].handle) @@ -1597,20 +1598,17 @@ Transfer(struct connectdata *conn) */ CURLcode Curl_pretransfer(struct SessionHandle *data) { + CURLcode res; if(!data->change.url) /* we can't do anything wihout URL */ return CURLE_URL_MALFORMAT; -#ifdef USE_SSLEAY - { - /* Init the SSL session ID cache here. We do it here since we want to do - it after the *_setopt() calls (that could change the size of the cache) - but before any transfer takes place. */ - CURLcode res = Curl_SSL_InitSessions(data, data->set.ssl.numsessions); - if(res) - return res; - } -#endif + /* Init the SSL session ID cache here. We do it here since we want to do it + after the *_setopt() calls (that could change the size of the cache) but + before any transfer takes place. */ + res = Curl_ssl_initsessions(data, data->set.ssl.numsessions); + if(res) + return res; data->set.followlocation=0; /* reset the location-follow counter */ data->state.this_is_a_follow = FALSE; /* reset this */ diff --git a/lib/url.c b/lib/url.c index 9d62f4290..7174e8cf0 100644 --- a/lib/url.c +++ b/lib/url.c @@ -102,7 +102,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by #include "formdata.h" #include "base64.h" -#include "ssluse.h" +#include "sslgen.h" #include "hostip.h" #include "transfer.h" #include "sendf.h" @@ -154,7 +154,6 @@ static bool ConnectionExists(struct SessionHandle *data, struct connectdata **usethis); static long ConnectionStore(struct SessionHandle *data, struct connectdata *conn); -static bool safe_strequal(char* str1, char* str2); #ifndef USE_ARES /* not for Win32, unless it is cygwin @@ -211,11 +210,8 @@ CURLcode Curl_close(struct SessionHandle *data) } } -#ifdef USE_SSLEAY /* Close down all open SSL info and sessions */ - Curl_SSL_Close_All(data); -#endif - + Curl_ssl_close_all(data); Curl_safefree(data->state.first_host); Curl_safefree(data->state.scratch); @@ -832,7 +828,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) { long auth = va_arg(param, long); /* switch off bits we can't support */ -#if ! defined(USE_SSLEAY) && !defined(USE_WINDOWS_SSPI) +#ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ #endif #ifndef HAVE_GSSAPI @@ -852,7 +848,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) { long auth = va_arg(param, long); /* switch off bits we can't support */ -#ifndef USE_SSLEAY +#ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ #endif #ifndef HAVE_GSSAPI @@ -1153,14 +1149,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...) */ argptr = va_arg(param, char *); if (argptr && argptr[0]) - result = Curl_SSL_set_engine(data, argptr); + result = Curl_ssl_set_engine(data, argptr); break; case CURLOPT_SSLENGINE_DEFAULT: /* * flag to set engine as default. */ - result = Curl_SSL_set_engine_default(data); + result = Curl_ssl_set_engine_default(data); break; case CURLOPT_CRLF: /* @@ -1450,9 +1446,7 @@ CURLcode Curl_disconnect(struct connectdata *conn) data->state.authproblem = FALSE; -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) Curl_ntlm_cleanup(conn); -#endif } if(conn->curl_disconnect) @@ -1481,7 +1475,7 @@ CURLcode Curl_disconnect(struct connectdata *conn) freed with idn_free() since this was allocated by libidn */ #endif - Curl_SSL_Close(conn); + Curl_ssl_close(conn); /* close possibly still open sockets */ if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) @@ -2419,10 +2413,10 @@ static CURLcode CreateConnection(struct SessionHandle *data, if(checkprefix("GOPHER.", conn->host.name)) strcpy(conn->protostr, "gopher"); -#ifdef USE_SSLEAY +#ifdef USE_SSL else if(checkprefix("FTPS", conn->host.name)) strcpy(conn->protostr, "ftps"); -#endif /* USE_SSLEAY */ +#endif /* USE_SSL */ else if(checkprefix("FTP.", conn->host.name)) strcpy(conn->protostr, "ftp"); else if(checkprefix("TELNET.", conn->host.name)) @@ -2728,7 +2722,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, #endif } else if (strequal(conn->protostr, "HTTPS")) { -#if defined(USE_SSLEAY) && !defined(CURL_DISABLE_HTTP) +#if defined(USE_SSL) && !defined(CURL_DISABLE_HTTP) conn->port = (data->set.use_port && data->state.allow_port)? data->set.use_port:PORT_HTTPS; @@ -2740,11 +2734,11 @@ static CURLcode CreateConnection(struct SessionHandle *data, conn->curl_done = Curl_http_done; conn->curl_connect = Curl_http_connect; -#else /* USE_SSLEAY */ +#else /* USE_SS */ failf(data, LIBCURL_NAME " was built with SSL disabled, https: not supported!"); return CURLE_UNSUPPORTED_PROTOCOL; -#endif /* !USE_SSLEAY */ +#endif /* !USE_SSL */ } else if (strequal(conn->protostr, "GOPHER")) { #ifndef CURL_DISABLE_GOPHER @@ -2774,7 +2768,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, int port = PORT_FTP; if(strequal(conn->protostr, "FTPS")) { -#ifdef USE_SSLEAY +#ifdef USE_SSL conn->protocol |= PROT_FTPS|PROT_SSL; conn->ssl[SECONDARYSOCKET].use = TRUE; /* send data securely */ port = PORT_FTPS; @@ -2782,7 +2776,7 @@ static CURLcode CreateConnection(struct SessionHandle *data, failf(data, LIBCURL_NAME " was built with SSL disabled, ftps: not supported!"); return CURLE_UNSUPPORTED_PROTOCOL; -#endif /* !USE_SSLEAY */ +#endif /* !USE_SSL */ } conn->port = (data->set.use_port && data->state.allow_port)? @@ -3739,89 +3733,3 @@ CURLcode Curl_do_more(struct connectdata *conn) return result; } -static bool safe_strequal(char* str1, char* str2) -{ - if(str1 && str2) - /* both pointers point to something then compare them */ - return strequal(str1, str2); - else - /* if both pointers are NULL then treat them as equal */ - return (!str1 && !str2); -} - -bool -Curl_ssl_config_matches(struct ssl_config_data* data, - struct ssl_config_data* needle) -{ - if((data->version == needle->version) && - (data->verifypeer == needle->verifypeer) && - (data->verifyhost == needle->verifyhost) && - safe_strequal(data->CApath, needle->CApath) && - safe_strequal(data->CAfile, needle->CAfile) && - safe_strequal(data->random_file, needle->random_file) && - safe_strequal(data->egdsocket, needle->egdsocket) && - safe_strequal(data->cipher_list, needle->cipher_list)) - return TRUE; - - return FALSE; -} - -bool -Curl_clone_ssl_config(struct ssl_config_data *source, - struct ssl_config_data *dest) -{ - dest->verifyhost = source->verifyhost; - dest->verifypeer = source->verifypeer; - dest->version = source->version; - - if(source->CAfile) { - dest->CAfile = strdup(source->CAfile); - if(!dest->CAfile) - return FALSE; - } - - if(source->CApath) { - dest->CApath = strdup(source->CApath); - if(!dest->CApath) - return FALSE; - } - - if(source->cipher_list) { - dest->cipher_list = strdup(source->cipher_list); - if(!dest->cipher_list) - return FALSE; - } - - if(source->egdsocket) { - dest->egdsocket = strdup(source->egdsocket); - if(!dest->egdsocket) - return FALSE; - } - - if(source->random_file) { - dest->random_file = strdup(source->random_file); - if(!dest->random_file) - return FALSE; - } - - return TRUE; -} - -void Curl_free_ssl_config(struct ssl_config_data* sslc) -{ - if(sslc->CAfile) - free(sslc->CAfile); - - if(sslc->CApath) - free(sslc->CApath); - - if(sslc->cipher_list) - free(sslc->cipher_list); - - if(sslc->egdsocket) - free(sslc->egdsocket); - - if(sslc->random_file) - free(sslc->random_file); -} - diff --git a/lib/url.h b/lib/url.h index 5a1dff266..7b3a76319 100644 --- a/lib/url.h +++ b/lib/url.h @@ -41,11 +41,6 @@ CURLcode Curl_disconnect(struct connectdata *); CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done); CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done); CURLcode Curl_protocol_doing(struct connectdata *conn, bool *done); -bool Curl_ssl_config_matches(struct ssl_config_data* data, - struct ssl_config_data* needle); -bool Curl_clone_ssl_config(struct ssl_config_data* source, - struct ssl_config_data* dest); -void Curl_free_ssl_config(struct ssl_config_data* sslc); void Curl_safefree(void *ptr); CURLcode Curl_protocol_fdset(struct connectdata *conn, fd_set *read_fd_set, diff --git a/lib/urldata.h b/lib/urldata.h index 6fa65fcdf..27b5e2d1b 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -50,7 +50,6 @@ #include "formdata.h" #ifdef USE_SSLEAY -/* SSLeay stuff usually in /usr/local/ssl/include */ #ifdef USE_OPENSSL #include "openssl/rsa.h" #include "openssl/crypto.h" @@ -64,14 +63,18 @@ #ifdef HAVE_OPENSSL_PKCS12_H #include #endif -#else +#else /* SSLeay-style includes */ #include "rsa.h" #include "crypto.h" #include "x509.h" #include "pem.h" #include "ssl.h" #include "err.h" -#endif +#endif /* USE_OPENSSL */ +#endif /* USE_SSLEAY */ + +#ifdef USE_GNUTLS +#include #endif #ifdef HAVE_NETINET_IN_H @@ -139,6 +142,10 @@ struct ssl_connect_data { SSL* handle; X509* server_cert; #endif /* USE_SSLEAY */ +#ifdef USE_GNUTLS + gnutls_session session; + gnutls_anon_client_credentials cred; +#endif /* USE_GNUTLS */ }; struct ssl_config_data { @@ -162,6 +169,7 @@ struct ssl_config_data { struct curl_ssl_session { char *name; /* host name for which this ID was used */ void *sessionid; /* as returned from the SSL layer */ + size_t idsize; /* if known, otherwise 0 */ long age; /* just a number, the higher the more recent */ unsigned short remote_port; /* remote port to connect to */ struct ssl_config_data ssl_config; /* setup for this session */ @@ -659,20 +667,16 @@ struct connectdata { char *newurl; /* This can only be set if a Location: was in the document headers */ + int sec_complete; /* if krb4 is enabled for this connection */ #ifdef HAVE_KRB4 enum protection_level command_prot; enum protection_level data_prot; enum protection_level request_data_prot; - size_t buffer_size; - struct krb4buffer in_buffer, out_buffer; - int sec_complete; void *app_data; - const struct Curl_sec_client_mech *mech; struct sockaddr_in local_addr; - #endif /*************** Request - specific items ************/ diff --git a/lib/version.c b/lib/version.c index 34741bb2b..f7fe757ec 100644 --- a/lib/version.c +++ b/lib/version.c @@ -28,6 +28,7 @@ #include #include "urldata.h" +#include "sslgen.h" #define _MPRINTF_REPLACE /* use the internal *printf() functions */ #include @@ -40,87 +41,20 @@ #include #endif -#ifdef USE_SSLEAY -static int getssl_version(char *ptr, size_t left, long *num) -{ - -#if (SSLEAY_VERSION_NUMBER >= 0x905000) - { - char sub[2]; - unsigned long ssleay_value; - sub[1]='\0'; - ssleay_value=SSLeay(); - *num = (long)ssleay_value; - if(ssleay_value < 0x906000) { - ssleay_value=SSLEAY_VERSION_NUMBER; - sub[0]='\0'; - } - else { - if(ssleay_value&0xff0) { - sub[0]=(char)((ssleay_value>>4)&0xff) + 'a' -1; - } - else - sub[0]='\0'; - } - - return snprintf(ptr, left, " OpenSSL/%lx.%lx.%lx%s", - (ssleay_value>>28)&0xf, - (ssleay_value>>20)&0xff, - (ssleay_value>>12)&0xff, - sub); - } - -#else - *num = SSLEAY_VERSION_NUMBER; -#if (SSLEAY_VERSION_NUMBER >= 0x900000) - return snprintf(ptr, left, " OpenSSL/%lx.%lx.%lx", - (SSLEAY_VERSION_NUMBER>>28)&0xff, - (SSLEAY_VERSION_NUMBER>>20)&0xff, - (SSLEAY_VERSION_NUMBER>>12)&0xf); -#else - { - char sub[2]; - sub[1]='\0'; - if(SSLEAY_VERSION_NUMBER&0x0f) { - sub[0]=(SSLEAY_VERSION_NUMBER&0x0f) + 'a' -1; - } - else - sub[0]='\0'; - - return snprintf(ptr, left, " SSL/%x.%x.%x%s", - (SSLEAY_VERSION_NUMBER>>12)&0xff, - (SSLEAY_VERSION_NUMBER>>8)&0xf, - (SSLEAY_VERSION_NUMBER>>4)&0xf, sub); - } -#endif -#endif -} - -#endif char *curl_version(void) { static char version[200]; char *ptr=version; - /* to prevent compier warnings, we only declare len if we have code - that uses it */ -#if defined(USE_SSLEAY) || defined(HAVE_LIBZ) || defined(USE_ARES) || \ - defined(USE_LIBIDN) - int len; -#endif + size_t len; size_t left = sizeof(version); strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION ); ptr=strchr(ptr, '\0'); left -= strlen(ptr); -#ifdef USE_SSLEAY - { - long num; - len = getssl_version(ptr, left, &num); - left -= len; - ptr += len; - } -#endif + len = Curl_ssl_version(ptr, left); + left -= len; + ptr += len; #ifdef HAVE_LIBZ len = snprintf(ptr, left, " zlib/%s", zlibVersion()); @@ -169,7 +103,7 @@ static const char * const protocols[] = { "file", #endif -#ifdef USE_SSLEAY +#ifdef USE_SSL #ifndef CURL_DISABLE_HTTP "https", #endif @@ -192,10 +126,10 @@ static curl_version_info_data version_info = { #ifdef HAVE_KRB4 | CURL_VERSION_KERBEROS4 #endif -#ifdef USE_SSLEAY +#ifdef USE_SSL | CURL_VERSION_SSL #endif -#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) +#ifdef USE_NTLM | CURL_VERSION_NTLM #endif #ifdef USE_WINDOWS_SSPI @@ -221,7 +155,7 @@ static curl_version_info_data version_info = { #endif , NULL, /* ssl_version */ - 0, /* ssl_version_num */ + 0, /* ssl_version_num, this is kept at zero */ NULL, /* zlib_version */ protocols, NULL, /* c-ares version */ @@ -231,14 +165,10 @@ static curl_version_info_data version_info = { curl_version_info_data *curl_version_info(CURLversion stamp) { -#ifdef USE_SSLEAY +#ifdef USE_SSL static char ssl_buffer[80]; - long num; - getssl_version(ssl_buffer, sizeof(ssl_buffer), &num); - + Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer)); version_info.ssl_version = ssl_buffer; - version_info.ssl_version_num = num; - /* SSL stuff is left zero if undefined */ #endif #ifdef HAVE_LIBZ