mirror of
https://github.com/moparisthebest/curl
synced 2024-12-22 08:08:50 -05:00
Added SSL session ID caching, moved some SSL code from url.c to ssluse.c
This commit is contained in:
parent
321ba15a82
commit
3c52c53ddd
186
lib/ssluse.c
186
lib/ssluse.c
@ -278,10 +278,43 @@ void Curl_SSL_init(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is called when an SSL connection is closed.
|
||||||
|
*/
|
||||||
|
void Curl_SSL_Close(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
if (conn->ssl.use) {
|
||||||
|
/*
|
||||||
|
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);
|
||||||
|
|
||||||
|
if(conn->ssl.handle) {
|
||||||
|
(void)SSL_shutdown(conn->ssl.handle);
|
||||||
|
SSL_set_connect_state(conn->ssl.handle);
|
||||||
|
|
||||||
|
SSL_free (conn->ssl.handle);
|
||||||
|
conn->ssl.handle = NULL;
|
||||||
|
}
|
||||||
|
if(conn->ssl.ctx) {
|
||||||
|
SSL_CTX_free (conn->ssl.ctx);
|
||||||
|
conn->ssl.ctx = NULL;
|
||||||
|
}
|
||||||
|
conn->ssl.use = FALSE; /* get back to ordinary socket usage */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Global cleanup */
|
/* Global cleanup */
|
||||||
void Curl_SSL_cleanup(void)
|
void Curl_SSL_cleanup(void)
|
||||||
{
|
{
|
||||||
#ifdef USE_SSLEAY
|
#ifdef USE_SSLEAY
|
||||||
|
|
||||||
if(init_ssl) {
|
if(init_ssl) {
|
||||||
/* only cleanup if we did a previous init */
|
/* only cleanup if we did a previous init */
|
||||||
|
|
||||||
@ -295,6 +328,140 @@ void Curl_SSL_cleanup(void)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This sets up a session cache to the specified size.
|
||||||
|
*/
|
||||||
|
CURLcode Curl_SSL_InitSessions(struct UrlData *data, long amount)
|
||||||
|
{
|
||||||
|
struct curl_ssl_session *session;
|
||||||
|
|
||||||
|
if(data->ssl.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->ssl.numsessions = amount;
|
||||||
|
data->ssl.session = session;
|
||||||
|
data->ssl.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 UrlData *data = conn->data;
|
||||||
|
long i;
|
||||||
|
|
||||||
|
for(i=0; i< data->ssl.numsessions; i++) {
|
||||||
|
check = &data->ssl.session[i];
|
||||||
|
if(!check->sessionid)
|
||||||
|
/* not session ID means blank entry */
|
||||||
|
continue;
|
||||||
|
if(strequal(conn->name, check->name)) {
|
||||||
|
/* yes, we have a session ID! */
|
||||||
|
data->ssl.sessionage++; /* increase general age */
|
||||||
|
check->age = data->ssl.sessionage; /* set this as used in this age */
|
||||||
|
*ssl_sessionid = check->sessionid;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*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)
|
||||||
|
{
|
||||||
|
if(session->sessionid) {
|
||||||
|
/* defensive check */
|
||||||
|
|
||||||
|
/* free the ID */
|
||||||
|
SSL_SESSION_free(session->sessionid);
|
||||||
|
session->sessionid=NULL;
|
||||||
|
session->age = 0; /* fresh */
|
||||||
|
free(session->name);
|
||||||
|
session->name = NULL; /* no name */
|
||||||
|
|
||||||
|
return 0; /* ok */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function is called when the 'data' struct is going away. Close
|
||||||
|
* down everything and free all resources!
|
||||||
|
*/
|
||||||
|
int Curl_SSL_Close_All(struct UrlData *data)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for(i=0; i< data->ssl.numsessions; i++)
|
||||||
|
/* the single-killer function handles empty table slots */
|
||||||
|
Kill_Single_Session(&data->ssl.session[i]);
|
||||||
|
|
||||||
|
/* free the cache data */
|
||||||
|
free(data->ssl.session);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the session id and store it in the session cache.
|
||||||
|
*/
|
||||||
|
static int Store_SSL_Session(struct connectdata *conn)
|
||||||
|
{
|
||||||
|
SSL_SESSION *ssl_sessionid;
|
||||||
|
struct curl_ssl_session *store;
|
||||||
|
int i;
|
||||||
|
struct UrlData *data=conn->data; /* the mother of all structs */
|
||||||
|
int oldest_age=data->ssl.session[0].age; /* zero if unused */
|
||||||
|
|
||||||
|
/* ask OpenSSL, say please */
|
||||||
|
ssl_sessionid = SSL_get1_session(conn->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. */
|
||||||
|
|
||||||
|
/* 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=0; (i<data->ssl.numsessions) && data->ssl.session[i].sessionid; i++) {
|
||||||
|
if(data->ssl.session[i].age < oldest_age) {
|
||||||
|
oldest_age = data->ssl.session[i].age;
|
||||||
|
store = &data->ssl.session[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i == data->ssl.numsessions)
|
||||||
|
/* cache is full, we must "kill" the oldest entry! */
|
||||||
|
Kill_Single_Session(store);
|
||||||
|
else
|
||||||
|
store = &data->ssl.session[i]; /* use this slot */
|
||||||
|
|
||||||
|
/* now init the session struct wisely */
|
||||||
|
store->sessionid = ssl_sessionid;
|
||||||
|
store->age = data->ssl.sessionage; /* set current age */
|
||||||
|
store->name = strdup(conn->name); /* clone host name */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ====================================================== */
|
/* ====================================================== */
|
||||||
CURLcode
|
CURLcode
|
||||||
@ -307,6 +474,7 @@ Curl_SSLConnect(struct connectdata *conn)
|
|||||||
int err;
|
int err;
|
||||||
char * str;
|
char * str;
|
||||||
SSL_METHOD *req_method;
|
SSL_METHOD *req_method;
|
||||||
|
SSL_SESSION *ssl_sessionid=NULL;
|
||||||
|
|
||||||
/* mark this is being ssl enabled from here on out. */
|
/* mark this is being ssl enabled from here on out. */
|
||||||
conn->ssl.use = TRUE;
|
conn->ssl.use = TRUE;
|
||||||
@ -362,6 +530,17 @@ Curl_SSLConnect(struct connectdata *conn)
|
|||||||
|
|
||||||
conn->ssl.server_cert = 0x0;
|
conn->ssl.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! */
|
||||||
|
SSL_set_session(conn->ssl.handle, ssl_sessionid);
|
||||||
|
/* Informational message */
|
||||||
|
infof (data, "SSL re-using session ID\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* pass the raw socket into the SSL layers */
|
/* pass the raw socket into the SSL layers */
|
||||||
SSL_set_fd (conn->ssl.handle, conn->firstsocket);
|
SSL_set_fd (conn->ssl.handle, conn->firstsocket);
|
||||||
err = SSL_connect (conn->ssl.handle);
|
err = SSL_connect (conn->ssl.handle);
|
||||||
@ -376,6 +555,13 @@ Curl_SSLConnect(struct connectdata *conn)
|
|||||||
infof (data, "SSL connection using %s\n",
|
infof (data, "SSL connection using %s\n",
|
||||||
SSL_get_cipher(conn->ssl.handle));
|
SSL_get_cipher(conn->ssl.handle));
|
||||||
|
|
||||||
|
if(!ssl_sessionid) {
|
||||||
|
/* Since this is not a cached session ID, then we want to stach this one
|
||||||
|
in the cache! */
|
||||||
|
Store_SSL_Session(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Get server's certificate (note: beware of dynamic allocation) - opt */
|
/* Get server's certificate (note: beware of dynamic allocation) - opt */
|
||||||
/* major serious hack alert -- we should check certificates
|
/* major serious hack alert -- we should check certificates
|
||||||
* to authenticate the server; otherwise we risk man-in-the-middle
|
* to authenticate the server; otherwise we risk man-in-the-middle
|
||||||
|
14
lib/ssluse.h
14
lib/ssluse.h
@ -24,8 +24,14 @@
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
#include "urldata.h"
|
#include "urldata.h"
|
||||||
CURLcode Curl_SSLConnect(struct connectdata *conn);
|
CURLcode Curl_SSLConnect(struct connectdata *conn);
|
||||||
/* Global SSL init */
|
void Curl_SSL_init(void); /* Global SSL init */
|
||||||
void Curl_SSL_init(void);
|
void Curl_SSL_cleanup(void); /* Global SSL cleanup */
|
||||||
/* Global SSL cleanup */
|
|
||||||
void Curl_SSL_cleanup(void);
|
/* init the SSL session ID cache */
|
||||||
|
CURLcode Curl_SSL_InitSessions(struct UrlData *, 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 UrlData *data);
|
||||||
#endif
|
#endif
|
||||||
|
@ -92,6 +92,7 @@
|
|||||||
#include "http.h"
|
#include "http.h"
|
||||||
#include "url.h"
|
#include "url.h"
|
||||||
#include "getinfo.h"
|
#include "getinfo.h"
|
||||||
|
#include "ssluse.h"
|
||||||
|
|
||||||
#define _MPRINTF_REPLACE /* use our functions only */
|
#define _MPRINTF_REPLACE /* use our functions only */
|
||||||
#include <curl/mprintf.h>
|
#include <curl/mprintf.h>
|
||||||
@ -896,6 +897,13 @@ CURLcode Curl_perform(struct UrlData *data)
|
|||||||
/* we can't do anything wihout URL */
|
/* we can't do anything wihout URL */
|
||||||
return CURLE_URL_MALFORMAT;
|
return CURLE_URL_MALFORMAT;
|
||||||
|
|
||||||
|
#ifdef USE_SSLEAY
|
||||||
|
/* Init the SSL session ID cache here. We do it here since we want to
|
||||||
|
do it after the *_setopt() calls (that could change the size) but
|
||||||
|
before any transfer. */
|
||||||
|
Curl_SSL_InitSessions(data, data->ssl.numsessions);
|
||||||
|
#endif
|
||||||
|
|
||||||
data->followlocation=0; /* reset the location-follow counter */
|
data->followlocation=0; /* reset the location-follow counter */
|
||||||
data->bits.this_is_a_follow = FALSE; /* reset this */
|
data->bits.this_is_a_follow = FALSE; /* reset this */
|
||||||
|
|
||||||
|
34
lib/url.c
34
lib/url.c
@ -148,6 +148,11 @@ CURLcode Curl_close(struct UrlData *data)
|
|||||||
/* Loop through all open connections and kill them one by one */
|
/* Loop through all open connections and kill them one by one */
|
||||||
while(-1 != ConnectionKillOne(data));
|
while(-1 != ConnectionKillOne(data));
|
||||||
|
|
||||||
|
#ifdef USE_SSLEAY
|
||||||
|
/* Close down all open info open SSL and sessions */
|
||||||
|
Curl_SSL_Close_All(data);
|
||||||
|
#endif
|
||||||
|
|
||||||
if(data->bits.proxystringalloc) {
|
if(data->bits.proxystringalloc) {
|
||||||
data->bits.proxystringalloc=FALSE;;
|
data->bits.proxystringalloc=FALSE;;
|
||||||
free(data->proxy);
|
free(data->proxy);
|
||||||
@ -242,6 +247,9 @@ CURLcode Curl_open(struct UrlData **curl)
|
|||||||
data->bits.hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
|
data->bits.hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
|
||||||
data->progress.flags |= PGRS_HIDE;
|
data->progress.flags |= PGRS_HIDE;
|
||||||
|
|
||||||
|
/* Set the default size of the SSL session ID cache */
|
||||||
|
data->ssl.numsessions = 5;
|
||||||
|
|
||||||
/* create an array with connection data struct pointers */
|
/* create an array with connection data struct pointers */
|
||||||
data->numconnects = 5; /* hard-coded right now */
|
data->numconnects = 5; /* hard-coded right now */
|
||||||
data->connects = (struct connectdata **)
|
data->connects = (struct connectdata **)
|
||||||
@ -875,31 +883,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
|
|||||||
free(conn->path);
|
free(conn->path);
|
||||||
|
|
||||||
#ifdef USE_SSLEAY
|
#ifdef USE_SSLEAY
|
||||||
if (conn->ssl.use) {
|
Curl_SSL_Close(conn);
|
||||||
/*
|
|
||||||
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);
|
|
||||||
|
|
||||||
if(conn->ssl.handle) {
|
|
||||||
(void)SSL_shutdown(conn->ssl.handle);
|
|
||||||
SSL_set_connect_state(conn->ssl.handle);
|
|
||||||
|
|
||||||
SSL_free (conn->ssl.handle);
|
|
||||||
conn->ssl.handle = NULL;
|
|
||||||
}
|
|
||||||
if(conn->ssl.ctx) {
|
|
||||||
SSL_CTX_free (conn->ssl.ctx);
|
|
||||||
conn->ssl.ctx = NULL;
|
|
||||||
}
|
|
||||||
conn->ssl.use = FALSE; /* get back to ordinary socket usage */
|
|
||||||
}
|
|
||||||
#endif /* USE_SSLEAY */
|
#endif /* USE_SSLEAY */
|
||||||
|
|
||||||
/* close possibly still open sockets */
|
/* close possibly still open sockets */
|
||||||
|
@ -125,6 +125,13 @@ struct ssl_connect_data {
|
|||||||
#endif /* USE_SSLEAY */
|
#endif /* USE_SSLEAY */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* information about one single SSL session */
|
||||||
|
struct curl_ssl_session {
|
||||||
|
char *name; /* host name for which this ID was used */
|
||||||
|
void *sessionid; /* as returned from the SSL layer */
|
||||||
|
long age; /* just a number, the higher the more recent */
|
||||||
|
};
|
||||||
|
|
||||||
struct ssl_config_data {
|
struct ssl_config_data {
|
||||||
long version; /* what version the client wants to use */
|
long version; /* what version the client wants to use */
|
||||||
long certverifyresult; /* result from the certificate verification */
|
long certverifyresult; /* result from the certificate verification */
|
||||||
@ -134,6 +141,10 @@ struct ssl_config_data {
|
|||||||
char *CAfile; /* cerficate to verify peer against */
|
char *CAfile; /* cerficate to verify peer against */
|
||||||
char *random_file; /* path to file containing "random" data */
|
char *random_file; /* path to file containing "random" data */
|
||||||
char *egdsocket; /* path to file containing the EGD daemon socket */
|
char *egdsocket; /* path to file containing the EGD daemon socket */
|
||||||
|
|
||||||
|
struct curl_ssl_session *session; /* array of 'numsessions' size */
|
||||||
|
long numsessions; /* SSL session id cache size */
|
||||||
|
long sessionage; /* number of the most recent session */
|
||||||
};
|
};
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
|
Loading…
Reference in New Issue
Block a user