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

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 <curl/curl.h>
#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();

View File

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

View File

@ -33,7 +33,7 @@
#include <stdarg.h>
#include <stdlib.h>
#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;

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 "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;
}

View File

@ -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 <stdio.h>
@ -769,5 +768,5 @@ Curl_ntlm_cleanup(struct connectdata *conn)
#endif
}
#endif /* USE_SSLEAY */
#endif /* USE_NTLM */
#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);
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 */

View File

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

View File

@ -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 <curl/mprintf.h>
#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 <string.h>
#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;
}

View File

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

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
* Linas Vepstas <linas@linas.org> and Sampo Kellomaki <sampo@iki.fi>
* 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 <curl/mprintf.h>
@ -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; (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,
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 */

View File

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

View File

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

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 "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);
}

View File

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

View File

@ -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 <openssl/pkcs12.h>
#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 <gnutls/gnutls.h>
#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 ************/

View File

@ -28,6 +28,7 @@
#include <curl/curl.h>
#include "urldata.h"
#include "sslgen.h"
#define _MPRINTF_REPLACE /* use the internal *printf() functions */
#include <curl/mprintf.h>
@ -40,87 +41,20 @@
#include <stringprep.h>
#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