X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=ssl%2Fssl_sess.c;h=74ec2178a936a0cadcf62cb6e7ddbc6aababd022;hb=bb7cd4e3ebb9c1114b7fb6f6d0e4c280ef984889;hp=af65c65dac94a7a608b9cc16bdb2776b0682604d;hpb=d02b48c63a58ea4367a0e905979f140b7d090f86;p=oweals%2Fopenssl.git diff --git a/ssl/ssl_sess.c b/ssl/ssl_sess.c index af65c65dac..74ec2178a9 100644 --- a/ssl/ssl_sess.c +++ b/ssl/ssl_sess.c @@ -1,5 +1,5 @@ /* ssl/ssl_sess.c */ -/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com) +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) * All rights reserved. * * This package is an SSL implementation written @@ -57,11 +57,63 @@ */ #include -#include "lhash.h" -#include "rand.h" +#include +#include #include "ssl_locl.h" -SSL_SESSION *SSL_SESSION_new() +static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s); +static void SSL_SESSION_list_add(SSL_CTX *ctx,SSL_SESSION *s); +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck); +static int ssl_session_num=0; +static STACK *ssl_session_meth=NULL; + +#if 1 /* traditional SSLeay behaviour */ +SSL_SESSION *SSL_get_session(SSL *ssl) + { + return(ssl->session); + } +#else /* suggested change: increase reference counter so that a session + * can later be set in a new SSL object. + * Objections: + * -- the modified function should have a new name (or old + * applications, including s_client, leak memory); + * -- the locking seems unnecessary given that SSL structures + * usually cannot be safely shared between threads anyway. */ +SSL_SESSION *SSL_get_session(SSL *ssl) + { + SSL_SESSION *sess; + /* Need to lock this all up rather than just use CRYPTO_add so that + * somebody doesn't free ssl->session between when we check it's + * non-null and when we up the reference count. */ + CRYPTO_r_lock(CRYPTO_LOCK_SSL_SESSION); + sess = ssl->session; + if(sess) + sess->references++; + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_SESSION); + return(sess); + } +#endif + +int SSL_SESSION_get_ex_new_index(long argl, char *argp, int (*new_func)(), + int (*dup_func)(), void (*free_func)()) + { + ssl_session_num++; + return(CRYPTO_get_ex_new_index(ssl_session_num-1, + &ssl_session_meth, + argl,argp,new_func,dup_func,free_func)); + } + +int SSL_SESSION_set_ex_data(SSL_SESSION *s, int idx, void *arg) + { + return(CRYPTO_set_ex_data(&s->ex_data,idx,arg)); + } + +void *SSL_SESSION_get_ex_data(SSL_SESSION *s, int idx) + { + return(CRYPTO_get_ex_data(&s->ex_data,idx)); + } + +SSL_SESSION *SSL_SESSION_new(void) { SSL_SESSION *ss; @@ -73,23 +125,30 @@ SSL_SESSION *SSL_SESSION_new() } memset(ss,0,sizeof(SSL_SESSION)); + ss->verify_result = 1; /* avoid 0 (= X509_V_OK) just in case */ ss->references=1; ss->timeout=60*5+4; /* 5 minute timeout by default */ ss->time=time(NULL); + ss->prev=NULL; + ss->next=NULL; + ss->compress_meth=0; + CRYPTO_new_ex_data(ssl_session_meth,(char *)ss,&ss->ex_data); return(ss); } -int ssl_get_new_session(s, session) -SSL *s; -int session; +int ssl_get_new_session(SSL *s, int session) { + /* This gets used by clients and servers. */ + SSL_SESSION *ss=NULL; if ((ss=SSL_SESSION_new()) == NULL) return(0); /* If the context has a default timeout, use it */ - if (s->ctx->session_timeout != 0) + if (s->ctx->session_timeout == 0) ss->timeout=SSL_get_default_timeout(s); + else + ss->timeout=s->ctx->session_timeout; if (s->session != NULL) { @@ -99,14 +158,19 @@ int session; if (session) { - if (s->version == SSL2_CLIENT_VERSION) + if (s->version == SSL2_VERSION) { - ss->ssl_version=2; + ss->ssl_version=SSL2_VERSION; ss->session_id_length=SSL2_SSL_SESSION_ID_LENGTH; } - else if (s->version == SSL3_VERSION_MAJOR) + else if (s->version == SSL3_VERSION) + { + ss->ssl_version=SSL3_VERSION; + ss->session_id_length=SSL3_SSL_SESSION_ID_LENGTH; + } + else if (s->version == TLS1_VERSION) { - ss->ssl_version=3; + ss->ssl_version=TLS1_VERSION; ss->session_id_length=SSL3_SSL_SESSION_ID_LENGTH; } else @@ -127,6 +191,8 @@ int session; CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); if (r == NULL) break; /* else - woops a session_id match */ + /* XXX should also check external cache! + * (But the probability of a collision is negligible, anyway...) */ } } else @@ -134,84 +200,133 @@ int session; ss->session_id_length=0; } + memcpy(ss->sid_ctx,s->sid_ctx,s->sid_ctx_length); + ss->sid_ctx_length=s->sid_ctx_length; s->session=ss; ss->ssl_version=s->version; + ss->verify_result = X509_V_OK; return(1); } -int ssl_get_prev_session(s, len, session) -SSL *s; -int len; -unsigned char *session; +int ssl_get_prev_session(SSL *s, unsigned char *session_id, int len) { - SSL_SESSION *ret,data; + /* This is used only by servers. */ + + SSL_SESSION *ret=NULL,data; + int fatal = 0; /* conn_init();*/ data.ssl_version=s->version; data.session_id_length=len; if (len > SSL_MAX_SSL_SESSION_ID_LENGTH) - return(0); - memcpy(data.session_id,session,len);; + goto err; + memcpy(data.session_id,session_id,len); - CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); - ret=(SSL_SESSION *)lh_retrieve(s->ctx->sessions,(char *)&data); - CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + if (!(s->ctx->session_cache_mode & SSL_SESS_CACHE_NO_INTERNAL_LOOKUP)) + { + CRYPTO_r_lock(CRYPTO_LOCK_SSL_CTX); + ret=(SSL_SESSION *)lh_retrieve(s->ctx->sessions,(char *)&data); + if (ret != NULL) + /* don't allow other threads to steal it: */ + CRYPTO_add(&ret->references,1,CRYPTO_LOCK_SSL_SESSION); + CRYPTO_r_unlock(CRYPTO_LOCK_SSL_CTX); + } if (ret == NULL) { int copy=1; - - s->ctx->sess_miss++; + + s->ctx->stats.sess_miss++; ret=NULL; - if ((s->ctx->get_session_cb != NULL) && - ((ret=s->ctx->get_session_cb(s,session,len,©)) - != NULL)) + if (s->ctx->get_session_cb != NULL + && (ret=s->ctx->get_session_cb(s,session_id,len,©)) + != NULL) { - s->ctx->sess_cb_hit++; + s->ctx->stats.sess_cb_hit++; + + /* Increment reference count now if the session callback + * asks us to do so (note that if the session structures + * returned by the callback are shared between threads, + * it must handle the reference count itself [i.e. copy == 0], + * or things won't be thread-safe). */ + if (copy) + CRYPTO_add(&ret->references,1,CRYPTO_LOCK_SSL_SESSION); /* The following should not return 1, otherwise, * things are very strange */ SSL_CTX_add_session(s->ctx,ret); - /* auto free it */ - if (!copy) - SSL_SESSION_free(ret); } - if (ret == NULL) return(0); + if (ret == NULL) + goto err; + } + + /* Now ret is non-NULL, and we own one of its reference counts. */ + + if((s->verify_mode&SSL_VERIFY_PEER) + && (!s->sid_ctx_length || ret->sid_ctx_length != s->sid_ctx_length + || memcmp(ret->sid_ctx,s->sid_ctx,ret->sid_ctx_length))) + { + /* We've found the session named by the client, but we don't + * want to use it in this context. */ + + if (s->sid_ctx_length == 0) + { + /* application should have used SSL[_CTX]_set_session_id_context + * -- we could tolerate this and just pretend we never heard + * of this session, but then applications could effectively + * disable the session cache by accident without anyone noticing */ + + SSLerr(SSL_F_SSL_GET_PREV_SESSION,SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED); + fatal = 1; + goto err; + } + else + { +#if 0 /* The client cannot always know when a session is not appropriate, + * so we shouldn't generate an error message. */ + + SSLerr(SSL_F_SSL_GET_PREV_SESSION,SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT); +#endif + goto err; /* treat like cache miss */ + } } if (ret->cipher == NULL) { - char buf[5],*p; + unsigned char buf[5],*p; unsigned long l; p=buf; l=ret->cipher_id; l2n(l,p); - if (ret->ssl_version == 3) + if ((ret->ssl_version>>8) == SSL3_VERSION_MAJOR) ret->cipher=ssl_get_cipher_by_char(s,&(buf[2])); else ret->cipher=ssl_get_cipher_by_char(s,&(buf[1])); if (ret->cipher == NULL) - return(0); + goto err; } + +#if 0 /* This is way too late. */ + /* If a thread got the session, then 'swaped', and another got * it and then due to a time-out decided to 'Free' it we could * be in trouble. So I'll increment it now, then double decrement * later - am I speaking rubbish?. */ CRYPTO_add(&ret->references,1,CRYPTO_LOCK_SSL_SESSION); +#endif if ((long)(ret->time+ret->timeout) < (long)time(NULL)) /* timeout */ { - s->ctx->sess_timeout++; + s->ctx->stats.sess_timeout++; /* remove it from the cache */ SSL_CTX_remove_session(s->ctx,ret); - SSL_SESSION_free(ret); /* again to actually Free it */ - return(0); + goto err; } - s->ctx->sess_hit++; + s->ctx->stats.sess_hit++; /* ret->time=time(NULL); */ /* rezero timeout? */ /* again, just leave the session @@ -220,13 +335,21 @@ unsigned char *session; if (s->session != NULL) SSL_SESSION_free(s->session); s->session=ret; + s->verify_result = s->session->verify_result; return(1); + + err: + if (ret != NULL) + SSL_SESSION_free(ret); + if (fatal) + return -1; + else + return 0; } -int SSL_CTX_add_session(ctx,c) -SSL_CTX *ctx; -SSL_SESSION *c; +int SSL_CTX_add_session(SSL_CTX *ctx, SSL_SESSION *c) { + int ret=0; SSL_SESSION *s; /* conn_init(); */ @@ -234,7 +357,10 @@ SSL_SESSION *c; CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); s=(SSL_SESSION *)lh_insert(ctx->sessions,(char *)c); - CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + + /* Put on the end of the queue unless it is already in the cache */ + if (s == NULL) + SSL_SESSION_list_add(ctx,c); /* If the same session if is being 're-added', Free the old * one when the last person stops using it. @@ -243,32 +369,56 @@ SSL_SESSION *c; if (s != NULL) { SSL_SESSION_free(s); - return(0); + ret=0; } else - return(1); + { + ret=1; + + if (SSL_CTX_sess_get_cache_size(ctx) > 0) + { + while (SSL_CTX_sess_number(ctx) > + SSL_CTX_sess_get_cache_size(ctx)) + { + if (!remove_session_lock(ctx, + ctx->session_cache_tail, 0)) + break; + else + ctx->stats.sess_cache_full++; + } + } + } + CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + return(ret); } -int SSL_CTX_remove_session(ctx,c) -SSL_CTX *ctx; -SSL_SESSION *c; +int SSL_CTX_remove_session(SSL_CTX *ctx, SSL_SESSION *c) +{ + return remove_session_lock(ctx, c, 1); +} + +static int remove_session_lock(SSL_CTX *ctx, SSL_SESSION *c, int lck) { SSL_SESSION *r; int ret=0; - if ((c->session_id_length != 0) && (c != NULL)) + if ((c != NULL) && (c->session_id_length != 0)) { - CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); + if(lck) CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); r=(SSL_SESSION *)lh_delete(ctx->sessions,(char *)c); - if (r != NULL) ret=1; + if (r != NULL) + { + ret=1; + SSL_SESSION_list_remove(ctx,c); + } - CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); + if(lck) CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); if (ret) { r->not_resumable=1; if (ctx->remove_session_cb != NULL) - ctx->remove_session_cb(ctx,c); + ctx->remove_session_cb(ctx,r); SSL_SESSION_free(r); } } @@ -277,12 +427,17 @@ SSL_SESSION *c; return(ret); } -void SSL_SESSION_free(ss) -SSL_SESSION *ss; +void SSL_SESSION_free(SSL_SESSION *ss) { int i; + if(ss == NULL) + return; + i=CRYPTO_add(&ss->references,-1,CRYPTO_LOCK_SSL_SESSION); +#ifdef REF_PRINT + REF_PRINT("SSL_SESSION",ss); +#endif if (i > 0) return; #ifdef REF_CHECK if (i < 0) @@ -292,19 +447,19 @@ SSL_SESSION *ss; } #endif + CRYPTO_free_ex_data(ssl_session_meth,(char *)ss,&ss->ex_data); + memset(ss->key_arg,0,SSL_MAX_KEY_ARG_LENGTH); memset(ss->master_key,0,SSL_MAX_MASTER_KEY_LENGTH); memset(ss->session_id,0,SSL_MAX_SSL_SESSION_ID_LENGTH); - if (ss->cert != NULL) ssl_cert_free(ss->cert); + if (ss->sess_cert != NULL) ssl_sess_cert_free(ss->sess_cert); if (ss->peer != NULL) X509_free(ss->peer); - if (ss->ciphers != NULL) sk_free(ss->ciphers); + if (ss->ciphers != NULL) sk_SSL_CIPHER_free(ss->ciphers); memset(ss,0,sizeof(*ss)); Free(ss); } -int SSL_set_session(s, session) -SSL *s; -SSL_SESSION *session; +int SSL_set_session(SSL *s, SSL_SESSION *session) { int ret=0; SSL_METHOD *meth; @@ -324,7 +479,10 @@ SSL_SESSION *session; { if (!SSL_set_ssl_method(s,meth)) return(0); - session->timeout=SSL_get_default_timeout(s); + if (s->ctx->session_timeout == 0) + session->timeout=SSL_get_default_timeout(s); + else + session->timeout=s->ctx->session_timeout; } /* CRYPTO_w_lock(CRYPTO_LOCK_SSL);*/ @@ -335,41 +493,66 @@ SSL_SESSION *session; /* CRYPTO_w_unlock(CRYPTO_LOCK_SSL);*/ ret=1; } + else + { + if (s->session != NULL) + { + SSL_SESSION_free(s->session); + s->session=NULL; + } + + meth=s->ctx->method; + if (meth != s->method) + { + if (!SSL_set_ssl_method(s,meth)) + return(0); + } + ret=1; + } return(ret); } -long SSL_set_timeout(s,t) -SSL_SESSION *s; -long t; +long SSL_SESSION_set_timeout(SSL_SESSION *s, long t) { if (s == NULL) return(0); s->timeout=t; return(1); } -long SSL_get_timeout(s) -SSL_SESSION *s; +long SSL_SESSION_get_timeout(SSL_SESSION *s) { if (s == NULL) return(0); return(s->timeout); } -long SSL_get_time(s) -SSL_SESSION *s; +long SSL_SESSION_get_time(SSL_SESSION *s) { if (s == NULL) return(0); return(s->time); } -long SSL_set_time(s,t) -SSL_SESSION *s; -long t; +long SSL_SESSION_set_time(SSL_SESSION *s, long t) { if (s == NULL) return(0); s->time=t; return(t); } +long SSL_CTX_set_timeout(SSL_CTX *s, long t) + { + long l; + if (s == NULL) return(0); + l=s->session_timeout; + s->session_timeout=t; + return(l); + } + +long SSL_CTX_get_timeout(SSL_CTX *s) + { + if (s == NULL) return(0); + return(s->session_timeout); + } + typedef struct timeout_param_st { SSL_CTX *ctx; @@ -377,13 +560,14 @@ typedef struct timeout_param_st LHASH *cache; } TIMEOUT_PARAM; -static void timeout(s,p) -SSL_SESSION *s; -TIMEOUT_PARAM *p; +static void timeout(SSL_SESSION *s, TIMEOUT_PARAM *p) { if ((p->time == 0) || (p->time > (s->time+s->timeout))) /* timeout */ { + /* The reason we don't call SSL_CTX_remove_session() is to + * save on locking overhead */ lh_delete(p->cache,(char *)s); + SSL_SESSION_list_remove(p->ctx,s); s->not_resumable=1; if (p->ctx->remove_session_cb != NULL) p->ctx->remove_session_cb(p->ctx,s); @@ -391,15 +575,13 @@ TIMEOUT_PARAM *p; } } -void SSL_CTX_flush_sessions(s,t) -SSL_CTX *s; -long t; +void SSL_CTX_flush_sessions(SSL_CTX *s, long t) { unsigned long i; TIMEOUT_PARAM tp; tp.ctx=s; - tp.cache=SSL_CTX_sessions(s); + tp.cache=s->sessions; if (tp.cache == NULL) return; tp.time=t; CRYPTO_w_lock(CRYPTO_LOCK_SSL_CTX); @@ -410,8 +592,7 @@ long t; CRYPTO_w_unlock(CRYPTO_LOCK_SSL_CTX); } -int ssl_clear_bad_session(s) -SSL *s; +int ssl_clear_bad_session(SSL *s) { if ( (s->session != NULL) && !(s->shutdown & SSL_SENT_SHUTDOWN) && @@ -423,3 +604,59 @@ SSL *s; else return(0); } + +/* locked by SSL_CTX in the calling function */ +static void SSL_SESSION_list_remove(SSL_CTX *ctx, SSL_SESSION *s) + { + if ((s->next == NULL) || (s->prev == NULL)) return; + + if (s->next == (SSL_SESSION *)&(ctx->session_cache_tail)) + { /* last element in list */ + if (s->prev == (SSL_SESSION *)&(ctx->session_cache_head)) + { /* only one element in list */ + ctx->session_cache_head=NULL; + ctx->session_cache_tail=NULL; + } + else + { + ctx->session_cache_tail=s->prev; + s->prev->next=(SSL_SESSION *)&(ctx->session_cache_tail); + } + } + else + { + if (s->prev == (SSL_SESSION *)&(ctx->session_cache_head)) + { /* first element in list */ + ctx->session_cache_head=s->next; + s->next->prev=(SSL_SESSION *)&(ctx->session_cache_head); + } + else + { /* middle of list */ + s->next->prev=s->prev; + s->prev->next=s->next; + } + } + s->prev=s->next=NULL; + } + +static void SSL_SESSION_list_add(SSL_CTX *ctx, SSL_SESSION *s) + { + if ((s->next != NULL) && (s->prev != NULL)) + SSL_SESSION_list_remove(ctx,s); + + if (ctx->session_cache_head == NULL) + { + ctx->session_cache_head=s; + ctx->session_cache_tail=s; + s->prev=(SSL_SESSION *)&(ctx->session_cache_head); + s->next=(SSL_SESSION *)&(ctx->session_cache_tail); + } + else + { + s->next=ctx->session_cache_head; + s->next->prev=s; + s->prev=(SSL_SESSION *)&(ctx->session_cache_head); + ctx->session_cache_head=s; + } + } +