1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-21 23:58:49 -05:00

GnuTLS support added. There's now a "generic" SSL layer that we use all over

internally, with code provided by sslgen.c. All SSL-layer-specific code is
then written in ssluse.c (for OpenSSL) and gtls.c (for GnuTLS).

As far as possible, internals should not need to know what SSL layer that is
in use. Building with GnuTLS currently makes two test cases fail.

TODO.gnutls contains a few known outstanding issues for the GnuTLS support.

GnuTLS support is enabled with configure --with-gnutls
This commit is contained in:
Daniel Stenberg 2005-04-07 15:27:13 +00:00
parent 015a618172
commit 6e61939382
22 changed files with 1521 additions and 624 deletions

View File

@ -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 \ 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 \ 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 \ 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 \ 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 \ 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 \ 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 \ 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 \ 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

21
lib/TODO.gnutls Normal file
View File

@ -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

View File

@ -75,7 +75,7 @@
#include "urldata.h" #include "urldata.h"
#include <curl/curl.h> #include <curl/curl.h>
#include "transfer.h" #include "transfer.h"
#include "ssluse.h" #include "sslgen.h"
#include "url.h" #include "url.h"
#include "getinfo.h" #include "getinfo.h"
#include "hostip.h" #include "hostip.h"
@ -201,7 +201,7 @@ CURLcode curl_global_init(long flags)
Curl_ccalloc = (curl_calloc_callback)calloc; Curl_ccalloc = (curl_calloc_callback)calloc;
if (flags & CURL_GLOBAL_SSL) if (flags & CURL_GLOBAL_SSL)
if (!Curl_SSL_init()) if (!Curl_ssl_init())
return CURLE_FAILED_INIT; return CURLE_FAILED_INIT;
if (flags & CURL_GLOBAL_WIN32) if (flags & CURL_GLOBAL_WIN32)
@ -266,7 +266,7 @@ void curl_global_cleanup(void)
Curl_global_host_cache_dtor(); Curl_global_host_cache_dtor();
if (init_flags & CURL_GLOBAL_SSL) if (init_flags & CURL_GLOBAL_SSL)
Curl_SSL_cleanup(); Curl_ssl_cleanup();
if (init_flags & CURL_GLOBAL_WIN32) if (init_flags & CURL_GLOBAL_WIN32)
win32_cleanup(); win32_cleanup();

View File

