Newer
Older
Daniel Stenberg
committed
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
Daniel Stenberg
committed
*
* 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.
*
***************************************************************************/
/* This file is for implementing all "generic" SSL functions that all libcurl
internals should use. It is then responsible for calling the proper
"backend" function.
Daniel Stenberg
committed
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
Curl_nss_ - prefix for NSS ones
Curl_cyassl_ - prefix for CyaSSL ones
Curl_schannel_ - prefix for Schannel SSPI ones
Curl_darwinssl_ - prefix for SecureTransport (Darwin) ones
Daniel Stenberg
committed
Daniel Stenberg
committed
Note that this source code uses curlssl_* functions, and they are all
defines/macros #defined by the lib-specific header files.
Daniel Stenberg
committed
"SSL/TLS Strong Encryption: An Introduction"
http://httpd.apache.org/docs-2.0/ssl/ssl_intro.html
*/
#include "setup.h"
Daniel Stenberg
committed
#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 "nssg.h" /* NSS versions */
#include "qssl.h" /* QSOSSL versions */
#include "polarssl.h" /* PolarSSL versions */
#include "axtls.h" /* axTLS versions */
#include "cyassl.h" /* CyaSSL versions */
#include "curl_schannel.h" /* Schannel SSPI version */
#include "curl_darwinssl.h" /* SecureTransport (Darwin) version */
Daniel Stenberg
committed
#include "sendf.h"
Daniel Stenberg
committed
#include "rawstr.h"
Daniel Stenberg
committed
#include "url.h"
#include "progress.h"
Daniel Stenberg
committed
/* The last #include file should be: */
#include "memdebug.h"
/* convenience macro to check if this handle is using a shared SSL session */
#define SSLSESSION_SHARED(data) (data->share && \
(data->share->specifier & \
(1<<CURL_LOCK_DATA_SSL_SESSION)))
Daniel Stenberg
committed
static bool safe_strequal(char* str1, char* str2)
{
if(str1 && str2)
/* both pointers point to something then compare them */
return (0 != Curl_raw_equal(str1, str2)) ? TRUE : FALSE;
Daniel Stenberg
committed
else
/* if both pointers are NULL then treat them as equal */
return (!str1 && !str2) ? TRUE : FALSE;
Daniel Stenberg
committed
}
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)
{
Daniel Stenberg
committed
dest->sessionid = source->sessionid;
Daniel Stenberg
committed
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;
}
else
dest->CAfile = NULL;
Daniel Stenberg
committed
if(source->CApath) {
dest->CApath = strdup(source->CApath);
if(!dest->CApath)
return FALSE;
}
else
dest->CApath = NULL;
Daniel Stenberg
committed
if(source->cipher_list) {
dest->cipher_list = strdup(source->cipher_list);
if(!dest->cipher_list)
return FALSE;
}
else
dest->cipher_list = NULL;
Daniel Stenberg
committed
if(source->egdsocket) {
dest->egdsocket = strdup(source->egdsocket);
if(!dest->egdsocket)
return FALSE;
}
else
dest->egdsocket = NULL;
Daniel Stenberg
committed
if(source->random_file) {
dest->random_file = strdup(source->random_file);
if(!dest->random_file)
return FALSE;
}
else
dest->random_file = NULL;
Daniel Stenberg
committed
return TRUE;
}
void Curl_free_ssl_config(struct ssl_config_data* sslc)
{
Daniel Stenberg
committed
Curl_safefree(sslc->CAfile);
Curl_safefree(sslc->CApath);
Curl_safefree(sslc->cipher_list);
Curl_safefree(sslc->egdsocket);
Curl_safefree(sslc->random_file);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
#ifdef USE_SSL
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/* SSL 'backend' compile-time sanity checks */
#if !defined(curlssl_init)
# error "SSL backend lacks definition for curlssl_init"
#elif !defined(curlssl_cleanup)
# error "SSL backend lacks definition for curlssl_cleanup"
#elif !defined(curlssl_connect) && !defined(curlssl_connect_nonblocking)
# error "SSL backend lacks curlssl_connect or curlssl_connect_nonblocking"
#elif !defined(curlssl_session_free)
# error "SSL backend lacks definition for curlssl_session_free"
#elif !defined(curlssl_close_all)
# error "SSL backend lacks definition for curlssl_close_all"
#elif !defined(curlssl_close)
# error "SSL backend lacks definition for curlssl_close"
#elif !defined(curlssl_shutdown)
# error "SSL backend lacks definition for curlssl_shutdown"
#elif !defined(curlssl_set_engine)
# error "SSL backend lacks definition for curlssl_set_engine"
#elif !defined(curlssl_set_engine_default)
# error "SSL backend lacks definition for curlssl_set_engine_default"
#elif !defined(curlssl_engines_list)
# error "SSL backend lacks definition for curlssl_engines_list"
#elif !defined(curlssl_version)
# error "SSL backend lacks definition for curlssl_version"
#elif !defined(curlssl_check_cxn)
# error "SSL backend lacks definition for curlssl_check_cxn"
#elif !defined(curlssl_data_pending)
# error "SSL backend lacks definition for curlssl_data_pending"
#elif !defined(curlssl_random)
# error "SSL backend lacks definition for curlssl_random"
#elif !defined(curlssl_md5sum)
# error "SSL backend lacks definition for curlssl_md5sum"
#endif
Daniel Stenberg
committed
/* "global" init done? */
static bool init_ssl=FALSE;
Daniel Stenberg
committed
/**
* 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 */
Daniel Stenberg
committed
return curlssl_init();
Daniel Stenberg
committed
}
/* Global cleanup */
void Curl_ssl_cleanup(void)
{
if(init_ssl) {
/* only cleanup if we did a previous init */
Daniel Stenberg
committed
curlssl_cleanup();
Daniel Stenberg
committed
init_ssl = FALSE;
}
}
CURLcode
Curl_ssl_connect(struct connectdata *conn, int sockindex)
{
Daniel Stenberg
committed
/* mark this is being ssl-enabled from here on. */
Daniel Stenberg
committed
conn->ssl[sockindex].use = TRUE;
Daniel Stenberg
committed
conn->ssl[sockindex].state = ssl_connection_negotiating;
Daniel Stenberg
committed
res = curlssl_connect(conn, sockindex);
if(!res)
Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
return res;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
CURLcode
Curl_ssl_connect_nonblocking(struct connectdata *conn, int sockindex,
bool *done)
{
Daniel Stenberg
committed
CURLcode res;
Daniel Stenberg
committed
/* mark this is being ssl requested from here on. */
Daniel Stenberg
committed
conn->ssl[sockindex].use = TRUE;
#ifdef curlssl_connect_nonblocking
Daniel Stenberg
committed
res = curlssl_connect_nonblocking(conn, sockindex, done);
#else
*done = TRUE; /* fallback to BLOCKING */
res = curlssl_connect(conn, sockindex);
Daniel Stenberg
committed
#endif /* non-blocking connect support */
if(!res && *done)
Curl_pgrsTime(conn->data, TIMER_APPCONNECT); /* SSL is connected */
return res;
Daniel Stenberg
committed
}
Daniel Stenberg
committed
/*
* 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 *general_age;
bool no_match = TRUE;
*ssl_sessionid = NULL;
Daniel Stenberg
committed
Daniel Stenberg
committed
if(!conn->ssl_config.sessionid)
/* session ID re-use is disabled */
return TRUE;
if(SSLSESSION_SHARED(data)) {
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
general_age = &data->share->sessionage;
}
else
general_age = &data->state.sessionage;
for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
Daniel Stenberg
committed
check = &data->state.session[i];
if(!check->sessionid)
/* not session ID means blank entry */
continue;
if(Curl_raw_equal(conn->host.name, check->name) &&
Daniel Stenberg
committed
(conn->remote_port == check->remote_port) &&
Curl_ssl_config_matches(&conn->ssl_config, &check->ssl_config)) {
/* yes, we have a session ID! */
(*general_age)++; /* increase general age */
check->age = *general_age; /* set this as used in this age */
Daniel Stenberg
committed
*ssl_sessionid = check->sessionid;
if(idsize)
*idsize = check->idsize;
no_match = FALSE;
break;
Daniel Stenberg
committed
}
}
if(SSLSESSION_SHARED(data))
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
Daniel Stenberg
committed
}
/*
* Kill a single session ID entry in the cache.
*/
void Curl_ssl_kill_session(struct curl_ssl_session *session)
Daniel Stenberg
committed
{
if(session->sessionid) {
/* defensive check */
/* free the ID the SSL-layer specific way */
Daniel Stenberg
committed
curlssl_session_free(session->sessionid);
Daniel Stenberg
committed
session->age = 0; /* fresh */
Curl_free_ssl_config(&session->ssl_config);
Curl_safefree(session->name);
}
}
/*
* Delete the given session ID from the cache.
*/
void Curl_ssl_delsessionid(struct connectdata *conn, void *ssl_sessionid)
{
struct SessionHandle *data=conn->data;
if(SSLSESSION_SHARED(data))
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
for(i = 0; i < data->set.ssl.max_ssl_sessions; i++) {
struct curl_ssl_session *check = &data->state.session[i];
if(check->sessionid == ssl_sessionid) {
if(SSLSESSION_SHARED(data))
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
Daniel Stenberg
committed
/*
* 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)
{
Daniel Stenberg
committed
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;
long *general_age;
Daniel Stenberg
committed
Daniel Stenberg
committed
/* Even though session ID re-use might be disabled, that only disables USING
IT. We still store it here in case the re-using is again enabled for an
upcoming transfer */
Daniel Stenberg
committed
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) */
/* If using shared SSL session, lock! */
if(SSLSESSION_SHARED(data)) {
Curl_share_lock(data, CURL_LOCK_DATA_SSL_SESSION, CURL_LOCK_ACCESS_SINGLE);
general_age = &data->share->sessionage;
}
else {
general_age = &data->state.sessionage;
}
Daniel Stenberg
committed
/* find an empty slot for us, or find the oldest */
for(i = 1; (i < data->set.ssl.max_ssl_sessions) &&
Daniel Stenberg
committed
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.max_ssl_sessions)
Daniel Stenberg
committed
/* cache is full, we must "kill" the oldest entry! */
Daniel Stenberg
committed
else
store = &data->state.session[i]; /* use this slot */
/* now init the session struct wisely */
store->sessionid = ssl_sessionid;
store->idsize = idsize;
store->age = *general_age; /* set current age */
/* free it if there's one already present */
Daniel Stenberg
committed
store->name = clone_host; /* clone host name */
store->remote_port = conn->remote_port; /* port number */
if(SSLSESSION_SHARED(data))
Curl_share_unlock(data, CURL_LOCK_DATA_SSL_SESSION);
if(!Curl_clone_ssl_config(&conn->ssl_config, &store->ssl_config)) {
store->sessionid = NULL; /* let caller free sessionid */
free(clone_host);
Daniel Stenberg
committed
return CURLE_OUT_OF_MEMORY;
Daniel Stenberg
committed
return CURLE_OK;
}
void Curl_ssl_close_all(struct SessionHandle *data)
{
/* kill the session ID cache if not shared */
if(data->state.session && !SSLSESSION_SHARED(data)) {
for(i = 0; i < data->set.ssl.max_ssl_sessions; i++)
Daniel Stenberg
committed
/* the single-killer function handles empty table slots */
Curl_ssl_kill_session(&data->state.session[i]);
Daniel Stenberg
committed
/* free the cache data */
Curl_safefree(data->state.session);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
curlssl_close_all(data);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
void Curl_ssl_close(struct connectdata *conn, int sockindex)
Daniel Stenberg
committed
{
Daniel Stenberg
committed
DEBUGASSERT((sockindex <= 1) && (sockindex >= -1));
Daniel Stenberg
committed
curlssl_close(conn, sockindex);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
CURLcode Curl_ssl_shutdown(struct connectdata *conn, int sockindex)
{
Daniel Stenberg
committed
if(curlssl_shutdown(conn, sockindex))
Daniel Stenberg
committed
return CURLE_SSL_SHUTDOWN_FAILED;
conn->ssl[sockindex].use = FALSE; /* get back to ordinary socket usage */
Daniel Stenberg
committed
conn->ssl[sockindex].state = ssl_connection_none;
Daniel Stenberg
committed
conn->recv[sockindex] = Curl_recv_plain;
conn->send[sockindex] = Curl_send_plain;
Daniel Stenberg
committed
return CURLE_OK;
}
Daniel Stenberg
committed
/* Selects an SSL crypto engine
Daniel Stenberg
committed
*/
CURLcode Curl_ssl_set_engine(struct SessionHandle *data, const char *engine)
{
Daniel Stenberg
committed
return curlssl_set_engine(data, engine);
Daniel Stenberg
committed
}
Daniel Stenberg
committed
/* Selects the default SSL crypto engine
Daniel Stenberg
committed
*/
CURLcode Curl_ssl_set_engine_default(struct SessionHandle *data)
{
Daniel Stenberg
committed
return curlssl_set_engine_default(data);
Daniel Stenberg
committed
}
/* Return list of OpenSSL crypto engine names. */
struct curl_slist *Curl_ssl_engines_list(struct SessionHandle *data)
{
Daniel Stenberg
committed
return curlssl_engines_list(data);
Daniel Stenberg
committed
}
/*
* 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, size_t amount)
Daniel Stenberg
committed
{
struct curl_ssl_session *session;
if(data->state.session)
/* this is just a precaution to prevent multiple inits */
return CURLE_OK;
session = calloc(amount, sizeof(struct curl_ssl_session));
Daniel Stenberg
committed
if(!session)
return CURLE_OUT_OF_MEMORY;
/* store the info in the SSL section */
data->set.ssl.max_ssl_sessions = amount;
Daniel Stenberg
committed
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)
{
Daniel Stenberg
committed
return curlssl_version(buffer, size);
Daniel Stenberg
committed
}
/*
* This function tries to determine connection status.
*
* Return codes:
* 1 means the connection is still in place
* 0 means the connection has been closed
* -1 means the connection status is unknown
*/
int Curl_ssl_check_cxn(struct connectdata *conn)
{
Daniel Stenberg
committed
return curlssl_check_cxn(conn);
}
bool Curl_ssl_data_pending(const struct connectdata *conn,
int connindex)
{
Daniel Stenberg
committed
return curlssl_data_pending(conn, connindex);
Daniel Stenberg
committed
Daniel Stenberg
committed
void Curl_ssl_free_certinfo(struct SessionHandle *data)
{
int i;
struct curl_certinfo *ci = &data->info.certs;
if(ci->num_of_certs) {
/* free all individual lists used */
Daniel Stenberg
committed
curl_slist_free_all(ci->certinfo[i]);
Daniel Stenberg
committed
free(ci->certinfo); /* free the actual array too */
Daniel Stenberg
committed
ci->num_of_certs = 0;
}
}
#ifndef USE_WINDOWS_SSPI
/* these functions are not used when SSPI is used for NTLM */
void Curl_ssl_random(struct SessionHandle *data,
unsigned char *entropy,
size_t length)
{
curlssl_random(data, entropy, length);
}
void Curl_ssl_md5sum(unsigned char *tmp, /* input */
size_t tmplen,
unsigned char *md5sum, /* output */
size_t md5len)
{
curlssl_md5sum(tmp, tmplen, md5sum, md5len);
}
#endif /* USE_WINDOWS_SSPI */
#endif /* USE_SSL */