aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/ssluse.c186
-rw-r--r--lib/ssluse.h14
-rw-r--r--lib/transfer.c8
-rw-r--r--lib/url.c34
-rw-r--r--lib/urldata.h11
5 files changed, 224 insertions, 29 deletions
diff --git a/lib/ssluse.c b/lib/ssluse.c
index eb7e485ac..b8d91d59d 100644
--- a/lib/ssluse.c
+++ b/lib/ssluse.c
@@ -278,10 +278,43 @@ void Curl_SSL_init(void)
#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 */
void Curl_SSL_cleanup(void)
{
#ifdef USE_SSLEAY
+
if(init_ssl) {
/* only cleanup if we did a previous init */
@@ -295,6 +328,140 @@ void Curl_SSL_cleanup(void)
#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
@@ -307,6 +474,7 @@ Curl_SSLConnect(struct connectdata *conn)
int err;
char * str;
SSL_METHOD *req_method;
+ SSL_SESSION *ssl_sessionid=NULL;
/* mark this is being ssl enabled from here on out. */
conn->ssl.use = TRUE;
@@ -362,6 +530,17 @@ Curl_SSLConnect(struct connectdata *conn)
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 */
SSL_set_fd (conn->ssl.handle, conn->firstsocket);
err = SSL_connect (conn->ssl.handle);
@@ -375,6 +554,13 @@ Curl_SSLConnect(struct connectdata *conn)
/* Informational message */
infof (data, "SSL connection using %s\n",
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 */
/* major serious hack alert -- we should check certificates
diff --git a/lib/ssluse.h b/lib/ssluse.h
index 645970f4b..47a1a1842 100644
--- a/lib/ssluse.h
+++ b/lib/ssluse.h
@@ -24,8 +24,14 @@
*****************************************************************************/
#include "urldata.h"
CURLcode Curl_SSLConnect(struct connectdata *conn);
-/* Global SSL init */
-void Curl_SSL_init(void);
-/* Global SSL cleanup */
-void Curl_SSL_cleanup(void);
+void 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 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
diff --git a/lib/transfer.c b/lib/transfer.c
index b4ea0b23f..ab0af9f42 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -92,6 +92,7 @@
#include "http.h"
#include "url.h"
#include "getinfo.h"
+#include "ssluse.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@@ -896,6 +897,13 @@ CURLcode Curl_perform(struct UrlData *data)
/* 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) but
+ before any transfer. */
+ Curl_SSL_InitSessions(data, data->ssl.numsessions);
+#endif
+
data->followlocation=0; /* reset the location-follow counter */
data->bits.this_is_a_follow = FALSE; /* reset this */
diff --git a/lib/url.c b/lib/url.c
index 66eedf473..294a5db9b 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -148,6 +148,11 @@ CURLcode Curl_close(struct UrlData *data)
/* Loop through all open connections and kill them one by one */
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) {
data->bits.proxystringalloc=FALSE;;
free(data->proxy);
@@ -242,6 +247,9 @@ CURLcode Curl_open(struct UrlData **curl)
data->bits.hide_progress = TRUE; /* CURLOPT_NOPROGRESS changes these */
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 */
data->numconnects = 5; /* hard-coded right now */
data->connects = (struct connectdata **)
@@ -875,31 +883,7 @@ CURLcode Curl_disconnect(struct connectdata *conn)
free(conn->path);
#ifdef USE_SSLEAY
- 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 */
- }
+ Curl_SSL_Close(conn);
#endif /* USE_SSLEAY */
/* close possibly still open sockets */
diff --git a/lib/urldata.h b/lib/urldata.h
index 342e143df..d0e54c7ba 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -125,6 +125,13 @@ struct ssl_connect_data {
#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 {
long version; /* what version the client wants to use */
long certverifyresult; /* result from the certificate verification */
@@ -134,6 +141,10 @@ struct ssl_config_data {
char *CAfile; /* cerficate to verify peer against */
char *random_file; /* path to file containing "random" data */
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 */
};
/****************************************************************************