diff --git a/include/proto/shctx.h b/include/proto/shctx.h index d56320c44..dfa0c5736 100644 --- a/include/proto/shctx.h +++ b/include/proto/shctx.h @@ -49,6 +49,14 @@ int shared_context_init(struct shared_context **orig_shctx, int size, int shared * Shared context MUST be firstly initialized */ void shared_context_set_cache(SSL_CTX *ctx); + +int shsess_free(struct shared_context *shctx, struct shared_session *shsess); + +struct shared_session *shsess_get_next(struct shared_context *shctx, int data_len); + +int shsess_store(struct shared_context *shctx, unsigned char *s_id, unsigned char *data, int data_len); + + /* Lock functions */ #if defined (USE_PRIVATE_CACHE) @@ -186,6 +194,30 @@ static inline void _shared_context_unlock(struct shared_context *shctx) #endif +/* List Macros */ + +#define shblock_unset(s) (s)->n->p = (s)->p; \ + (s)->p->n = (s)->n; + +static inline void shblock_set_free(struct shared_context *shctx, + struct shared_block *s) +{ + shblock_unset(s); + (s)->n = &shctx->free; + (s)->p = shctx->free.p; + shctx->free.p->n = s; + shctx->free.p = s; +} + +static inline void shblock_set_active(struct shared_context *shctx, + struct shared_block *s) +{ + shblock_unset(s) + (s)->n = &shctx->active; + (s)->p = shctx->active.p; + shctx->active.p->n = s; + shctx->active.p = s; +} #endif /* SHCTX_H */ diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h index 86ad137a9..9f974dd69 100644 --- a/include/proto/ssl_sock.h +++ b/include/proto/ssl_sock.h @@ -76,6 +76,19 @@ SSL_CTX *ssl_sock_get_generated_cert(unsigned int key, struct bind_conf *bind_co int ssl_sock_set_generated_cert(SSL_CTX *ctx, unsigned int key, struct bind_conf *bind_conf); unsigned int ssl_sock_generated_cert_key(const void *data, size_t len); + +/* ssl shctx macro */ + +#define shsess_tree_delete(s) ebmb_delete(&(s)->key); + +#define shsess_tree_insert(shctx, s) (struct shared_session *)ebmb_insert(&shctx->active.data.session.key.node.branches, \ + &(s)->key, SSL_MAX_SSL_SESSION_ID_LENGTH); + +#define shsess_tree_lookup(shctx, k) (struct shared_session *)ebmb_lookup(&shctx->active.data.session.key.node.branches, \ + (k), SSL_MAX_SSL_SESSION_ID_LENGTH); + + + #endif /* _PROTO_SSL_SOCK_H */ /* diff --git a/src/shctx.c b/src/shctx.c index 8f900cc31..853b522ee 100644 --- a/src/shctx.c +++ b/src/shctx.c @@ -15,55 +15,25 @@ #include #include +#include #include +#include #include #include #include + #if !defined (USE_PRIVATE_CACHE) int use_shared_mem = 0; #endif -/* List Macros */ - -#define shblock_unset(s) (s)->n->p = (s)->p; \ - (s)->p->n = (s)->n; - -static inline void shblock_set_free(struct shared_context *shctx, - struct shared_block *s) -{ - shblock_unset(s); - (s)->n = &shctx->free; - (s)->p = shctx->free.p; - shctx->free.p->n = s; - shctx->free.p = s; -} - -static inline void shblock_set_active(struct shared_context *shctx, - struct shared_block *s) -{ - shblock_unset(s) - (s)->n = &shctx->active; - (s)->p = shctx->active.p; - shctx->active.p->n = s; - shctx->active.p = s; -} - /* Tree Macros */ -#define shsess_tree_delete(s) ebmb_delete(&(s)->key); - -#define shsess_tree_insert(shctx, s) (struct shared_session *)ebmb_insert(&shctx->active.data.session.key.node.branches, \ - &(s)->key, SSL_MAX_SSL_SESSION_ID_LENGTH); - -#define shsess_tree_lookup(shctx, k) (struct shared_session *)ebmb_lookup(&shctx->active.data.session.key.node.branches, \ - (k), SSL_MAX_SSL_SESSION_ID_LENGTH); - /* shared session functions */ /* Free session blocks, returns number of freed blocks */ -static int shsess_free(struct shared_context *shctx, struct shared_session *shsess) +int shsess_free(struct shared_context *shctx, struct shared_session *shsess) { struct shared_block *block; int ret = 1; @@ -95,7 +65,7 @@ static int shsess_free(struct shared_context *shctx, struct shared_session *shse * Returns a ptr on a free block if it succeeds, or NULL if there are not * enough blocks to store that session. */ -static struct shared_session *shsess_get_next(struct shared_context *shctx, int data_len) +struct shared_session *shsess_get_next(struct shared_context *shctx, int data_len) { int head = 0; struct shared_block *b; @@ -135,7 +105,7 @@ static struct shared_session *shsess_get_next(struct shared_context *shctx, int * data_len: asn1 encoded session length * Returns 1 id session was stored (else 0) */ -static int shsess_store(struct shared_context *shctx, unsigned char *s_id, unsigned char *data, int data_len) +int shsess_store(struct shared_context *shctx, unsigned char *s_id, unsigned char *data, int data_len) { struct shared_session *shsess, *oldshsess; @@ -200,168 +170,6 @@ static int shsess_store(struct shared_context *shctx, unsigned char *s_id, unsig } -/* SSL context callbacks */ - -/* SSL callback used on new session creation */ -int shctx_new_cb(SSL *ssl, SSL_SESSION *sess) -{ - unsigned char encsess[SHSESS_MAX_DATA_LEN]; /* encoded session */ - unsigned char encid[SSL_MAX_SSL_SESSION_ID_LENGTH]; /* encoded id */ - unsigned char *p; - int data_len; - unsigned int sid_length, sid_ctx_length; - const unsigned char *sid_data; - const unsigned char *sid_ctx_data; - - /* Session id is already stored in to key and session id is known - * so we dont store it to keep size. - */ - - sid_data = SSL_SESSION_get_id(sess, &sid_length); - sid_ctx_data = SSL_SESSION_get0_id_context(sess, &sid_ctx_length); - SSL_SESSION_set1_id(sess, sid_data, 0); - SSL_SESSION_set1_id_context(sess, sid_ctx_data, 0); - - /* check if buffer is large enough for the ASN1 encoded session */ - data_len = i2d_SSL_SESSION(sess, NULL); - if (data_len > SHSESS_MAX_DATA_LEN) - goto err; - - p = encsess; - - /* process ASN1 session encoding before the lock */ - i2d_SSL_SESSION(sess, &p); - - memcpy(encid, sid_data, sid_length); - if (sid_length < SSL_MAX_SSL_SESSION_ID_LENGTH) - memset(encid + sid_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sid_length); - - shared_context_lock(ssl_shctx); - - /* store to cache */ - shsess_store(ssl_shctx, encid, encsess, data_len); - - shared_context_unlock(ssl_shctx); - -err: - /* reset original length values */ - SSL_SESSION_set1_id(sess, sid_data, sid_length); - SSL_SESSION_set1_id_context(sess, sid_ctx_data, sid_ctx_length); - - return 0; /* do not increment session reference count */ -} - -/* SSL callback used on lookup an existing session cause none found in internal cache */ -SSL_SESSION *shctx_get_cb(SSL *ssl, __OPENSSL_110_CONST__ unsigned char *key, int key_len, int *do_copy) -{ - struct shared_session *shsess; - unsigned char data[SHSESS_MAX_DATA_LEN], *p; - unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH]; - int data_len; - SSL_SESSION *sess; - - global.shctx_lookups++; - - /* allow the session to be freed automatically by openssl */ - *do_copy = 0; - - /* tree key is zeros padded sessionid */ - if (key_len < SSL_MAX_SSL_SESSION_ID_LENGTH) { - memcpy(tmpkey, key, key_len); - memset(tmpkey + key_len, 0, SSL_MAX_SSL_SESSION_ID_LENGTH - key_len); - key = tmpkey; - } - - /* lock cache */ - shared_context_lock(ssl_shctx); - - /* lookup for session */ - shsess = shsess_tree_lookup(ssl_shctx, key); - if (!shsess) { - /* no session found: unlock cache and exit */ - shared_context_unlock(ssl_shctx); - global.shctx_misses++; - return NULL; - } - - data_len = ((struct shared_block *)shsess)->data_len; - if (data_len <= sizeof(shsess->data)) { - /* Session stored on single block */ - memcpy(data, shsess->data, data_len); - shblock_set_active(ssl_shctx, (struct shared_block *)shsess); - } - else { - /* Session stored on multiple blocks */ - struct shared_block *block; - - memcpy(data, shsess->data, sizeof(shsess->data)); - p = data + sizeof(shsess->data); - block = ((struct shared_block *)shsess)->n; - shblock_set_active(ssl_shctx, (struct shared_block *)shsess); - while (1) { - /* Retrieve data from next block */ - struct shared_block *next; - - if (block->data_len <= sizeof(block->data.data)) { - /* This is the last block */ - memcpy(p, block->data.data, block->data_len); - p += block->data_len; - shblock_set_active(ssl_shctx, block); - break; - } - /* Intermediate block */ - memcpy(p, block->data.data, sizeof(block->data.data)); - p += sizeof(block->data.data); - next = block->n; - shblock_set_active(ssl_shctx, block); - block = next; - } - } - - shared_context_unlock(ssl_shctx); - - /* decode ASN1 session */ - p = data; - sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len); - /* Reset session id and session id contenxt */ - if (sess) { - SSL_SESSION_set1_id(sess, key, key_len); - SSL_SESSION_set1_id_context(sess, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME)); - } - - return sess; -} - -/* SSL callback used to signal session is no more used in internal cache */ -void shctx_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) -{ - struct shared_session *shsess; - unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH]; - unsigned int sid_length; - const unsigned char *sid_data; - (void)ctx; - - sid_data = SSL_SESSION_get_id(sess, &sid_length); - /* tree key is zeros padded sessionid */ - if (sid_length < SSL_MAX_SSL_SESSION_ID_LENGTH) { - memcpy(tmpkey, sid_data, sid_length); - memset(tmpkey+sid_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH - sid_length); - sid_data = tmpkey; - } - - shared_context_lock(ssl_shctx); - - /* lookup for session */ - shsess = shsess_tree_lookup(ssl_shctx, sid_data); - if (shsess) { - /* free session */ - shsess_tree_delete(shsess); - shsess_free(ssl_shctx, shsess); - } - - /* unlock cache */ - shared_context_unlock(ssl_shctx); -} /* Allocate shared memory context. * is maximum cached sessions. @@ -461,25 +269,3 @@ err: return ret; } - -/* Set session cache mode to server and disable openssl internal cache. - * Set shared cache callbacks on an ssl context. - * Shared context MUST be firstly initialized */ -void shared_context_set_cache(SSL_CTX *ctx) -{ - SSL_CTX_set_session_id_context(ctx, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME)); - - if (!ssl_shctx) { - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); - return; - } - - SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER | - SSL_SESS_CACHE_NO_INTERNAL | - SSL_SESS_CACHE_NO_AUTO_CLEAR); - - /* Set callbacks */ - SSL_CTX_sess_set_new_cb(ctx, shctx_new_cb); - SSL_CTX_sess_set_get_cb(ctx, shctx_get_cb); - SSL_CTX_sess_set_remove_cb(ctx, shctx_remove_cb); -} diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 24e4f21ad..71bcbe3dc 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -3718,6 +3718,191 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf) return cfgerr; } +/* SSL context callbacks */ + +/* SSL callback used on new session creation */ +int shctx_new_cb(SSL *ssl, SSL_SESSION *sess) +{ + unsigned char encsess[SHSESS_MAX_DATA_LEN]; /* encoded session */ + unsigned char encid[SSL_MAX_SSL_SESSION_ID_LENGTH]; /* encoded id */ + unsigned char *p; + int data_len; + unsigned int sid_length, sid_ctx_length; + const unsigned char *sid_data; + const unsigned char *sid_ctx_data; + + /* Session id is already stored in to key and session id is known + * so we dont store it to keep size. + */ + + sid_data = SSL_SESSION_get_id(sess, &sid_length); + sid_ctx_data = SSL_SESSION_get0_id_context(sess, &sid_ctx_length); + SSL_SESSION_set1_id(sess, sid_data, 0); + SSL_SESSION_set1_id_context(sess, sid_ctx_data, 0); + + /* check if buffer is large enough for the ASN1 encoded session */ + data_len = i2d_SSL_SESSION(sess, NULL); + if (data_len > SHSESS_MAX_DATA_LEN) + goto err; + + p = encsess; + + /* process ASN1 session encoding before the lock */ + i2d_SSL_SESSION(sess, &p); + + memcpy(encid, sid_data, sid_length); + if (sid_length < SSL_MAX_SSL_SESSION_ID_LENGTH) + memset(encid + sid_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sid_length); + + shared_context_lock(ssl_shctx); + + /* store to cache */ + shsess_store(ssl_shctx, encid, encsess, data_len); + + shared_context_unlock(ssl_shctx); + +err: + /* reset original length values */ + SSL_SESSION_set1_id(sess, sid_data, sid_length); + SSL_SESSION_set1_id_context(sess, sid_ctx_data, sid_ctx_length); + + return 0; /* do not increment session reference count */ +} + +/* SSL callback used on lookup an existing session cause none found in internal cache */ +SSL_SESSION *shctx_get_cb(SSL *ssl, __OPENSSL_110_CONST__ unsigned char *key, int key_len, int *do_copy) +{ + struct shared_session *shsess; + unsigned char data[SHSESS_MAX_DATA_LEN], *p; + unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH]; + int data_len; + SSL_SESSION *sess; + + global.shctx_lookups++; + + /* allow the session to be freed automatically by openssl */ + *do_copy = 0; + + /* tree key is zeros padded sessionid */ + if (key_len < SSL_MAX_SSL_SESSION_ID_LENGTH) { + memcpy(tmpkey, key, key_len); + memset(tmpkey + key_len, 0, SSL_MAX_SSL_SESSION_ID_LENGTH - key_len); + key = tmpkey; + } + + /* lock cache */ + shared_context_lock(ssl_shctx); + + /* lookup for session */ + shsess = shsess_tree_lookup(ssl_shctx, key); + if (!shsess) { + /* no session found: unlock cache and exit */ + shared_context_unlock(ssl_shctx); + global.shctx_misses++; + return NULL; + } + + data_len = ((struct shared_block *)shsess)->data_len; + if (data_len <= sizeof(shsess->data)) { + /* Session stored on single block */ + memcpy(data, shsess->data, data_len); + shblock_set_active(ssl_shctx, (struct shared_block *)shsess); + } + else { + /* Session stored on multiple blocks */ + struct shared_block *block; + + memcpy(data, shsess->data, sizeof(shsess->data)); + p = data + sizeof(shsess->data); + block = ((struct shared_block *)shsess)->n; + shblock_set_active(ssl_shctx, (struct shared_block *)shsess); + while (1) { + /* Retrieve data from next block */ + struct shared_block *next; + + if (block->data_len <= sizeof(block->data.data)) { + /* This is the last block */ + memcpy(p, block->data.data, block->data_len); + p += block->data_len; + shblock_set_active(ssl_shctx, block); + break; + } + /* Intermediate block */ + memcpy(p, block->data.data, sizeof(block->data.data)); + p += sizeof(block->data.data); + next = block->n; + shblock_set_active(ssl_shctx, block); + block = next; + } + } + + shared_context_unlock(ssl_shctx); + + /* decode ASN1 session */ + p = data; + sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len); + /* Reset session id and session id contenxt */ + if (sess) { + SSL_SESSION_set1_id(sess, key, key_len); + SSL_SESSION_set1_id_context(sess, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME)); + } + + return sess; +} + +/* SSL callback used to signal session is no more used in internal cache */ +void shctx_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess) +{ + struct shared_session *shsess; + unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH]; + unsigned int sid_length; + const unsigned char *sid_data; + (void)ctx; + + sid_data = SSL_SESSION_get_id(sess, &sid_length); + /* tree key is zeros padded sessionid */ + if (sid_length < SSL_MAX_SSL_SESSION_ID_LENGTH) { + memcpy(tmpkey, sid_data, sid_length); + memset(tmpkey+sid_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH - sid_length); + sid_data = tmpkey; + } + + shared_context_lock(ssl_shctx); + + /* lookup for session */ + shsess = shsess_tree_lookup(ssl_shctx, sid_data); + if (shsess) { + /* free session */ + shsess_tree_delete(shsess); + shsess_free(ssl_shctx, shsess); + } + + /* unlock cache */ + shared_context_unlock(ssl_shctx); +} + +/* Set session cache mode to server and disable openssl internal cache. + * Set shared cache callbacks on an ssl context. + * Shared context MUST be firstly initialized */ +void shared_context_set_cache(SSL_CTX *ctx) +{ + SSL_CTX_set_session_id_context(ctx, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME)); + + if (!ssl_shctx) { + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF); + return; + } + + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER | + SSL_SESS_CACHE_NO_INTERNAL | + SSL_SESS_CACHE_NO_AUTO_CLEAR); + + /* Set callbacks */ + SSL_CTX_sess_set_new_cb(ctx, shctx_new_cb); + SSL_CTX_sess_set_get_cb(ctx, shctx_get_cb); + SSL_CTX_sess_set_remove_cb(ctx, shctx_remove_cb); +} + int ssl_sock_prepare_ctx(struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf, SSL_CTX *ctx) { struct proxy *curproxy = bind_conf->frontend;