2012-09-03 09:14:36 +00:00
|
|
|
/*
|
|
|
|
* shctx.c - shared context management functions for SSL
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011-2012 EXCELIANCE
|
|
|
|
*
|
|
|
|
* Author: Emeric Brun - emeric@exceliance.fr
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version
|
|
|
|
* 2 of the License, or (at your option) any later version.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/mman.h>
|
2012-09-25 09:11:16 +00:00
|
|
|
#ifndef USE_PRIVATE_CACHE
|
2012-09-03 09:14:36 +00:00
|
|
|
#ifdef USE_SYSCALL_FUTEX
|
|
|
|
#include <unistd.h>
|
2012-09-04 10:26:26 +00:00
|
|
|
#ifndef u32
|
|
|
|
#define u32 unsigned int
|
|
|
|
#endif
|
2012-09-03 09:14:36 +00:00
|
|
|
#include <linux/futex.h>
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
#else /* USE_SYSCALL_FUTEX */
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif /* USE_SYSCALL_FUTEX */
|
2012-09-25 09:11:16 +00:00
|
|
|
#endif
|
2012-11-28 17:47:52 +00:00
|
|
|
#include <arpa/inet.h>
|
2012-09-03 09:14:36 +00:00
|
|
|
#include "ebmbtree.h"
|
|
|
|
#include "proto/shctx.h"
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
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];
|
|
|
|
};
|
|
|
|
|
2012-09-03 09:14:36 +00:00
|
|
|
struct shared_session {
|
|
|
|
struct ebmb_node key;
|
|
|
|
unsigned char key_data[SSL_MAX_SSL_SESSION_ID_LENGTH];
|
2012-11-28 17:47:52 +00:00
|
|
|
unsigned char data[SHSESS_BLOCK_MIN_SIZE];
|
2012-09-03 09:14:36 +00:00
|
|
|
};
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
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;
|
|
|
|
};
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
struct shared_context {
|
2012-09-25 09:11:16 +00:00
|
|
|
#ifndef USE_PRIVATE_CACHE
|
2012-09-03 09:14:36 +00:00
|
|
|
#ifdef USE_SYSCALL_FUTEX
|
|
|
|
unsigned int waiters;
|
|
|
|
#else /* USE_SYSCALL_FUTEX */
|
|
|
|
pthread_mutex_t mutex;
|
2012-09-25 09:11:16 +00:00
|
|
|
#endif
|
2012-09-03 09:14:36 +00:00
|
|
|
#endif
|
2012-11-28 17:47:52 +00:00
|
|
|
struct shsess_packet_hdr upd;
|
|
|
|
unsigned char data[SHSESS_MAX_DATA_LEN];
|
|
|
|
short int data_len;
|
|
|
|
struct shared_block active;
|
|
|
|
struct shared_block free;
|
2012-09-03 09:14:36 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Static shared context */
|
|
|
|
static struct shared_context *shctx = NULL;
|
2012-09-25 09:11:16 +00:00
|
|
|
#ifndef USE_PRIVATE_CACHE
|
2012-09-24 13:48:52 +00:00
|
|
|
static int use_shared_mem = 0;
|
2012-09-25 09:11:16 +00:00
|
|
|
#endif
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
/* Lock functions */
|
2012-09-25 09:11:16 +00:00
|
|
|
#ifdef USE_PRIVATE_CACHE
|
2012-10-18 13:11:52 +00:00
|
|
|
#define shared_context_lock()
|
|
|
|
#define shared_context_unlock()
|
2012-09-25 09:11:16 +00:00
|
|
|
|
|
|
|
#else
|
2012-09-03 09:14:36 +00:00
|
|
|
#ifdef USE_SYSCALL_FUTEX
|
2012-10-04 15:28:25 +00:00
|
|
|
#if defined (__i486__) || defined (__i586__) || defined (__i686__) || defined (__x86_64__)
|
2012-09-03 09:14:36 +00:00
|
|
|
static inline unsigned int xchg(unsigned int *ptr, unsigned int x)
|
|
|
|
{
|
|
|
|
__asm volatile("lock xchgl %0,%1"
|
|
|
|
: "=r" (x), "+m" (*ptr)
|
|
|
|
: "0" (x)
|
|
|
|
: "memory");
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int cmpxchg(unsigned int *ptr, unsigned int old, unsigned int new)
|
|
|
|
{
|
|
|
|
unsigned int ret;
|
|
|
|
|
|
|
|
__asm volatile("lock cmpxchgl %2,%1"
|
|
|
|
: "=a" (ret), "+m" (*ptr)
|
|
|
|
: "r" (new), "0" (old)
|
|
|
|
: "memory");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned char atomic_dec(unsigned int *ptr)
|
|
|
|
{
|
|
|
|
unsigned char ret;
|
|
|
|
__asm volatile("lock decl %0\n"
|
|
|
|
"setne %1\n"
|
|
|
|
: "+m" (*ptr), "=qm" (ret)
|
|
|
|
:
|
|
|
|
: "memory");
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* if no x86_64 or i586 arch: use less optimized gcc >= 4.1 built-ins */
|
|
|
|
static inline unsigned int xchg(unsigned int *ptr, unsigned int x)
|
|
|
|
{
|
|
|
|
return __sync_lock_test_and_set(ptr, x);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned int cmpxchg(unsigned int *ptr, unsigned int old, unsigned int new)
|
|
|
|
{
|
|
|
|
return __sync_val_compare_and_swap(ptr, old, new);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline unsigned char atomic_dec(unsigned int *ptr)
|
|
|
|
{
|
|
|
|
return __sync_sub_and_fetch(ptr, 1) ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2012-09-24 13:48:52 +00:00
|
|
|
static inline void _shared_context_lock(void)
|
2012-09-03 09:14:36 +00:00
|
|
|
{
|
|
|
|
unsigned int x;
|
|
|
|
|
|
|
|
x = cmpxchg(&shctx->waiters, 0, 1);
|
|
|
|
if (x) {
|
|
|
|
if (x != 2)
|
|
|
|
x = xchg(&shctx->waiters, 2);
|
|
|
|
|
|
|
|
while (x) {
|
|
|
|
syscall(SYS_futex, &shctx->waiters, FUTEX_WAIT, 2, NULL, 0, 0);
|
|
|
|
x = xchg(&shctx->waiters, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-09-24 13:48:52 +00:00
|
|
|
static inline void _shared_context_unlock(void)
|
2012-09-03 09:14:36 +00:00
|
|
|
{
|
|
|
|
if (atomic_dec(&shctx->waiters)) {
|
|
|
|
shctx->waiters = 0;
|
|
|
|
syscall(SYS_futex, &shctx->waiters, FUTEX_WAKE, 1, NULL, 0, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-18 13:11:52 +00:00
|
|
|
#define shared_context_lock() if (use_shared_mem) _shared_context_lock()
|
2012-09-24 13:48:52 +00:00
|
|
|
|
2012-10-18 13:11:52 +00:00
|
|
|
#define shared_context_unlock() if (use_shared_mem) _shared_context_unlock()
|
2012-09-24 13:48:52 +00:00
|
|
|
|
2012-09-03 09:14:36 +00:00
|
|
|
#else /* USE_SYSCALL_FUTEX */
|
|
|
|
|
2012-10-18 13:11:52 +00:00
|
|
|
#define shared_context_lock() if (use_shared_mem) pthread_mutex_lock(&shctx->mutex)
|
2012-09-24 13:48:52 +00:00
|
|
|
|
2012-10-18 13:11:52 +00:00
|
|
|
#define shared_context_unlock() if (use_shared_mem) pthread_mutex_unlock(&shctx->mutex)
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-09-25 09:11:16 +00:00
|
|
|
#endif
|
2012-09-03 09:14:36 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* List Macros */
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
#define shblock_unset(s) (s)->n->p = (s)->p; \
|
2012-09-03 09:14:36 +00:00
|
|
|
(s)->p->n = (s)->n;
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
#define shblock_set_free(s) shblock_unset(s) \
|
|
|
|
(s)->n = &shctx->free; \
|
|
|
|
(s)->p = shctx->free.p; \
|
|
|
|
shctx->free.p->n = s; \
|
|
|
|
shctx->free.p = s;
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
#define shblock_set_active(s) shblock_unset(s) \
|
|
|
|
(s)->n = &shctx->active; \
|
|
|
|
(s)->p = shctx->active.p; \
|
|
|
|
shctx->active.p->n = s; \
|
|
|
|
shctx->active.p = s;
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Tree Macros */
|
|
|
|
|
|
|
|
#define shsess_tree_delete(s) ebmb_delete(&(s)->key);
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
#define shsess_tree_insert(s) (struct shared_session *)ebmb_insert(&shctx->active.data.session.key.node.branches, \
|
2012-09-03 09:14:36 +00:00
|
|
|
&(s)->key, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
#define shsess_tree_lookup(k) (struct shared_session *)ebmb_lookup(&shctx->active.data.session.key.node.branches, \
|
2012-09-03 09:14:36 +00:00
|
|
|
(k), SSL_MAX_SSL_SESSION_ID_LENGTH);
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* shared session functions */
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* Free session blocks, returns number of freed blocks */
|
|
|
|
static int shsess_free(struct shared_session *shsess)
|
|
|
|
{
|
|
|
|
struct shared_block *block;
|
|
|
|
int ret = 1;
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
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;
|
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* 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)
|
2012-09-03 09:14:36 +00:00
|
|
|
{
|
2012-11-28 17:47:52 +00:00
|
|
|
struct shared_session *shsess, *oldshsess;
|
|
|
|
|
|
|
|
shsess = shsess_get_next(data_len);
|
|
|
|
if (!shsess) {
|
|
|
|
/* Could not retrieve enough free blocks to store that session */
|
2012-09-03 09:14:36 +00:00
|
|
|
return 0;
|
2012-11-28 17:47:52 +00:00
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* prepare key */
|
|
|
|
memcpy(shsess->key_data, s_id, SSL_MAX_SSL_SESSION_ID_LENGTH);
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
((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);
|
|
|
|
}
|
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
return 1;
|
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* SSL context callbacks */
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* SSL callback used on new session creation */
|
|
|
|
int shctx_new_cb(SSL *ssl, SSL_SESSION *sess)
|
|
|
|
{
|
|
|
|
unsigned char encsess[sizeof(struct shsess_packet)+SHSESS_MAX_DATA_LEN];
|
|
|
|
struct shsess_packet *packet = (struct shsess_packet *)encsess;
|
|
|
|
unsigned char *p;
|
2013-04-26 16:56:49 +00:00
|
|
|
int data_len, sid_length, sid_ctx_length;
|
2012-11-28 17:47:52 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* Session id is already stored in to key and session id is known
|
|
|
|
* so we dont store it to keep size.
|
|
|
|
*/
|
|
|
|
sid_length = sess->session_id_length;
|
|
|
|
sess->session_id_length = 0;
|
2013-04-26 16:56:49 +00:00
|
|
|
sid_ctx_length = sess->sid_ctx_length;
|
2012-11-28 17:47:52 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
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);
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
shared_context_lock();
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* store to cache */
|
|
|
|
shsess_store(packet->hdr.id, packet->data, data_len);
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
shared_context_unlock();
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
err:
|
|
|
|
/* reset original length values */
|
|
|
|
sess->session_id_length = sid_length;
|
2013-04-26 16:56:49 +00:00
|
|
|
sess->sid_ctx_length = sid_ctx_length;
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
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, 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];
|
2012-11-28 17:47:52 +00:00
|
|
|
int data_len;
|
2012-09-03 09:14:36 +00:00
|
|
|
SSL_SESSION *sess;
|
|
|
|
|
|
|
|
/* 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();
|
|
|
|
|
|
|
|
/* lookup for session */
|
|
|
|
shsess = shsess_tree_lookup(key);
|
|
|
|
if (!shsess) {
|
|
|
|
/* no session found: unlock cache and exit */
|
|
|
|
shared_context_unlock();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
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((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((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(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;
|
|
|
|
}
|
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
shared_context_unlock();
|
|
|
|
|
|
|
|
/* decode ASN1 session */
|
|
|
|
p = data;
|
|
|
|
sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len);
|
2012-11-28 17:47:52 +00:00
|
|
|
/* Reset session id and session id contenxt */
|
|
|
|
if (sess) {
|
|
|
|
memcpy(sess->session_id, key, key_len);
|
|
|
|
sess->session_id_length = key_len;
|
2013-04-26 16:56:49 +00:00
|
|
|
memcpy(sess->sid_ctx, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME));
|
2012-11-28 17:47:52 +00:00
|
|
|
sess->sid_ctx_length = ssl->sid_ctx_length;
|
|
|
|
}
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
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 char *key = sess->session_id;
|
|
|
|
(void)ctx;
|
|
|
|
|
|
|
|
/* tree key is zeros padded sessionid */
|
|
|
|
if (sess->session_id_length < SSL_MAX_SSL_SESSION_ID_LENGTH) {
|
|
|
|
memcpy(tmpkey, sess->session_id, sess->session_id_length);
|
|
|
|
memset(tmpkey+sess->session_id_length, 0, SSL_MAX_SSL_SESSION_ID_LENGTH - sess->session_id_length);
|
|
|
|
key = tmpkey;
|
|
|
|
}
|
|
|
|
|
|
|
|
shared_context_lock();
|
|
|
|
|
|
|
|
/* lookup for session */
|
|
|
|
shsess = shsess_tree_lookup(key);
|
|
|
|
if (shsess) {
|
2012-11-28 17:47:52 +00:00
|
|
|
/* free session */
|
|
|
|
shsess_tree_delete(shsess);
|
|
|
|
shsess_free(shsess);
|
2012-09-03 09:14:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* unlock cache */
|
|
|
|
shared_context_unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate shared memory context.
|
2012-11-28 17:47:52 +00:00
|
|
|
* <size> is maximum cached sessions.
|
2012-12-28 13:41:32 +00:00
|
|
|
* If <size> is set to less or equal to 0, ssl cache is disabled.
|
2012-11-28 17:47:52 +00:00
|
|
|
* Returns: -1 on alloc failure, <size> if it performs context alloc,
|
|
|
|
* and 0 if cache is already allocated.
|
|
|
|
*/
|
2012-09-24 13:48:52 +00:00
|
|
|
int shared_context_init(int size, int shared)
|
2012-09-03 09:14:36 +00:00
|
|
|
{
|
|
|
|
int i;
|
2012-09-25 09:11:16 +00:00
|
|
|
#ifndef USE_PRIVATE_CACHE
|
2012-09-03 09:14:36 +00:00
|
|
|
#ifndef USE_SYSCALL_FUTEX
|
|
|
|
pthread_mutexattr_t attr;
|
|
|
|
#endif /* USE_SYSCALL_FUTEX */
|
2012-09-25 09:11:16 +00:00
|
|
|
#endif
|
2012-11-28 17:47:52 +00:00
|
|
|
struct shared_block *prev,*cur;
|
2012-09-24 13:48:52 +00:00
|
|
|
int maptype = MAP_PRIVATE;
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
if (shctx)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (size<=0)
|
2012-12-28 13:41:32 +00:00
|
|
|
return 0;
|
2012-09-03 09:14:36 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
/* Increate size by one to reserve one node for lookup */
|
|
|
|
size++;
|
2012-09-25 09:11:16 +00:00
|
|
|
#ifndef USE_PRIVATE_CACHE
|
2012-09-24 13:48:52 +00:00
|
|
|
if (shared)
|
|
|
|
maptype = MAP_SHARED;
|
2012-09-25 09:11:16 +00:00
|
|
|
#endif
|
2012-09-24 13:48:52 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
shctx = (struct shared_context *)mmap(NULL, sizeof(struct shared_context)+(size*sizeof(struct shared_block)),
|
2012-09-24 13:48:52 +00:00
|
|
|
PROT_READ | PROT_WRITE, maptype | MAP_ANON, -1, 0);
|
2012-09-03 09:14:36 +00:00
|
|
|
if (!shctx || shctx == MAP_FAILED) {
|
|
|
|
shctx = NULL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-09-25 09:11:16 +00:00
|
|
|
#ifndef USE_PRIVATE_CACHE
|
2012-09-03 09:14:36 +00:00
|
|
|
#ifdef USE_SYSCALL_FUTEX
|
|
|
|
shctx->waiters = 0;
|
|
|
|
#else
|
|
|
|
pthread_mutexattr_init(&attr);
|
|
|
|
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
|
|
|
pthread_mutex_init(&shctx->mutex, &attr);
|
|
|
|
#endif
|
2012-09-24 13:48:52 +00:00
|
|
|
if (maptype == MAP_SHARED)
|
|
|
|
use_shared_mem = 1;
|
2012-09-25 09:11:16 +00:00
|
|
|
#endif
|
2012-09-24 13:48:52 +00:00
|
|
|
|
2012-11-28 17:47:52 +00:00
|
|
|
memset(&shctx->active.data.session.key, 0, sizeof(struct ebmb_node));
|
|
|
|
memset(&shctx->free.data.session.key, 0, sizeof(struct ebmb_node));
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
/* No duplicate authorized in tree: */
|
2012-11-28 17:47:52 +00:00
|
|
|
shctx->active.data.session.key.node.branches = EB_ROOT_UNIQUE;
|
|
|
|
|
|
|
|
/* Init remote update cache */
|
|
|
|
shctx->upd.eol = 0;
|
|
|
|
shctx->upd.seq = 0;
|
|
|
|
shctx->data_len = 0;
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
cur = &shctx->active;
|
|
|
|
cur->n = cur->p = cur;
|
|
|
|
|
|
|
|
cur = &shctx->free;
|
|
|
|
for (i = 0 ; i < size ; i++) {
|
|
|
|
prev = cur;
|
2012-11-28 17:47:52 +00:00
|
|
|
cur = (struct shared_block *)((char *)prev + sizeof(struct shared_block));
|
2012-09-03 09:14:36 +00:00
|
|
|
prev->n = cur;
|
|
|
|
cur->p = prev;
|
|
|
|
}
|
|
|
|
cur->n = &shctx->free;
|
|
|
|
shctx->free.p = cur;
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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)
|
|
|
|
{
|
2012-11-26 17:37:12 +00:00
|
|
|
SSL_CTX_set_session_id_context(ctx, (const unsigned char *)SHCTX_APPNAME, strlen(SHCTX_APPNAME));
|
|
|
|
|
2012-12-28 13:41:32 +00:00
|
|
|
if (!shctx) {
|
|
|
|
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
2012-09-03 09:14:36 +00:00
|
|
|
return;
|
2012-12-28 13:41:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER |
|
|
|
|
SSL_SESS_CACHE_NO_INTERNAL |
|
|
|
|
SSL_SESS_CACHE_NO_AUTO_CLEAR);
|
2012-09-03 09:14:36 +00:00
|
|
|
|
|
|
|
/* 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);
|
|
|
|
}
|