mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-09-22 14:21:25 +02:00
MEDIUM: ssl: manage shared cache by blocks for huge sessions.
Sessions using client certs are huge (more than 1 kB) and do not fit in session cache, or require a huge cache. In this new implementation sshcachesize set a number of available blocks instead a number of available sessions. Each block is large enough (128 bytes) to store a simple session (without client certs). Huge sessions will take multiple blocks depending on client certificate size. Note: some unused code for session sync with remote peers was temporarily removed.
This commit is contained in:
parent
dc979f2492
commit
af9619da3e
@ -878,14 +878,16 @@ tune.sndbuf.server <number>
|
|||||||
notifying haproxy again.
|
notifying haproxy again.
|
||||||
|
|
||||||
tune.ssl.cachesize <number>
|
tune.ssl.cachesize <number>
|
||||||
Sets the size of the global SSL session cache, in number of sessions. Each
|
Sets the size of the global SSL session cache, in a number of blocks. A block
|
||||||
entry uses approximately 600 bytes of memory. The default value may be forced
|
is large enough to contain an encoded session without peer certificate.
|
||||||
at build time, otherwise defaults to 20000. When the cache is full, the most
|
An encoded session with peer certificate is stored in multiple blocks
|
||||||
idle entries are purged and reassigned. Higher values reduce the occurrence
|
depending on the size of the peer certificate. A block use approximatively
|
||||||
of such a purge, hence the number of CPU-intensive SSL handshakes by ensuring
|
200 bytes of memory. The default value may be forced at build time, otherwise
|
||||||
that all users keep their session as long as possible. All entries are pre-
|
defaults to 20000. When the cache is full, the most idle entries are purged
|
||||||
allocated upon startup and are shared between all processes if "nbproc" is
|
and reassigned. Higher values reduce the occurrence of such a purge, hence
|
||||||
greater than 1.
|
the number of CPU-intensive SSL handshakes by ensuring that all users keep
|
||||||
|
their session as long as possible. All entries are pre-allocated upon startup
|
||||||
|
and are shared between all processes if "nbproc" is greater than 1.
|
||||||
|
|
||||||
tune.ssl.lifetime <timeout>
|
tune.ssl.lifetime <timeout>
|
||||||
Sets how long a cached SSL session may remain valid. This time is expressed
|
Sets how long a cached SSL session may remain valid. This time is expressed
|
||||||
|
@ -16,13 +16,12 @@
|
|||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#ifndef SHSESS_MAX_FOOTER_LEN
|
#ifndef SHSESS_BLOCK_MIN_SIZE
|
||||||
#define SHSESS_MAX_FOOTER_LEN sizeof(uint32_t) \
|
#define SHSESS_BLOCK_MIN_SIZE 128
|
||||||
+ EVP_MAX_MD_SIZE
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SHSESS_MAX_DATA_LEN
|
#ifndef SHSESS_MAX_DATA_LEN
|
||||||
#define SHSESS_MAX_DATA_LEN 512
|
#define SHSESS_MAX_DATA_LEN 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef SHCTX_DEFAULT_SIZE
|
#ifndef SHCTX_DEFAULT_SIZE
|
||||||
@ -33,37 +32,15 @@
|
|||||||
#define SHCTX_APPNAME "haproxy"
|
#define SHCTX_APPNAME "haproxy"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SHSESS_MAX_ENCODED_LEN SSL_MAX_SSL_SESSION_ID_LENGTH \
|
|
||||||
+ SHSESS_MAX_DATA_LEN \
|
|
||||||
+ SHSESS_MAX_FOOTER_LEN
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* Callback called on a new session event:
|
|
||||||
* session contains the sessionid zeros padded to SSL_MAX_SSL_SESSION_ID_LENGTH
|
|
||||||
* followed by ASN1 session encoding.
|
|
||||||
* len is set to SSL_MAX_SSL_SESSION_ID_LENGTH + ASN1 session length
|
|
||||||
* len is always less than SSL_MAX_SSL_SESSION_ID_LENGTH + SHSESS_MAX_DATA_LEN.
|
|
||||||
* Remaining Bytes from len to SHSESS_MAX_ENCODED_LEN can be used to add a footer.
|
|
||||||
* cdate is the creation date timestamp.
|
|
||||||
*/
|
|
||||||
void shsess_set_new_cbk(void (*func)(unsigned char *session, unsigned int len, long cdate));
|
|
||||||
|
|
||||||
/* Add a session into the cache,
|
|
||||||
* session contains the sessionid zeros padded to SSL_MAX_SSL_SESSION_ID_LENGTH
|
|
||||||
* followed by ASN1 session encoding.
|
|
||||||
* len is set to SSL_MAX_SSL_SESSION_ID_LENGTH + ASN1 data length.
|
|
||||||
* if len greater than SHSESS_MAX_ENCODED_LEN, session is not added.
|
|
||||||
* if cdate not 0, on get events session creation date will be reset to cdate */
|
|
||||||
void shctx_sess_add(const unsigned char *session, unsigned int session_len, long cdate);
|
|
||||||
|
|
||||||
/* Allocate shared memory context.
|
/* Allocate shared memory context.
|
||||||
* size is maximum cached sessions.
|
* <size> is the number of allocated blocks into cache (default 128 bytes)
|
||||||
* if set less or equal to 0, SHCTX_DEFAULT_SIZE is used.
|
* A block is large enough to contain a classic session (without client cert)
|
||||||
* set use_shared_memory to 1 to use a mapped shared memory insteed
|
* If <size> is set less or equal to 0, SHCTX_DEFAULT_SIZE is used.
|
||||||
* of private. (ignored if compiled whith USE_PRIVATE_CACHE=1)
|
* Set <use_shared_memory> to 1 to use a mapped shared memory instead
|
||||||
* Returns: -1 on alloc failure, size if it performs context alloc,
|
* of private. (ignored if compiled with USE_PRIVATE_CACHE=1).
|
||||||
* and 0 if cache is already allocated */
|
* Returns: -1 on alloc failure, <size> if it performs context alloc,
|
||||||
|
* and 0 if cache is already allocated.
|
||||||
|
*/
|
||||||
int shared_context_init(int size, int use_shared_memory);
|
int shared_context_init(int size, int use_shared_memory);
|
||||||
|
|
||||||
/* Set shared cache callbacks on an ssl context.
|
/* Set shared cache callbacks on an ssl context.
|
||||||
|
401
src/shctx.c
401
src/shctx.c
@ -24,20 +24,39 @@
|
|||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#endif /* USE_SYSCALL_FUTEX */
|
#endif /* USE_SYSCALL_FUTEX */
|
||||||
#endif
|
#endif
|
||||||
|
#include <arpa/inet.h>
|
||||||
#include "ebmbtree.h"
|
#include "ebmbtree.h"
|
||||||
#include "proto/shctx.h"
|
#include "proto/shctx.h"
|
||||||
|
|
||||||
|
struct shsess_packet_hdr {
|
||||||
|
unsigned int eol;
|
||||||
|
unsigned char final:1;
|
||||||
|
unsigned char seq:7;
|
||||||
|
unsigned char id[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct shsess_packet {
|
||||||
|
unsigned char version;
|
||||||
|
unsigned char sig[SHA_DIGEST_LENGTH];
|
||||||
|
struct shsess_packet_hdr hdr;
|
||||||
|
unsigned char data[0];
|
||||||
|
};
|
||||||
|
|
||||||
struct shared_session {
|
struct shared_session {
|
||||||
struct ebmb_node key;
|
struct ebmb_node key;
|
||||||
unsigned char key_data[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
unsigned char key_data[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
||||||
long c_date;
|
unsigned char data[SHSESS_BLOCK_MIN_SIZE];
|
||||||
int data_len;
|
|
||||||
unsigned char data[SHSESS_MAX_DATA_LEN];
|
|
||||||
struct shared_session *p;
|
|
||||||
struct shared_session *n;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct shared_block {
|
||||||
|
union {
|
||||||
|
struct shared_session session;
|
||||||
|
unsigned char data[sizeof(struct shared_session)];
|
||||||
|
} data;
|
||||||
|
short int data_len;
|
||||||
|
struct shared_block *p;
|
||||||
|
struct shared_block *n;
|
||||||
|
};
|
||||||
|
|
||||||
struct shared_context {
|
struct shared_context {
|
||||||
#ifndef USE_PRIVATE_CACHE
|
#ifndef USE_PRIVATE_CACHE
|
||||||
@ -47,8 +66,11 @@ struct shared_context {
|
|||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
struct shared_session active;
|
struct shsess_packet_hdr upd;
|
||||||
struct shared_session free;
|
unsigned char data[SHSESS_MAX_DATA_LEN];
|
||||||
|
short int data_len;
|
||||||
|
struct shared_block active;
|
||||||
|
struct shared_block free;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Static shared context */
|
/* Static shared context */
|
||||||
@ -57,9 +79,6 @@ static struct shared_context *shctx = NULL;
|
|||||||
static int use_shared_mem = 0;
|
static int use_shared_mem = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Callbacks */
|
|
||||||
static void (*shared_session_new_cbk)(unsigned char *session, unsigned int session_len, long cdate);
|
|
||||||
|
|
||||||
/* Lock functions */
|
/* Lock functions */
|
||||||
#ifdef USE_PRIVATE_CACHE
|
#ifdef USE_PRIVATE_CACHE
|
||||||
#define shared_context_lock()
|
#define shared_context_lock()
|
||||||
@ -156,41 +175,171 @@ static inline void _shared_context_unlock(void)
|
|||||||
|
|
||||||
/* List Macros */
|
/* List Macros */
|
||||||
|
|
||||||
#define shsess_unset(s) (s)->n->p = (s)->p; \
|
#define shblock_unset(s) (s)->n->p = (s)->p; \
|
||||||
(s)->p->n = (s)->n;
|
(s)->p->n = (s)->n;
|
||||||
|
|
||||||
#define shsess_set_free(s) shsess_unset(s) \
|
#define shblock_set_free(s) shblock_unset(s) \
|
||||||
(s)->p = &shctx->free; \
|
(s)->n = &shctx->free; \
|
||||||
(s)->n = shctx->free.n; \
|
(s)->p = shctx->free.p; \
|
||||||
shctx->free.n->p = s; \
|
shctx->free.p->n = s; \
|
||||||
shctx->free.n = s;
|
shctx->free.p = s;
|
||||||
|
|
||||||
|
|
||||||
#define shsess_set_active(s) shsess_unset(s) \
|
#define shblock_set_active(s) shblock_unset(s) \
|
||||||
(s)->p = &shctx->active; \
|
(s)->n = &shctx->active; \
|
||||||
(s)->n = shctx->active.n; \
|
(s)->p = shctx->active.p; \
|
||||||
shctx->active.n->p = s; \
|
shctx->active.p->n = s; \
|
||||||
shctx->active.n = s;
|
shctx->active.p = s;
|
||||||
|
|
||||||
|
|
||||||
#define shsess_get_next() (shctx->free.p == &shctx->free) ? \
|
|
||||||
shctx->active.p : shctx->free.p;
|
|
||||||
|
|
||||||
/* Tree Macros */
|
/* Tree Macros */
|
||||||
|
|
||||||
#define shsess_tree_delete(s) ebmb_delete(&(s)->key);
|
#define shsess_tree_delete(s) ebmb_delete(&(s)->key);
|
||||||
|
|
||||||
#define shsess_tree_insert(s) (struct shared_session *)ebmb_insert(&shctx->active.key.node.branches, \
|
#define shsess_tree_insert(s) (struct shared_session *)ebmb_insert(&shctx->active.data.session.key.node.branches, \
|
||||||
&(s)->key, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
&(s)->key, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
||||||
|
|
||||||
#define shsess_tree_lookup(k) (struct shared_session *)ebmb_lookup(&shctx->active.key.node.branches, \
|
#define shsess_tree_lookup(k) (struct shared_session *)ebmb_lookup(&shctx->active.data.session.key.node.branches, \
|
||||||
(k), SSL_MAX_SSL_SESSION_ID_LENGTH);
|
(k), SSL_MAX_SSL_SESSION_ID_LENGTH);
|
||||||
|
|
||||||
/* Other Macros */
|
/* shared session functions */
|
||||||
|
|
||||||
#define shsess_set_key(s,k,l) { memcpy((s)->key_data, (k), (l)); \
|
/* Free session blocks, returns number of freed blocks */
|
||||||
if ((l) < SSL_MAX_SSL_SESSION_ID_LENGTH) \
|
static int shsess_free(struct shared_session *shsess)
|
||||||
memset((s)->key_data+(l), 0, SSL_MAX_SSL_SESSION_ID_LENGTH-(l)); };
|
{
|
||||||
|
struct shared_block *block;
|
||||||
|
int ret = 1;
|
||||||
|
|
||||||
|
if (((struct shared_block *)shsess)->data_len <= sizeof(shsess->data)) {
|
||||||
|
shblock_set_free((struct shared_block *)shsess);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
block = ((struct shared_block *)shsess)->n;
|
||||||
|
shblock_set_free((struct shared_block *)shsess);
|
||||||
|
while (1) {
|
||||||
|
struct shared_block *next;
|
||||||
|
|
||||||
|
if (block->data_len <= sizeof(block->data)) {
|
||||||
|
/* last block */
|
||||||
|
shblock_set_free(block);
|
||||||
|
ret++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
next = block->n;
|
||||||
|
shblock_set_free(block);
|
||||||
|
ret++;
|
||||||
|
block = next;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This function frees enough blocks to store a new session of data_len.
|
||||||
|
* 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(int data_len)
|
||||||
|
{
|
||||||
|
int head = 0;
|
||||||
|
struct shared_block *b;
|
||||||
|
|
||||||
|
b = shctx->free.n;
|
||||||
|
while (b != &shctx->free) {
|
||||||
|
if (!head) {
|
||||||
|
data_len -= sizeof(b->data.session.data);
|
||||||
|
head = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
data_len -= sizeof(b->data.data);
|
||||||
|
if (data_len <= 0)
|
||||||
|
return &shctx->free.n->data.session;
|
||||||
|
b = b->n;
|
||||||
|
}
|
||||||
|
b = shctx->active.n;
|
||||||
|
while (b != &shctx->active) {
|
||||||
|
int freed;
|
||||||
|
|
||||||
|
shsess_tree_delete(&b->data.session);
|
||||||
|
freed = shsess_free(&b->data.session);
|
||||||
|
if (!head)
|
||||||
|
data_len -= sizeof(b->data.session.data) + (freed-1)*sizeof(b->data.data);
|
||||||
|
else
|
||||||
|
data_len -= freed*sizeof(b->data.data);
|
||||||
|
if (data_len <= 0)
|
||||||
|
return &shctx->free.n->data.session;
|
||||||
|
b = shctx->active.n;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* store a session into the cache
|
||||||
|
* s_id : session id padded with zero to SSL_MAX_SSL_SESSION_ID_LENGTH
|
||||||
|
* data: asn1 encoded session
|
||||||
|
* data_len: asn1 encoded session length
|
||||||
|
* Returns 1 id session was stored (else 0)
|
||||||
|
*/
|
||||||
|
static int shsess_store(unsigned char *s_id, unsigned char *data, int data_len)
|
||||||
|
{
|
||||||
|
struct shared_session *shsess, *oldshsess;
|
||||||
|
|
||||||
|
shsess = shsess_get_next(data_len);
|
||||||
|
if (!shsess) {
|
||||||
|
/* Could not retrieve enough free blocks to store that session */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* prepare key */
|
||||||
|
memcpy(shsess->key_data, s_id, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
||||||
|
|
||||||
|
/* it returns the already existing node
|
||||||
|
or current node if none, never returns null */
|
||||||
|
oldshsess = shsess_tree_insert(shsess);
|
||||||
|
if (oldshsess != shsess) {
|
||||||
|
/* free all blocks used by old node */
|
||||||
|
shsess_free(oldshsess);
|
||||||
|
shsess = oldshsess;
|
||||||
|
}
|
||||||
|
|
||||||
|
((struct shared_block *)shsess)->data_len = data_len;
|
||||||
|
if (data_len <= sizeof(shsess->data)) {
|
||||||
|
/* Store on a single block */
|
||||||
|
memcpy(shsess->data, data, data_len);
|
||||||
|
shblock_set_active((struct shared_block *)shsess);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned char *p;
|
||||||
|
/* Store on multiple blocks */
|
||||||
|
int cur_len;
|
||||||
|
|
||||||
|
memcpy(shsess->data, data, sizeof(shsess->data));
|
||||||
|
p = data + sizeof(shsess->data);
|
||||||
|
cur_len = data_len - sizeof(shsess->data);
|
||||||
|
shblock_set_active((struct shared_block *)shsess);
|
||||||
|
while (1) {
|
||||||
|
/* Store next data on free block.
|
||||||
|
* shsess_get_next guarantees that there are enough
|
||||||
|
* free blocks in queue.
|
||||||
|
*/
|
||||||
|
struct shared_block *block;
|
||||||
|
|
||||||
|
block = shctx->free.n;
|
||||||
|
if (cur_len <= sizeof(block->data)) {
|
||||||
|
/* This is the last block */
|
||||||
|
block->data_len = cur_len;
|
||||||
|
memcpy(block->data.data, p, cur_len);
|
||||||
|
shblock_set_active(block);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Intermediate block */
|
||||||
|
block->data_len = cur_len;
|
||||||
|
memcpy(block->data.data, p, sizeof(block->data));
|
||||||
|
p += sizeof(block->data.data);
|
||||||
|
cur_len -= sizeof(block->data.data);
|
||||||
|
shblock_set_active(block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* SSL context callbacks */
|
/* SSL context callbacks */
|
||||||
@ -198,51 +347,43 @@ static inline void _shared_context_unlock(void)
|
|||||||
/* SSL callback used on new session creation */
|
/* SSL callback used on new session creation */
|
||||||
int shctx_new_cb(SSL *ssl, SSL_SESSION *sess)
|
int shctx_new_cb(SSL *ssl, SSL_SESSION *sess)
|
||||||
{
|
{
|
||||||
struct shared_session *shsess;
|
unsigned char encsess[sizeof(struct shsess_packet)+SHSESS_MAX_DATA_LEN];
|
||||||
unsigned char *data,*p;
|
struct shsess_packet *packet = (struct shsess_packet *)encsess;
|
||||||
unsigned int data_len;
|
unsigned char *p;
|
||||||
unsigned char encsess[SHSESS_MAX_ENCODED_LEN];
|
int data_len, sid_length;
|
||||||
(void)ssl;
|
|
||||||
|
|
||||||
/* check if session reserved size in aligned buffer is large enougth for the ASN1 encode session */
|
|
||||||
data_len=i2d_SSL_SESSION(sess, NULL);
|
|
||||||
if(data_len > SHSESS_MAX_DATA_LEN)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* process ASN1 session encoding before the lock: lower cost */
|
/* Session id is already stored in to key and session id is known
|
||||||
p = data = encsess+SSL_MAX_SSL_SESSION_ID_LENGTH;
|
* so we dont store it to keep size.
|
||||||
|
*/
|
||||||
|
sid_length = sess->session_id_length;
|
||||||
|
sess->session_id_length = 0;
|
||||||
|
sess->sid_ctx_length = 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;
|
||||||
|
|
||||||
|
/* process ASN1 session encoding before the lock */
|
||||||
|
p = packet->data;
|
||||||
i2d_SSL_SESSION(sess, &p);
|
i2d_SSL_SESSION(sess, &p);
|
||||||
|
|
||||||
|
memcpy(packet->hdr.id, sess->session_id, sid_length);
|
||||||
|
if (sid_length < SSL_MAX_SSL_SESSION_ID_LENGTH)
|
||||||
|
memset(&packet->hdr.id[sid_length], 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sid_length);
|
||||||
|
|
||||||
shared_context_lock();
|
shared_context_lock();
|
||||||
|
|
||||||
shsess = shsess_get_next();
|
/* store to cache */
|
||||||
|
shsess_store(packet->hdr.id, packet->data, data_len);
|
||||||
shsess_tree_delete(shsess);
|
|
||||||
|
|
||||||
shsess_set_key(shsess, sess->session_id, sess->session_id_length);
|
|
||||||
|
|
||||||
/* it returns the already existing node or current node if none, never returns null */
|
|
||||||
shsess = shsess_tree_insert(shsess);
|
|
||||||
|
|
||||||
/* store ASN1 encoded session into cache */
|
|
||||||
shsess->data_len = data_len;
|
|
||||||
memcpy(shsess->data, data, data_len);
|
|
||||||
|
|
||||||
/* store creation date */
|
|
||||||
shsess->c_date = SSL_SESSION_get_time(sess);
|
|
||||||
|
|
||||||
shsess_set_active(shsess);
|
|
||||||
|
|
||||||
shared_context_unlock();
|
shared_context_unlock();
|
||||||
|
|
||||||
if (shared_session_new_cbk) { /* if user level callback is set */
|
err:
|
||||||
/* copy sessionid padded with 0 into the sessionid + data aligned buffer */
|
/* reset original length values */
|
||||||
memcpy(encsess, sess->session_id, sess->session_id_length);
|
sess->sid_ctx_length = ssl->sid_ctx_length;
|
||||||
if (sess->session_id_length < SSL_MAX_SSL_SESSION_ID_LENGTH)
|
sess->session_id_length = sid_length;
|
||||||
memset(encsess+sess->session_id_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH-sess->session_id_length);
|
|
||||||
|
|
||||||
shared_session_new_cbk(encsess, SSL_MAX_SSL_SESSION_ID_LENGTH+data_len, SSL_SESSION_get_time(sess));
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; /* do not increment session reference count */
|
return 0; /* do not increment session reference count */
|
||||||
}
|
}
|
||||||
@ -253,10 +394,8 @@ SSL_SESSION *shctx_get_cb(SSL *ssl, unsigned char *key, int key_len, int *do_cop
|
|||||||
struct shared_session *shsess;
|
struct shared_session *shsess;
|
||||||
unsigned char data[SHSESS_MAX_DATA_LEN], *p;
|
unsigned char data[SHSESS_MAX_DATA_LEN], *p;
|
||||||
unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
unsigned char tmpkey[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
||||||
unsigned int data_len;
|
int data_len;
|
||||||
long cdate;
|
|
||||||
SSL_SESSION *sess;
|
SSL_SESSION *sess;
|
||||||
(void)ssl;
|
|
||||||
|
|
||||||
/* allow the session to be freed automatically by openssl */
|
/* allow the session to be freed automatically by openssl */
|
||||||
*do_copy = 0;
|
*do_copy = 0;
|
||||||
@ -279,24 +418,52 @@ SSL_SESSION *shctx_get_cb(SSL *ssl, unsigned char *key, int key_len, int *do_cop
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* backup creation date to reset in session after ASN1 decode */
|
data_len = ((struct shared_block *)shsess)->data_len;
|
||||||
cdate = shsess->c_date;
|
if (data_len <= sizeof(shsess->data)) {
|
||||||
|
/* Session stored on single block */
|
||||||
|
memcpy(data, shsess->data, data_len);
|
||||||
|
shblock_set_active((struct shared_block *)shsess);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Session stored on multiple blocks */
|
||||||
|
struct shared_block *block;
|
||||||
|
|
||||||
/* copy ASN1 session data to decode outside the lock */
|
memcpy(data, shsess->data, sizeof(shsess->data));
|
||||||
data_len = shsess->data_len;
|
p = data + sizeof(shsess->data);
|
||||||
memcpy(data, shsess->data, shsess->data_len);
|
block = ((struct shared_block *)shsess)->n;
|
||||||
|
shblock_set_active((struct shared_block *)shsess);
|
||||||
|
while (1) {
|
||||||
|
/* Retrieve data from next block */
|
||||||
|
struct shared_block *next;
|
||||||
|
|
||||||
shsess_set_active(shsess);
|
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(block);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Intermediate block */
|
||||||
|
memcpy(p, block->data.data, sizeof(block->data.data));
|
||||||
|
p += sizeof(block->data.data);
|
||||||
|
next = block->n;
|
||||||
|
shblock_set_active(block);
|
||||||
|
block = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
shared_context_unlock();
|
shared_context_unlock();
|
||||||
|
|
||||||
/* decode ASN1 session */
|
/* decode ASN1 session */
|
||||||
p = data;
|
p = data;
|
||||||
sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len);
|
sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len);
|
||||||
|
/* Reset session id and session id contenxt */
|
||||||
/* reset creation date */
|
if (sess) {
|
||||||
if (sess)
|
memcpy(sess->session_id, key, key_len);
|
||||||
SSL_SESSION_set_time(sess, cdate);
|
sess->session_id_length = key_len;
|
||||||
|
memcpy(sess->sid_ctx, ssl->sid_ctx, ssl->sid_ctx_length);
|
||||||
|
sess->sid_ctx_length = ssl->sid_ctx_length;
|
||||||
|
}
|
||||||
|
|
||||||
return sess;
|
return sess;
|
||||||
}
|
}
|
||||||
@ -321,59 +488,21 @@ void shctx_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess)
|
|||||||
/* lookup for session */
|
/* lookup for session */
|
||||||
shsess = shsess_tree_lookup(key);
|
shsess = shsess_tree_lookup(key);
|
||||||
if (shsess) {
|
if (shsess) {
|
||||||
shsess_set_free(shsess);
|
/* free session */
|
||||||
|
shsess_tree_delete(shsess);
|
||||||
|
shsess_free(shsess);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* unlock cache */
|
/* unlock cache */
|
||||||
shared_context_unlock();
|
shared_context_unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* User level function called to add a session to the cache (remote updates) */
|
|
||||||
void shctx_sess_add(const unsigned char *encsess, unsigned int len, long cdate)
|
|
||||||
{
|
|
||||||
struct shared_session *shsess;
|
|
||||||
|
|
||||||
/* check buffer is at least padded key long + 1 byte
|
|
||||||
and data_len not too long */
|
|
||||||
if ((len <= SSL_MAX_SSL_SESSION_ID_LENGTH)
|
|
||||||
|| (len > SHSESS_MAX_DATA_LEN+SSL_MAX_SSL_SESSION_ID_LENGTH))
|
|
||||||
return;
|
|
||||||
|
|
||||||
shared_context_lock();
|
|
||||||
|
|
||||||
shsess = shsess_get_next();
|
|
||||||
|
|
||||||
shsess_tree_delete(shsess);
|
|
||||||
|
|
||||||
shsess_set_key(shsess, encsess, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
|
||||||
|
|
||||||
/* it returns the already existing node or current node if none, never returns null */
|
|
||||||
shsess = shsess_tree_insert(shsess);
|
|
||||||
|
|
||||||
/* store into cache and update earlier on session get events */
|
|
||||||
if (cdate)
|
|
||||||
shsess->c_date = (long)cdate;
|
|
||||||
|
|
||||||
/* copy ASN1 session data into cache */
|
|
||||||
shsess->data_len = len-SSL_MAX_SSL_SESSION_ID_LENGTH;
|
|
||||||
memcpy(shsess->data, encsess+SSL_MAX_SSL_SESSION_ID_LENGTH, shsess->data_len);
|
|
||||||
|
|
||||||
shsess_set_active(shsess);
|
|
||||||
|
|
||||||
shared_context_unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Function used to set a callback on new session creation */
|
|
||||||
void shsess_set_new_cbk(void (*func)(unsigned char *, unsigned int, long))
|
|
||||||
{
|
|
||||||
shared_session_new_cbk = func;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate shared memory context.
|
/* Allocate shared memory context.
|
||||||
* size is maximum cached sessions.
|
* <size> is maximum cached sessions.
|
||||||
* if set less or equal to 0, SHCTX_DEFAULT_SIZE is used.
|
* If <size> is set to less or equal to 0, SHCTX_DEFAULT_SIZE is used.
|
||||||
* Returns: -1 on alloc failure, size if it performs context alloc,
|
* Returns: -1 on alloc failure, <size> if it performs context alloc,
|
||||||
* and 0 if cache is already allocated */
|
* and 0 if cache is already allocated.
|
||||||
|
*/
|
||||||
int shared_context_init(int size, int shared)
|
int shared_context_init(int size, int shared)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@ -382,7 +511,7 @@ int shared_context_init(int size, int shared)
|
|||||||
pthread_mutexattr_t attr;
|
pthread_mutexattr_t attr;
|
||||||
#endif /* USE_SYSCALL_FUTEX */
|
#endif /* USE_SYSCALL_FUTEX */
|
||||||
#endif
|
#endif
|
||||||
struct shared_session *prev,*cur;
|
struct shared_block *prev,*cur;
|
||||||
int maptype = MAP_PRIVATE;
|
int maptype = MAP_PRIVATE;
|
||||||
|
|
||||||
if (shctx)
|
if (shctx)
|
||||||
@ -391,12 +520,14 @@ int shared_context_init(int size, int shared)
|
|||||||
if (size<=0)
|
if (size<=0)
|
||||||
size = SHCTX_DEFAULT_SIZE;
|
size = SHCTX_DEFAULT_SIZE;
|
||||||
|
|
||||||
|
/* Increate size by one to reserve one node for lookup */
|
||||||
|
size++;
|
||||||
#ifndef USE_PRIVATE_CACHE
|
#ifndef USE_PRIVATE_CACHE
|
||||||
if (shared)
|
if (shared)
|
||||||
maptype = MAP_SHARED;
|
maptype = MAP_SHARED;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
shctx = (struct shared_context *)mmap(NULL, sizeof(struct shared_context)+(size*sizeof(struct shared_session)),
|
shctx = (struct shared_context *)mmap(NULL, sizeof(struct shared_context)+(size*sizeof(struct shared_block)),
|
||||||
PROT_READ | PROT_WRITE, maptype | MAP_ANON, -1, 0);
|
PROT_READ | PROT_WRITE, maptype | MAP_ANON, -1, 0);
|
||||||
if (!shctx || shctx == MAP_FAILED) {
|
if (!shctx || shctx == MAP_FAILED) {
|
||||||
shctx = NULL;
|
shctx = NULL;
|
||||||
@ -415,12 +546,16 @@ int shared_context_init(int size, int shared)
|
|||||||
use_shared_mem = 1;
|
use_shared_mem = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
memset(&shctx->active.key, 0, sizeof(struct ebmb_node));
|
memset(&shctx->active.data.session.key, 0, sizeof(struct ebmb_node));
|
||||||
memset(&shctx->free.key, 0, sizeof(struct ebmb_node));
|
memset(&shctx->free.data.session.key, 0, sizeof(struct ebmb_node));
|
||||||
|
|
||||||
/* No duplicate authorized in tree: */
|
/* No duplicate authorized in tree: */
|
||||||
//shctx->active.key.node.branches.b[1] = (void *)1;
|
shctx->active.data.session.key.node.branches = EB_ROOT_UNIQUE;
|
||||||
shctx->active.key.node.branches = EB_ROOT_UNIQUE;
|
|
||||||
|
/* Init remote update cache */
|
||||||
|
shctx->upd.eol = 0;
|
||||||
|
shctx->upd.seq = 0;
|
||||||
|
shctx->data_len = 0;
|
||||||
|
|
||||||
cur = &shctx->active;
|
cur = &shctx->active;
|
||||||
cur->n = cur->p = cur;
|
cur->n = cur->p = cur;
|
||||||
@ -428,7 +563,7 @@ int shared_context_init(int size, int shared)
|
|||||||
cur = &shctx->free;
|
cur = &shctx->free;
|
||||||
for (i = 0 ; i < size ; i++) {
|
for (i = 0 ; i < size ; i++) {
|
||||||
prev = cur;
|
prev = cur;
|
||||||
cur = (struct shared_session *)((char *)prev + sizeof(struct shared_session));
|
cur = (struct shared_block *)((char *)prev + sizeof(struct shared_block));
|
||||||
prev->n = cur;
|
prev->n = cur;
|
||||||
cur->p = prev;
|
cur->p = prev;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user