From 3c52c53dddd3193d20bcb6fb335b1813eb1c07dd Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Tue, 28 Aug 2001 08:37:54 +0000 Subject: [PATCH] Added SSL session ID caching, moved some SSL code from url.c to ssluse.c --- lib/ssluse.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++ lib/ssluse.h | 14 ++-- lib/transfer.c | 8 +++ lib/url.c | 34 +++------ lib/urldata.h | 11 +++ 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; (issl.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 @@ -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 */ }; /****************************************************************************