@ -87,7 +87,7 @@
#include "strtoofft.h" #include "strtoofft.h"
#include "strequal.h" #include "strequal.h"
#include "ssluse.h" #include "sslgen.h"
#include "connect.h" #include "connect.h"
#include "strerror.h" #include "strerror.h"
#include "memory.h" #include "memory.h"
@ -2013,7 +2013,7 @@ static CURLcode ftp_state_stor_resp(struct connectdata *conn,
do the TLS stuff */ do the TLS stuff */
infof(data, "Doing the SSL/TLS handshake on the data stream\n"); infof(data, "Doing the SSL/TLS handshake on the data stream\n");
/* BLOCKING */ /* BLOCKING */
result = Curl_SSLConnect(conn, SECONDARYSOCKET); result = Curl_ssl_connect(conn, SECONDARYSOCKET);
if(result) if(result)
return 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 /* since we only have a plaintext TCP connection here, we must now
do the TLS stuff */ do the TLS stuff */
infof(data, "Doing the SSL/TLS handshake on the data stream\n"); 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) if(result)
return result; return result;
} }
@ -2373,8 +2373,8 @@ static CURLcode ftp_statemach_act(struct connectdata *conn)
*/ */
if((ftpcode == 234) || (ftpcode == 334)) { if((ftpcode == 234) || (ftpcode == 334)) {
/* Curl_SSLConnect is BLOCKING */ /* Curl_ssl_connect is BLOCKING */
result = Curl_SSLConnect(conn, FIRSTSOCKET); result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(CURLE_OK == result) { if(CURLE_OK == result) {
conn->protocol |= PROT_FTPS; conn->protocol |= PROT_FTPS;
conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */ conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
@ -2748,7 +2748,7 @@ CURLcode Curl_ftp_connect(struct connectdata *conn,
/* BLOCKING */ /* BLOCKING */
/* FTPS is simply ftp with SSL for the control channel */ /* FTPS is simply ftp with SSL for the control channel */
/* now, perform the SSL initialization for this socket */ /* now, perform the SSL initialization for this socket */
result = Curl_SSLConnect(conn, FIRSTSOCKET); result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result) if(result)
return result; return result;
} }

View File

@ -33,7 +33,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdlib.h> #include <stdlib.h>
#include "memory.h" #include "memory.h"
#include "ssluse.h" #include "sslgen.h"
/* Make this the last #include */ /* Make this the last #include */
#include "memdebug.h" #include "memdebug.h"
@ -182,7 +182,7 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
*param_longp = data->info.numconnects; *param_longp = data->info.numconnects;
break; break;
case CURLINFO_SSL_ENGINES: case CURLINFO_SSL_ENGINES:
*param_slistp = Curl_SSL_engines_list(data); *param_slistp = Curl_ssl_engines_list(data);
break; break;
default: default:
return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_BAD_FUNCTION_ARGUMENT;

464
lib/gtls.c Normal file
View File

@ -0,0 +1,464 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, 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 <gnutls/gnutls.h>
#include <gnutls/x509.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#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 <curl/mprintf.h>
#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 */

45
lib/gtls.h Normal file
View File

@ -0,0 +1,45 @@
#ifndef __GTLS_H
#define __GTLS_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, 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

View File

@ -85,7 +85,7 @@
#include "base64.h" #include "base64.h"
#include "cookie.h" #include "cookie.h"
#include "strequal.h" #include "strequal.h"
#include "ssluse.h" #include "sslgen.h"
#include "http_digest.h" #include "http_digest.h"
#include "http_ntlm.h" #include "http_ntlm.h"
#include "http_negotiate.h" #include "http_negotiate.h"
@ -416,7 +416,7 @@ Curl_http_output_auth(struct connectdata *conn,
/* Send proxy authentication header if needed */ /* Send proxy authentication header if needed */
if (conn->bits.httpproxy && if (conn->bits.httpproxy &&
(conn->bits.tunnel_proxy == proxytunnel)) { (conn->bits.tunnel_proxy == proxytunnel)) {
#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) #ifdef USE_NTLM
if(authproxy->picked == CURLAUTH_NTLM) { if(authproxy->picked == CURLAUTH_NTLM) {
auth=(char *)"NTLM"; auth=(char *)"NTLM";
result = Curl_output_ntlm(conn, TRUE); result = Curl_output_ntlm(conn, TRUE);
@ -484,7 +484,7 @@ Curl_http_output_auth(struct connectdata *conn,
} }
else else
#endif #endif
#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) #ifdef USE_NTLM
if(authhost->picked == CURLAUTH_NTLM) { if(authhost->picked == CURLAUTH_NTLM) {
auth=(char *)"NTLM"; auth=(char *)"NTLM";
result = Curl_output_ntlm(conn, FALSE); result = Curl_output_ntlm(conn, FALSE);
@ -597,7 +597,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn,
} }
else else
#endif #endif
#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) #ifdef USE_NTLM
/* NTLM support requires the SSL crypto libs */ /* NTLM support requires the SSL crypto libs */
if(checkprefix("NTLM", start)) { if(checkprefix("NTLM", start)) {
*availp |= CURLAUTH_NTLM; *availp |= CURLAUTH_NTLM;
@ -1268,8 +1268,8 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
} }
if(conn->protocol & PROT_HTTPS) { if(conn->protocol & PROT_HTTPS) {
/* now, perform the SSL initialization for this socket */ /* perform SSL initialization for this socket */
result = Curl_SSLConnect(conn, FIRSTSOCKET); result = Curl_ssl_connect(conn, FIRSTSOCKET);
if(result) if(result)
return result; return result;
} }

View File

@ -30,8 +30,7 @@
*/ */
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP
#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) #ifdef USE_NTLM
/* We need OpenSSL for the crypto lib to provide us with MD4 and DES */
/* -- WIN32 approved -- */ /* -- WIN32 approved -- */
#include <stdio.h> #include <stdio.h>
@ -769,5 +768,5 @@ Curl_ntlm_cleanup(struct connectdata *conn)
#endif #endif
} }
#endif /* USE_SSLEAY */ #endif /* USE_NTLM */
#endif /* !CURL_DISABLE_HTTP */ #endif /* !CURL_DISABLE_HTTP */

View File

@ -39,6 +39,9 @@ CURLntlm Curl_input_ntlm(struct connectdata *conn, bool proxy, char *header);
CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy); CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);
void Curl_ntlm_cleanup(struct connectdata *conn); 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 */ /* Flag bits definitions based on http://davenport.sourceforge.net/ntlm.html */

View File

@ -44,13 +44,18 @@
#include "urldata.h" #include "urldata.h"
#include "sendf.h" #include "sendf.h"
#include "connect.h" /* for the Curl_ourerrno() proto */ #include "connect.h" /* for the Curl_ourerrno() proto */
#include "sslgen.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */ #define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h> #include <curl/mprintf.h>
#ifdef HAVE_KRB4 #ifdef HAVE_KRB4
#include "krb4.h" #include "krb4.h"
#else
#define Curl_sec_write(a,b,c,d) -1
#define Curl_sec_read(a,b,c,d) -1
#endif #endif
#include <string.h> #include <string.h>
#include "memory.h" #include "memory.h"
#include "strerror.h" #include "strerror.h"
@ -233,63 +238,18 @@ CURLcode Curl_write(struct connectdata *conn,
{ {
ssize_t bytes_written; ssize_t bytes_written;
CURLcode retcode; 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]); 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) { if (conn->ssl[num].use)
err = SSL_get_error(conn->ssl[num].handle, rc); /* only TRUE if SSL enabled */
bytes_written = Curl_ssl_send(conn, num, mem, len);
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;
}
else { else {
#else if(conn->sec_complete)
(void)conn; /* only TRUE if krb4 enabled */
#endif
#ifdef HAVE_KRB4
if(conn->sec_complete) {
bytes_written = Curl_sec_write(conn, sockfd, mem, len); bytes_written = Curl_sec_write(conn, sockfd, mem, len);
}
else else
#endif /* HAVE_KRB4 */
{
bytes_written = (ssize_t)swrite(sockfd, mem, len); bytes_written = (ssize_t)swrite(sockfd, mem, len);
}
if(-1 == bytes_written) { if(-1 == bytes_written) {
int err = Curl_ourerrno(); int err = Curl_ourerrno();
@ -311,10 +271,7 @@ CURLcode Curl_write(struct connectdata *conn,
failf(conn->data, "Send failure: %s", failf(conn->data, "Send failure: %s",
Curl_strerror(conn, err)); Curl_strerror(conn, err));
} }
#ifdef USE_SSLEAY
} }
#endif
*written = bytes_written; *written = bytes_written;
retcode = (-1 != bytes_written)?CURLE_OK:CURLE_SEND_ERROR; 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 *n) /* amount bytes read */
{ {
ssize_t nread; ssize_t nread;
#ifdef USE_SSLEAY
/* Set 'num' to 0 or 1, depending on which socket that has been sent here. /* 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 If it is the second socket, we set num to 1. Otherwise to 0. This lets
us use the correct ssl handle. */ us use the correct ssl handle. */
@ -384,45 +341,17 @@ int Curl_read(struct connectdata *conn, /* connection data */
*n=0; /* reset amount to zero */ *n=0; /* reset amount to zero */
if (conn->ssl[num].use) { if(conn->ssl[num].use) {
nread = (ssize_t)SSL_read(conn->ssl[num].handle, buf, (int)buffersize); nread = Curl_ssl_recv(conn, num, buf, buffersize);
if(nread < 0) { if(nread == -1)
/* failed SSL_read */ return -1; /* -1 from Curl_ssl_recv() means EWOULDBLOCK */
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;
}
}
} }
else { else {
#else
(void)conn;
#endif
*n=0; /* reset amount to zero */ *n=0; /* reset amount to zero */
#ifdef HAVE_KRB4
if(conn->sec_complete) if(conn->sec_complete)
nread = Curl_sec_read(conn, sockfd, buf, buffersize); nread = Curl_sec_read(conn, sockfd, buf, buffersize);
else else
#endif
nread = sread(sockfd, buf, buffersize); nread = sread(sockfd, buf, buffersize);
if(-1 == nread) { if(-1 == nread) {
@ -434,10 +363,7 @@ int Curl_read(struct connectdata *conn, /* connection data */
#endif #endif
return -1; return -1;
} }
#ifdef USE_SSLEAY
} }
#endif /* USE_SSLEAY */
*n = nread; *n = nread;
return CURLE_OK; return CURLE_OK;
} }

View File

@ -280,4 +280,12 @@ typedef int curl_socket_t;
#define HAVE_INET_NTOA_R_2_ARGS 1 #define HAVE_INET_NTOA_R_2_ARGS 1
#endif #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 */ #endif /* __CONFIG_H */

535
lib/sslgen.c Normal file
View File

@ -0,0 +1,535 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, 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 <string.h>
#include <stdlib.h>
#include <ctype.h>
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#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; (i<data->set.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 */
}

72
lib/sslgen.h Normal file
View File

@ -0,0 +1,72 @@
#ifndef __SSLGEN_H
#define __SSLGEN_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2005, Daniel Stenberg, <daniel@haxx.se>, 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

View File

@ -22,8 +22,13 @@
***************************************************************************/ ***************************************************************************/
/* /*
* The original SSL code for curl was written by * Source file for all OpenSSL-specific code for the TLS/SSL layer. No code
* Linas Vepstas <linas@linas.org> and Sampo Kellomaki <sampo@iki.fi> * 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" #include "setup.h"
@ -47,6 +52,7 @@
#include "connect.h" /* Curl_ourerrno() proto */ #include "connect.h" /* Curl_ourerrno() proto */
#include "strequal.h" #include "strequal.h"
#include "select.h" #include "select.h"
#include "sslgen.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */ #define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -447,8 +453,8 @@ int cert_stuff(struct connectdata *conn,
#endif #endif
case SSL_FILETYPE_PKCS12: case SSL_FILETYPE_PKCS12:
if(!cert_done) { if(!cert_done) {
failf(data, "file type P12 for private key not supported\n"); failf(data, "file type P12 for private key not supported\n");
return 0; return 0;
} }
break; break;
default: default:
@ -519,27 +525,20 @@ static char *SSL_strerror(unsigned long error, char *buf, size_t size)
return (buf); return (buf);
} }
/* "global" init done? */
static int init_ssl=0;
/* we have the "SSL is seeded" boolean global for the application to /* we have the "SSL is seeded" boolean global for the application to
prevent multiple time-consuming seedings in vain */ prevent multiple time-consuming seedings in vain */
static bool ssl_seeded = FALSE; static bool ssl_seeded = FALSE;
#endif /* USE_SSLEAY */ #endif /* USE_SSLEAY */
#ifdef USE_SSLEAY
/** /**
* Global SSL init * Global SSL init
* *
* @retval 0 error initializing SSL * @retval 0 error initializing SSL
* @retval 1 SSL initialized successfully * @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 #ifdef HAVE_ENGINE_LOAD_BUILTIN_ENGINES
ENGINE_load_builtin_engines(); ENGINE_load_builtin_engines();
#endif #endif
@ -551,57 +550,39 @@ int Curl_SSL_init(void)
if (!SSLeay_add_ssl_algorithms()) if (!SSLeay_add_ssl_algorithms())
return 0; return 0;
init_ssl++; /* never again */
#else
/* SSL disabled, do nothing */
#endif
return 1; return 1;
} }
/* Global cleanup */ #endif /* USE_SSLEAY */
void Curl_SSL_cleanup(void)
{
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
if(init_ssl) {
/* only cleanup if we did a previous init */
/* Free the SSL error strings */ /* Global cleanup */
ERR_free_strings(); void Curl_ossl_cleanup(void)
{
/* Free the SSL error strings */
ERR_free_strings();
/* EVP_cleanup() removes all ciphers and digests from the /* EVP_cleanup() removes all ciphers and digests from the
table. */ table. */
EVP_cleanup(); EVP_cleanup();
#ifdef HAVE_ENGINE_cleanup #ifdef HAVE_ENGINE_cleanup
ENGINE_cleanup(); ENGINE_cleanup();
#endif #endif
#ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
/* this function was not present in 0.9.6b, but was added sometimes /* this function was not present in 0.9.6b, but was added sometimes
later */ later */
CRYPTO_cleanup_all_ex_data(); CRYPTO_cleanup_all_ex_data();
#endif
init_ssl=0; /* not inited any more */
}
#else
/* SSL disabled, do nothing */
#endif #endif
} }
#ifndef USE_SSLEAY #endif /* USE_SSLEAY */
void Curl_SSL_Close(struct connectdata *conn)
{
(void)conn;
}
#endif
/* Selects an OpenSSL crypto engine /* 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) #if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H)
ENGINE *e = ENGINE_by_id(engine); ENGINE *e = ENGINE_by_id(engine);
@ -633,11 +614,12 @@ CURLcode Curl_SSL_set_engine(struct SessionHandle *data, const char *engine)
#endif #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 (data->state.engine) {
if (ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) { if (ENGINE_set_default(data->state.engine, ENGINE_METHOD_ALL) > 0) {
infof(data,"set default crypto engine %s\n", data->state.engine); 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 #else
(void) data; (void) data;
#endif #endif
return (CURLE_OK); return CURLE_OK;
} }
#endif /* USE_SSLEAY */
/* Return list of OpenSSL crypto engine names. /* 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; struct curl_slist *list = NULL;
#if defined(USE_SSLEAY) && defined(HAVE_OPENSSL_ENGINE_H) #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. * 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;
int i; /*
/* ERR_remove_state() frees the error queue associated with
ERR_remove_state() frees the error queue associated with thread pid. If pid == 0, the current thread will have its
thread pid. If pid == 0, the current thread will have its error queue removed.
error queue removed.
Since error queue data structures are allocated Since error queue data structures are allocated
automatically for new threads, they must be freed when automatically for new threads, they must be freed when
threads are terminated in oder to avoid memory leaks. threads are terminated in oder to avoid memory leaks.
*/ */
ERR_remove_state(0); ERR_remove_state(0);
for(i=0; i<2; i++) { for(i=0; i<2; i++) {
struct ssl_connect_data *connssl = &conn->ssl[i]; struct ssl_connect_data *connssl = &conn->ssl[i];
if(connssl->handle) { if(connssl->handle) {
(void)SSL_shutdown(connssl->handle); (void)SSL_shutdown(connssl->handle);
SSL_set_connect_state(connssl->handle); SSL_set_connect_state(connssl->handle);
SSL_free (connssl->handle); SSL_free (connssl->handle);
connssl->handle = NULL; connssl->handle = NULL;
}
if(connssl->ctx) {
SSL_CTX_free (connssl->ctx);
connssl->ctx = NULL;
}
connssl->use = FALSE; /* get back to ordinary socket usage */
} }
} if(connssl->ctx) {
} SSL_CTX_free (connssl->ctx);
connssl->ctx = 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;
} }
connssl->use = FALSE; /* get back to ordinary socket usage */
} }
*ssl_sessionid = (SSL_SESSION *)NULL;
return TRUE;
} }
/* void Curl_ossl_session_free(void *ptr)
* Kill a single session ID entry in the cache.
*/
static int Kill_Single_Session(struct curl_ssl_session *session)
{ {
if(session->sessionid) { /* free the ID */
/* defensive check */ SSL_SESSION_free(ptr);
/* 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;
} }
/* /*
* This function is called when the 'data' struct is going away. Close * This function is called when the 'data' struct is going away. Close
* down everything and free all resources! * 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 #ifdef HAVE_OPENSSL_ENGINE_H
if(data->state.engine) { if(data->state.engine) {
ENGINE_finish(data->state.engine); ENGINE_finish(data->state.engine);
@ -817,74 +711,6 @@ int Curl_SSL_Close_All(struct SessionHandle *data)
return 0; 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; (i<data->set.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, static int Curl_ASN1_UTCTIME_output(struct connectdata *conn,
const char *prefix, const char *prefix,
ASN1_UTCTIME *tm) ASN1_UTCTIME *tm)
@ -1280,28 +1106,25 @@ static void ssl_tls_trace(int direction, int ssl_ver, int content_type,
} }
#endif #endif
#ifdef USE_SSLEAY
/* ====================================================== */ /* ====================================================== */
CURLcode CURLcode
Curl_SSLConnect(struct connectdata *conn, Curl_ossl_connect(struct connectdata *conn,
int sockindex) int sockindex)
{ {
CURLcode retcode = CURLE_OK; CURLcode retcode = CURLE_OK;
#ifdef USE_SSLEAY
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
int err; int err;
long lerr; long lerr;
int what; int what;
char * str; char * str;
SSL_METHOD *req_method; SSL_METHOD *req_method;
SSL_SESSION *ssl_sessionid=NULL; void *ssl_sessionid=NULL;
ASN1_TIME *certdate; ASN1_TIME *certdate;
curl_socket_t sockfd = conn->sock[sockindex]; curl_socket_t sockfd = conn->sock[sockindex];
struct ssl_connect_data *connssl = &conn->ssl[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) { if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
/* Make funny stuff to get random input */ /* Make funny stuff to get random input */
random_the_seed(data); random_the_seed(data);
@ -1447,19 +1270,16 @@ Curl_SSLConnect(struct connectdata *conn,
connssl->server_cert = 0x0; connssl->server_cert = 0x0;
if(!conn->bits.reuse) { /* Check if there's a cached ID we can/should use here! */
/* We're not re-using a connection, check if there's a cached ID we if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
can/should use here! */ /* we got a session id, use it! */
if(!Get_SSL_Session(conn, &ssl_sessionid)) { if (!SSL_set_session(connssl->handle, ssl_sessionid)) {
/* we got a session id, use it! */ failf(data, "SSL: SSL_set_session failed: %s",
if (!SSL_set_session(connssl->handle, ssl_sessionid)) { ERR_error_string(ERR_get_error(),NULL));
failf(data, "SSL: SSL_set_session failed: %s", return CURLE_SSL_CONNECT_ERROR;
ERR_error_string(ERR_get_error(),NULL));
return CURLE_SSL_CONNECT_ERROR;
}
/* Informational message */
infof (data, "SSL re-using session ID\n");
} }
/* Informational message */
infof (data, "SSL re-using session ID\n");
} }
/* pass the raw socket into the SSL layers */ /* pass the raw socket into the SSL layers */
@ -1473,16 +1293,13 @@ Curl_SSLConnect(struct connectdata *conn,
int writefd; int writefd;
int readfd; int readfd;
long timeout_ms; long timeout_ms;
long has_passed;
/* Find out if any timeout is set. If not, use 300 seconds. /* Find out if any timeout is set. If not, use 300 seconds.
Otherwise, figure out the most strict timeout of the two possible one 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 and then how much time that has elapsed to know how much time we
allow for the connect call */ allow for the connect call */
if(data->set.timeout || data->set.connecttimeout) { 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 */ /* get the most strict timeout of the ones converted to milliseconds */
if(data->set.timeout && if(data->set.timeout &&
@ -1490,20 +1307,22 @@ Curl_SSLConnect(struct connectdata *conn,
timeout_ms = data->set.timeout*1000; timeout_ms = data->set.timeout*1000;
else else
timeout_ms = data->set.connecttimeout*1000; 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 else
/* no particular time-out has been set */ /* no particular time-out has been set */
timeout_ms= DEFAULT_CONNECT_TIMEOUT; 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; readfd = CURL_SOCKET_BAD;
writefd = CURL_SOCKET_BAD; writefd = CURL_SOCKET_BAD;
@ -1586,12 +1405,7 @@ Curl_SSLConnect(struct connectdata *conn,
return CURLE_OPERATION_TIMEDOUT; return CURLE_OPERATION_TIMEDOUT;
} }
else { else {
#if !defined(WIN32) && defined(EINTR) /* anything that gets here is fatally bad */
/* 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 */
failf(data, "select on SSL socket, errno: %d", Curl_ourerrno()); failf(data, "select on SSL socket, errno: %d", Curl_ourerrno());
return CURLE_SSL_CONNECT_ERROR; return CURLE_SSL_CONNECT_ERROR;
} }
@ -1605,9 +1419,30 @@ Curl_SSLConnect(struct connectdata *conn,
if(!ssl_sessionid) { if(!ssl_sessionid) {
/* Since this is not a cached session ID, then we want to stach this one /* Since this is not a cached session ID, then we want to stach this one
in the cache! */ 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) { if(retcode) {
failf(data,"failure to store ssl session"); failf(data, "failed to store ssl session");
return retcode; return retcode;
} }
} }
@ -1686,9 +1521,145 @@ Curl_SSLConnect(struct connectdata *conn,
X509_free(connssl->server_cert); X509_free(connssl->server_cert);
connssl->server_cert = NULL; connssl->server_cert = NULL;
#else /* USE_SSLEAY */
(void)conn;
(void)sockindex;
#endif
return retcode; 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 */

View File

@ -22,27 +22,43 @@
* *
* $Id$ * $Id$
***************************************************************************/ ***************************************************************************/
/*
* This header should only be needed to get included by sslgen.c and ssluse.c
*/
#include "urldata.h" #include "urldata.h"
CURLcode Curl_SSLConnect(struct connectdata *conn, int sockindex); CURLcode Curl_ossl_connect(struct connectdata *conn, int sockindex);
void Curl_ossl_close(struct connectdata *conn); /* close a SSL connection */
int Curl_SSL_init(void); /* Global SSL init */ /* tell OpenSSL to close down all open information regarding connections (and
void Curl_SSL_cleanup(void); /* Global SSL cleanup */ thus session ID caching etc) */
int Curl_ossl_close_all(struct SessionHandle *data);
/* 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);
/* Sets an OpenSSL engine */ /* 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 */ /* function provided for the generic SSL-layer, called when a session id
CURLcode Curl_SSL_set_engine_default(struct SessionHandle *data); 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 */ /* 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 #endif

View File

@ -94,7 +94,7 @@
#include "http.h" #include "http.h"
#include "url.h" #include "url.h"
#include "getinfo.h" #include "getinfo.h"
#include "ssluse.h" #include "sslgen.h"
#include "http_digest.h" #include "http_digest.h"
#include "http_ntlm.h" #include "http_ntlm.h"
#include "http_negotiate.h" #include "http_negotiate.h"
@ -243,6 +243,7 @@ CURLcode Curl_readrewind(struct connectdata *conn)
} }
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
/* FIX: this is nasty OpenSSL-specific code that really shouldn't be here */
static int data_pending(struct connectdata *conn) static int data_pending(struct connectdata *conn)
{ {
if(conn->ssl[FIRSTSOCKET].handle) if(conn->ssl[FIRSTSOCKET].handle)
@ -1597,20 +1598,17 @@ Transfer(struct connectdata *conn)
*/ */
CURLcode Curl_pretransfer(struct SessionHandle *data) CURLcode Curl_pretransfer(struct SessionHandle *data)
{ {
CURLcode res;
if(!data->change.url) if(!data->change.url)
/* we can't do anything wihout URL */ /* we can't do anything wihout URL */
return CURLE_URL_MALFORMAT; 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
/* Init the SSL session ID cache here. We do it here since we want to do before any transfer takes place. */
it after the *_setopt() calls (that could change the size of the cache) res = Curl_ssl_initsessions(data, data->set.ssl.numsessions);
but before any transfer takes place. */ if(res)
CURLcode res = Curl_SSL_InitSessions(data, data->set.ssl.numsessions); return res;
if(res)
return res;
}
#endif
data->set.followlocation=0; /* reset the location-follow counter */ data->set.followlocation=0; /* reset the location-follow counter */
data->state.this_is_a_follow = FALSE; /* reset this */ data->state.this_is_a_follow = FALSE; /* reset this */

120
lib/url.c
View File

@ -102,7 +102,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, not provided by
#include "formdata.h" #include "formdata.h"
#include "base64.h" #include "base64.h"
#include "ssluse.h" #include "sslgen.h"
#include "hostip.h" #include "hostip.h"
#include "transfer.h" #include "transfer.h"
#include "sendf.h" #include "sendf.h"
@ -154,7 +154,6 @@ static bool ConnectionExists(struct SessionHandle *data,
struct connectdata **usethis); struct connectdata **usethis);
static long ConnectionStore(struct SessionHandle *data, static long ConnectionStore(struct SessionHandle *data,
struct connectdata *conn); struct connectdata *conn);
static bool safe_strequal(char* str1, char* str2);
#ifndef USE_ARES #ifndef USE_ARES
/* not for Win32, unless it is cygwin /* 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 */ /* Close down all open SSL info and sessions */
Curl_SSL_Close_All(data); Curl_ssl_close_all(data);
#endif
Curl_safefree(data->state.first_host); Curl_safefree(data->state.first_host);
Curl_safefree(data->state.scratch); Curl_safefree(data->state.scratch);
@ -832,7 +828,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
{ {
long auth = va_arg(param, long); long auth = va_arg(param, long);
/* switch off bits we can't support */ /* 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 */ auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
#endif #endif
#ifndef HAVE_GSSAPI #ifndef HAVE_GSSAPI
@ -852,7 +848,7 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
{ {
long auth = va_arg(param, long); long auth = va_arg(param, long);
/* switch off bits we can't support */ /* switch off bits we can't support */
#ifndef USE_SSLEAY #ifndef USE_NTLM
auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */
#endif #endif
#ifndef HAVE_GSSAPI #ifndef HAVE_GSSAPI
@ -1153,14 +1149,14 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
*/ */
argptr = va_arg(param, char *); argptr = va_arg(param, char *);
if (argptr && argptr[0]) if (argptr && argptr[0])
result = Curl_SSL_set_engine(data, argptr); result = Curl_ssl_set_engine(data, argptr);
break; break;
case CURLOPT_SSLENGINE_DEFAULT: case CURLOPT_SSLENGINE_DEFAULT:
/* /*
* flag to set engine as default. * flag to set engine as default.
*/ */
result = Curl_SSL_set_engine_default(data); result = Curl_ssl_set_engine_default(data);
break; break;
case CURLOPT_CRLF: case CURLOPT_CRLF:
/* /*
@ -1450,9 +1446,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
data->state.authproblem = FALSE; data->state.authproblem = FALSE;
#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI)
Curl_ntlm_cleanup(conn); Curl_ntlm_cleanup(conn);
#endif
} }
if(conn->curl_disconnect) if(conn->curl_disconnect)
@ -1481,7 +1475,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
freed with idn_free() since this was freed with idn_free() since this was
allocated by libidn */ allocated by libidn */
#endif #endif
Curl_SSL_Close(conn); Curl_ssl_close(conn);
/* close possibly still open sockets */ /* close possibly still open sockets */
if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
@ -2419,10 +2413,10 @@ static CURLcode CreateConnection(struct SessionHandle *data,
if(checkprefix("GOPHER.", conn->host.name)) if(checkprefix("GOPHER.", conn->host.name))
strcpy(conn->protostr, "gopher"); strcpy(conn->protostr, "gopher");
#ifdef USE_SSLEAY #ifdef USE_SSL
else if(checkprefix("FTPS", conn->host.name)) else if(checkprefix("FTPS", conn->host.name))
strcpy(conn->protostr, "ftps"); strcpy(conn->protostr, "ftps");
#endif /* USE_SSLEAY */ #endif /* USE_SSL */
else if(checkprefix("FTP.", conn->host.name)) else if(checkprefix("FTP.", conn->host.name))
strcpy(conn->protostr, "ftp"); strcpy(conn->protostr, "ftp");
else if(checkprefix("TELNET.", conn->host.name)) else if(checkprefix("TELNET.", conn->host.name))
@ -2728,7 +2722,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
#endif #endif
} }
else if (strequal(conn->protostr, "HTTPS")) { 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)? conn->port = (data->set.use_port && data->state.allow_port)?
data->set.use_port:PORT_HTTPS; data->set.use_port:PORT_HTTPS;
@ -2740,11 +2734,11 @@ static CURLcode CreateConnection(struct SessionHandle *data,
conn->curl_done = Curl_http_done; conn->curl_done = Curl_http_done;
conn->curl_connect = Curl_http_connect; conn->curl_connect = Curl_http_connect;
#else /* USE_SSLEAY */ #else /* USE_SS */
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME
" was built with SSL disabled, https: not supported!"); " was built with SSL disabled, https: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL; return CURLE_UNSUPPORTED_PROTOCOL;
#endif /* !USE_SSLEAY */ #endif /* !USE_SSL */
} }
else if (strequal(conn->protostr, "GOPHER")) { else if (strequal(conn->protostr, "GOPHER")) {
#ifndef CURL_DISABLE_GOPHER #ifndef CURL_DISABLE_GOPHER
@ -2774,7 +2768,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
int port = PORT_FTP; int port = PORT_FTP;
if(strequal(conn->protostr, "FTPS")) { if(strequal(conn->protostr, "FTPS")) {
#ifdef USE_SSLEAY #ifdef USE_SSL
conn->protocol |= PROT_FTPS|PROT_SSL; conn->protocol |= PROT_FTPS|PROT_SSL;
conn->ssl[SECONDARYSOCKET].use = TRUE; /* send data securely */ conn->ssl[SECONDARYSOCKET].use = TRUE; /* send data securely */
port = PORT_FTPS; port = PORT_FTPS;
@ -2782,7 +2776,7 @@ static CURLcode CreateConnection(struct SessionHandle *data,
failf(data, LIBCURL_NAME failf(data, LIBCURL_NAME
" was built with SSL disabled, ftps: not supported!"); " was built with SSL disabled, ftps: not supported!");
return CURLE_UNSUPPORTED_PROTOCOL; return CURLE_UNSUPPORTED_PROTOCOL;
#endif /* !USE_SSLEAY */ #endif /* !USE_SSL */
} }
conn->port = (data->set.use_port && data->state.allow_port)? conn->port = (data->set.use_port && data->state.allow_port)?
@ -3739,89 +3733,3 @@ CURLcode Curl_do_more(struct connectdata *conn)
return result; 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);
}

View File

@ -41,11 +41,6 @@ CURLcode Curl_disconnect(struct connectdata *);
CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done); CURLcode Curl_protocol_connect(struct connectdata *conn, bool *done);
CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done); CURLcode Curl_protocol_connecting(struct connectdata *conn, bool *done);
CURLcode Curl_protocol_doing(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); void Curl_safefree(void *ptr);
CURLcode Curl_protocol_fdset(struct connectdata *conn, CURLcode Curl_protocol_fdset(struct connectdata *conn,
fd_set *read_fd_set, fd_set *read_fd_set,

View File

@ -50,7 +50,6 @@
#include "formdata.h" #include "formdata.h"
#ifdef USE_SSLEAY #ifdef USE_SSLEAY
/* SSLeay stuff usually in /usr/local/ssl/include */
#ifdef USE_OPENSSL #ifdef USE_OPENSSL
#include "openssl/rsa.h" #include "openssl/rsa.h"
#include "openssl/crypto.h" #include "openssl/crypto.h"
@ -64,14 +63,18 @@
#ifdef HAVE_OPENSSL_PKCS12_H #ifdef HAVE_OPENSSL_PKCS12_H
#include <openssl/pkcs12.h> #include <openssl/pkcs12.h>
#endif #endif
#else #else /* SSLeay-style includes */
#include "rsa.h" #include "rsa.h"
#include "crypto.h" #include "crypto.h"
#include "x509.h" #include "x509.h"
#include "pem.h" #include "pem.h"
#include "ssl.h" #include "ssl.h"
#include "err.h" #include "err.h"
#endif #endif /* USE_OPENSSL */
#endif /* USE_SSLEAY */
#ifdef USE_GNUTLS
#include <gnutls/gnutls.h>
#endif #endif
#ifdef HAVE_NETINET_IN_H #ifdef HAVE_NETINET_IN_H
@ -139,6 +142,10 @@ struct ssl_connect_data {
SSL* handle; SSL* handle;
X509* server_cert; X509* server_cert;
#endif /* USE_SSLEAY */ #endif /* USE_SSLEAY */
#ifdef USE_GNUTLS
gnutls_session session;
gnutls_anon_client_credentials cred;
#endif /* USE_GNUTLS */
}; };
struct ssl_config_data { struct ssl_config_data {
@ -162,6 +169,7 @@ struct ssl_config_data {
struct curl_ssl_session { struct curl_ssl_session {
char *name; /* host name for which this ID was used */ char *name; /* host name for which this ID was used */
void *sessionid; /* as returned from the SSL layer */ 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 */ long age; /* just a number, the higher the more recent */
unsigned short remote_port; /* remote port to connect to */ unsigned short remote_port; /* remote port to connect to */
struct ssl_config_data ssl_config; /* setup for this session */ 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 char *newurl; /* This can only be set if a Location: was in the
document headers */ document headers */
int sec_complete; /* if krb4 is enabled for this connection */
#ifdef HAVE_KRB4 #ifdef HAVE_KRB4
enum protection_level command_prot; enum protection_level command_prot;
enum protection_level data_prot; enum protection_level data_prot;
enum protection_level request_data_prot; enum protection_level request_data_prot;
size_t buffer_size; size_t buffer_size;
struct krb4buffer in_buffer, out_buffer; struct krb4buffer in_buffer, out_buffer;
int sec_complete;
void *app_data; void *app_data;
const struct Curl_sec_client_mech *mech; const struct Curl_sec_client_mech *mech;
struct sockaddr_in local_addr; struct sockaddr_in local_addr;
#endif #endif
/*************** Request - specific items ************/ /*************** Request - specific items ************/

View File

@ -28,6 +28,7 @@
#include <curl/curl.h> #include <curl/curl.h>
#include "urldata.h" #include "urldata.h"
#include "sslgen.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */ #define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h> #include <curl/mprintf.h>
@ -40,87 +41,20 @@
#include <stringprep.h> #include <stringprep.h>
#endif #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) char *curl_version(void)
{ {
static char version[200]; static char version[200];
char *ptr=version; char *ptr=version;
/* to prevent compier warnings, we only declare len if we have code size_t len;
that uses it */
#if defined(USE_SSLEAY) || defined(HAVE_LIBZ) || defined(USE_ARES) || \
defined(USE_LIBIDN)
int len;
#endif
size_t left = sizeof(version); size_t left = sizeof(version);
strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION ); strcpy(ptr, LIBCURL_NAME "/" LIBCURL_VERSION );
ptr=strchr(ptr, '\0'); ptr=strchr(ptr, '\0');
left -= strlen(ptr); left -= strlen(ptr);
#ifdef USE_SSLEAY len = Curl_ssl_version(ptr, left);
{ left -= len;
long num; ptr += len;
len = getssl_version(ptr, left, &num);
left -= len;
ptr += len;
}
#endif
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ
len = snprintf(ptr, left, " zlib/%s", zlibVersion()); len = snprintf(ptr, left, " zlib/%s", zlibVersion());
@ -169,7 +103,7 @@ static const char * const protocols[] = {
"file", "file",
#endif #endif
#ifdef USE_SSLEAY #ifdef USE_SSL
#ifndef CURL_DISABLE_HTTP #ifndef CURL_DISABLE_HTTP
"https", "https",
#endif #endif
@ -192,10 +126,10 @@ static curl_version_info_data version_info = {
#ifdef HAVE_KRB4 #ifdef HAVE_KRB4
| CURL_VERSION_KERBEROS4 | CURL_VERSION_KERBEROS4
#endif #endif
#ifdef USE_SSLEAY #ifdef USE_SSL
| CURL_VERSION_SSL | CURL_VERSION_SSL
#endif #endif
#if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) #ifdef USE_NTLM
| CURL_VERSION_NTLM | CURL_VERSION_NTLM
#endif #endif
#ifdef USE_WINDOWS_SSPI #ifdef USE_WINDOWS_SSPI
@ -221,7 +155,7 @@ static curl_version_info_data version_info = {
#endif #endif
, ,
NULL, /* ssl_version */ NULL, /* ssl_version */
0, /* ssl_version_num */ 0, /* ssl_version_num, this is kept at zero */
NULL, /* zlib_version */ NULL, /* zlib_version */
protocols, protocols,
NULL, /* c-ares version */ NULL, /* c-ares version */
@ -231,14 +165,10 @@ static curl_version_info_data version_info = {
curl_version_info_data *curl_version_info(CURLversion stamp) curl_version_info_data *curl_version_info(CURLversion stamp)
{ {
#ifdef USE_SSLEAY #ifdef USE_SSL
static char ssl_buffer[80]; static char ssl_buffer[80];
long num; Curl_ssl_version(ssl_buffer, sizeof(ssl_buffer));
getssl_version(ssl_buffer, sizeof(ssl_buffer), &num);
version_info.ssl_version = ssl_buffer; version_info.ssl_version = ssl_buffer;
version_info.ssl_version_num = num;
/* SSL stuff is left zero if undefined */
#endif #endif
#ifdef HAVE_LIBZ #ifdef HAVE_LIBZ