aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ssl/ssl.h29
-rw-r--r--ssl/ssl_err.c3
-rw-r--r--ssl/ssl_lib.c42
-rw-r--r--ssl/ssl_sess.c91
-rwxr-xr-xutil/ssleay.num3
5 files changed, 150 insertions, 18 deletions
diff --git a/ssl/ssl.h b/ssl/ssl.h
index de87c2e6c4..e80ed98696 100644
--- a/ssl/ssl.h
+++ b/ssl/ssl.h
@@ -391,6 +391,22 @@ typedef struct ssl_session_st
#define SSL_SESSION_CACHE_MAX_SIZE_DEFAULT (1024*20)
+/* This callback type is used inside SSL_CTX, SSL, and in the functions that set
+ * them. It is used to override the generation of SSL/TLS session IDs in a
+ * server. Return value should be zero on an error, non-zero to proceed. Also,
+ * callbacks should themselves check if the id they generate is unique otherwise
+ * the SSL handshake will fail with an error - callbacks can do this using the
+ * 'ssl' value they're passed by;
+ * SSL_CTX_has_matching_session_id(ssl->ctx, id, *id_len)
+ * The length value passed in is set at the maximum size the session ID can be.
+ * In SSLv2 this is 16 bytes, whereas SSLv3/TLSv1 it is 32 bytes. The callback
+ * can alter this length to be less if desired, but under SSLv2 session IDs are
+ * supposed to be fixed at 16 bytes so the id will be padded after the callback
+ * returns in this case. It is also an error for the callback to set the size to
+ * zero. */
+typedef int (*GEN_SESSION_CB)(const SSL *ssl, unsigned char *id,
+ unsigned int *id_len);
+
typedef struct ssl_comp_st
{
int id;
@@ -486,6 +502,9 @@ struct ssl_ctx_st
int purpose; /* Purpose setting */
int trust; /* Trust setting */
+ /* Default generate session ID callback. */
+ GEN_SESSION_CB generate_session_id;
+
/* Default password callback. */
/**/ pem_password_cb *default_passwd_callback;
@@ -674,6 +693,9 @@ struct ssl_st
/* This can also be in the session once a session is established */
SSL_SESSION *session;
+ /* Default generate session ID callback. */
+ GEN_SESSION_CB generate_session_id;
+
/* Used in SSL2 and SSL3 */
int verify_mode; /* 0 don't care about verify failure.
* 1 fail if verify fails */
@@ -1029,6 +1051,10 @@ int i2d_SSL_SESSION(SSL_SESSION *in,unsigned char **pp);
int SSL_set_session(SSL *to, SSL_SESSION *session);
int SSL_CTX_add_session(SSL_CTX *s, SSL_SESSION *c);
int SSL_CTX_remove_session(SSL_CTX *,SSL_SESSION *c);
+int SSL_CTX_set_generate_session_id(SSL_CTX *, GEN_SESSION_CB);
+int SSL_set_generate_session_id(SSL *, GEN_SESSION_CB);
+int SSL_CTX_has_matching_session_id(const SSL_CTX *ctx, const unsigned char *id,
+ unsigned int id_len);
SSL_SESSION *d2i_SSL_SESSION(SSL_SESSION **a,unsigned char **pp,long length);
#ifdef HEADER_X509_H
@@ -1524,6 +1550,9 @@ int SSL_COMP_add_compression_method(int id,char *cm);
#define SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION 228
#define SSL_R_SSL_HANDSHAKE_FAILURE 229
#define SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS 230
+#define SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH 1101
+#define SSL_R_SSL_SESSION_ID_CALLBACK_FAILED 1102
+#define SSL_R_SSL_SESSION_ID_CONFLICT 1103
#define SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG 273
#define SSL_R_SSL_SESSION_ID_IS_DIFFERENT 231
#define SSL_R_TLSV1_ALERT_ACCESS_DENIED 1049
diff --git a/ssl/ssl_err.c b/ssl/ssl_err.c
index 4a46099002..133b6193cf 100644
--- a/ssl/ssl_err.c
+++ b/ssl/ssl_err.c
@@ -366,6 +366,9 @@ static ERR_STRING_DATA SSL_str_reasons[]=
{SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION,"ssl ctx has no default ssl version"},
{SSL_R_SSL_HANDSHAKE_FAILURE ,"ssl handshake failure"},
{SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS ,"ssl library has no ciphers"},
+{SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH ,"ssl session id has bad length"},
+{SSL_R_SSL_SESSION_ID_CALLBACK_FAILED ,"ssl session id callback failed"},
+{SSL_R_SSL_SESSION_ID_CONFLICT ,"ssl session id conflict"},
{SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG ,"ssl session id context too long"},
{SSL_R_SSL_SESSION_ID_IS_DIFFERENT ,"ssl session id is different"},
{SSL_R_TLSV1_ALERT_ACCESS_DENIED ,"tlsv1 alert access denied"},
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 21e4d6e921..7864f2f7b0 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -218,6 +218,7 @@ SSL *SSL_new(SSL_CTX *ctx)
s->verify_mode=ctx->verify_mode;
s->verify_depth=ctx->verify_depth;
s->verify_callback=ctx->default_verify_callback;
+ s->generate_session_id=ctx->generate_session_id;
s->purpose = ctx->purpose;
s->trust = ctx->trust;
CRYPTO_add(&ctx->references,1,CRYPTO_LOCK_SSL_CTX);
@@ -282,6 +283,41 @@ int SSL_set_session_id_context(SSL *ssl,const unsigned char *sid_ctx,
return 1;
}
+int SSL_CTX_set_generate_session_id(SSL_CTX *ctx, GEN_SESSION_CB cb)
+ {
+ CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX);
+ ctx->generate_session_id = cb;
+ CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX);
+ return 1;
+ }
+
+int SSL_set_generate_session_id(SSL *ssl, GEN_SESSION_CB cb)
+ {
+ CRYPTO_w_lock(CRYPTO_LOCK_SSL);
+ ssl->generate_session_id = cb;
+ CRYPTO_w_unlock(CRYPTO_LOCK_SSL);
+ return 1;
+ }
+
+int SSL_CTX_has_matching_session_id(const SSL_CTX *ctx, const unsigned char *id,
+ unsigned int id_len)
+ {
+ /* A quick examination of SSL_SESSION_hash and SSL_SESSION_cmp shows how
+ * we can "construct" a session to give us the desired check - ie. to
+ * find if there's a session in the hash table that would conflict with
+ * any new session built out of this id/id_len and the ssl_version in
+ * use by this SSL_CTX. */
+ SSL_SESSION r, *p;
+ r.ssl_version = ctx->method->version;
+ r.session_id_length = id_len;
+ memcpy(r.session_id, id, id_len);
+
+ CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
+ p = (SSL_SESSION *)lh_retrieve(ctx->sessions, &r);
+ CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
+ return (p != NULL);
+ }
+
int SSL_CTX_set_purpose(SSL_CTX *s, int purpose)
{
if(X509_PURPOSE_get_by_id(purpose) == -1) {
@@ -1092,6 +1128,11 @@ unsigned long SSL_SESSION_hash(SSL_SESSION *a)
return(l);
}
+/* NB: If this function (or indeed the hash function which uses a sort of
+ * coarser function than this one) is changed, ensure
+ * SSL_CTX_has_matching_session_id() is checked accordingly. It relies on being
+ * able to construct an SSL_SESSION that will collide with any existing session
+ * with a matching session ID. */
int SSL_SESSION_cmp(SSL_SESSION *a,SSL_SESSION *b)
{
if (a->ssl_version != b->ssl_version)
@@ -1143,6 +1184,7 @@ SSL_CTX *SSL_CTX_new(SSL_METHOD *meth)
ret->new_session_cb=NULL;
ret->remove_session_cb=NULL;
ret->get_session_cb=NULL;
+ ret->generate_session_id=NULL;
memset((char *)&ret->stats,0,sizeof(ret->stats));
diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c
index 9364612e40..a5270ce502 100644
--- a/ssl/ssl_sess.c
+++ b/ssl/ssl_sess.c
@@ -130,11 +130,45 @@ SSL_SESSION *SSL_SESSION_new(void)
return(ss);
}
+/* Even with SSLv2, we have 16 bytes (128 bits) of session ID space. SSLv3/TLSv1
+ * has 32 bytes (256 bits). As such, filling the ID with random gunk repeatedly
+ * until we have no conflict is going to complete in one iteration pretty much
+ * "most" of the time (btw: understatement). So, if it takes us 10 iterations
+ * and we still can't avoid a conflict - well that's a reasonable point to call
+ * it quits. Either the RAND code is broken or someone is trying to open roughly
+ * very close to 2^128 (or 2^256) SSL sessions to our server. How you might
+ * store that many sessions is perhaps a more interesting question ... */
+
+#define MAX_SESS_ID_ATTEMPTS 10
+static int def_generate_session_id(const SSL *ssl, unsigned char *id,
+ unsigned int *id_len)
+{
+ unsigned int retry = 0;
+ do
+ RAND_pseudo_bytes(id, *id_len);
+ while(SSL_CTX_has_matching_session_id(ssl->ctx, id, *id_len) &&
+ (++retry < MAX_SESS_ID_ATTEMPTS));
+ if(retry < MAX_SESS_ID_ATTEMPTS)
+ return 1;
+ /* else - woops a session_id match */
+ /* XXX We should also check the external cache --
+ * but the probability of a collision is negligible, and
+ * we could not prevent the concurrent creation of sessions
+ * with identical IDs since we currently don't have means
+ * to atomically check whether a session ID already exists
+ * and make a reservation for it if it does not
+ * (this problem applies to the internal cache as well).
+ */
+ return 0;
+}
+
int ssl_get_new_session(SSL *s, int session)
{
/* This gets used by clients and servers. */
+ unsigned int tmp;
SSL_SESSION *ss=NULL;
+ GEN_SESSION_CB cb = def_generate_session_id;
if ((ss=SSL_SESSION_new()) == NULL) return(0);
@@ -173,25 +207,46 @@ int ssl_get_new_session(SSL *s, int session)
SSL_SESSION_free(ss);
return(0);
}
-
- for (;;)
+ /* Choose which callback will set the session ID */
+ CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
+ if(s->generate_session_id)
+ cb = s->generate_session_id;
+ else if(s->ctx->generate_session_id)
+ cb = s->ctx->generate_session_id;
+ CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
+ /* Choose a session ID */
+ tmp = ss->session_id_length;
+ if(!cb(s, ss->session_id, &tmp))
{
- SSL_SESSION *r;
-
- RAND_pseudo_bytes(ss->session_id,ss->session_id_length);
- CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX);
- r=(SSL_SESSION *)lh_retrieve(s->ctx->sessions, ss);
- CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX);
- if (r == NULL) break;
- /* else - woops a session_id match */
- /* XXX We should also check the external cache --
- * but the probability of a collision is negligible, and
- * we could not prevent the concurrent creation of sessions
- * with identical IDs since we currently don't have means
- * to atomically check whether a session ID already exists
- * and make a reservation for it if it does not
- * (this problem applies to the internal cache as well).
- */
+ /* The callback failed */
+ SSLerr(SSL_F_SSL_GET_NEW_SESSION,
+ SSL_R_SSL_SESSION_ID_CALLBACK_FAILED);
+ SSL_SESSION_free(ss);
+ return(0);
+ }
+ /* Don't allow the callback to set the session length to zero.
+ * nor set it higher than it was. */
+ if(!tmp || (tmp > ss->session_id_length))
+ {
+ /* The callback set an illegal length */
+ SSLerr(SSL_F_SSL_GET_NEW_SESSION,
+ SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH);
+ SSL_SESSION_free(ss);
+ return(0);
+ }
+ /* If the session length was shrunk and we're SSLv2, pad it */
+ if((tmp < ss->session_id_length) && (s->version == SSL2_VERSION))
+ memset(ss->session_id + tmp, 0, ss->session_id_length - tmp);
+ else
+ ss->session_id_length = tmp;
+ /* Finally, check for a conflict */
+ if(SSL_CTX_has_matching_session_id(s->ctx, ss->session_id,
+ ss->session_id_length))
+ {
+ SSLerr(SSL_F_SSL_GET_NEW_SESSION,
+ SSL_R_SSL_SESSION_ID_CONFLICT);
+ SSL_SESSION_free(ss);
+ return(0);
}
}
else
diff --git a/util/ssleay.num b/util/ssleay.num
index 26d38fb48c..840c92747d 100755
--- a/util/ssleay.num
+++ b/util/ssleay.num
@@ -208,3 +208,6 @@ kssl_ctx_free 257 EXIST::FUNCTION:KRB5
kssl_krb5_free_data_contents 258 EXIST::FUNCTION:KRB5
print_krb5_data 259 EXIST::FUNCTION:KRB5
kssl_ctx_setstring 260 EXIST::FUNCTION:KRB5
+SSL_CTX_has_matching_session_id 261 EXIST::FUNCTION:
+SSL_set_generate_session_id 262 EXIST::FUNCTION:
+SSL_CTX_set_generate_session_id 263 EXIST::FUNCTION: