"Fossies" - the Fresh Open Source Software Archive

Member "hitch-1.5.2/src/shctx.c" (26 Nov 2019, 9573 Bytes) of package /linux/www/hitch-1.5.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "shctx.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5.0_vs_1.5.1.

    1 /*
    2  * shctx.c
    3  *
    4  * Copyright (C) 2011 EXCELIANCE
    5  *
    6  * Author: Emeric Brun - emeric@exceliance.fr
    7  *
    8  */
    9 
   10 #include "config.h"
   11 
   12 #include <sys/mman.h>
   13 
   14 #ifdef USE_SYSCALL_FUTEX
   15 #  include <unistd.h>
   16 #  include <linux/futex.h>
   17 #  include <sys/syscall.h>
   18 #else
   19 #  include <pthread.h>
   20 #endif
   21 
   22 #include "ebtree/ebmbtree.h"
   23 #include "foreign/vas.h"
   24 #include "shctx.h"
   25 
   26 struct shared_session {
   27     struct ebmb_node    key;
   28     unsigned char       key_data[SSL_MAX_SSL_SESSION_ID_LENGTH];
   29     long            c_date;
   30     int         data_len;
   31     unsigned char       data[SHSESS_MAX_DATA_LEN];
   32     struct shared_session   *p;
   33     struct shared_session   *n;
   34 };
   35 
   36 struct shared_context {
   37 #ifdef USE_SYSCALL_FUTEX
   38     unsigned        waiters;
   39 #else
   40     pthread_mutex_t     mutex;
   41 #endif
   42     struct shared_session   active;
   43     struct shared_session   free;
   44 };
   45 
   46 /* Static shared context */
   47 static struct shared_context *shctx = NULL;
   48 
   49 /* Callbacks */
   50 shsess_new_f *shared_session_new_cbk;
   51 
   52 /* Lock functions */
   53 #ifdef USE_SYSCALL_FUTEX
   54 static inline unsigned
   55 xchg(unsigned *ptr, unsigned x)
   56 {
   57     __asm volatile("lock xchgl %0,%1"
   58              : "=r" (x), "+m" (*ptr)
   59              : "0" (x)
   60              : "memory");
   61     return (x);
   62 }
   63 
   64 static inline unsigned
   65 cmpxchg(unsigned *ptr, unsigned old, unsigned new)
   66 {
   67     unsigned ret;
   68 
   69     __asm volatile("lock cmpxchgl %2,%1"
   70              : "=a" (ret), "+m" (*ptr)
   71              : "r" (new), "0" (old)
   72              : "memory");
   73     return (ret);
   74 }
   75 
   76 static inline unsigned char
   77 atomic_dec(unsigned *ptr)
   78 {
   79     unsigned char ret;
   80     __asm volatile("lock decl %0\n"
   81              "setne %1\n"
   82              : "+m" (*ptr), "=qm" (ret)
   83              :
   84              : "memory");
   85     return (ret);
   86 }
   87 
   88 static inline void
   89 shared_context_lock(void)
   90 {
   91     unsigned x;
   92 
   93     x = cmpxchg(&shctx->waiters, 0, 1);
   94     if (x) {
   95         if (x != 2)
   96             x = xchg(&shctx->waiters, 2);
   97 
   98         while (x) {
   99             syscall(SYS_futex, &shctx->waiters, FUTEX_WAIT, 2, NULL, 0, 0);
  100             x = xchg(&shctx->waiters, 2);
  101         }
  102     }
  103 }
  104 
  105 static inline void
  106 shared_context_unlock(void)
  107 {
  108     if (atomic_dec(&shctx->waiters)) {
  109         shctx->waiters = 0;
  110         syscall(SYS_futex, &shctx->waiters, FUTEX_WAKE, 1, NULL, 0, 0);
  111     }
  112 }
  113 
  114 #else /* USE_SYSCALL_FUTEX */
  115 #  define shared_context_lock(v) pthread_mutex_lock(&shctx->mutex)
  116 #  define shared_context_unlock(v) pthread_mutex_unlock(&shctx->mutex)
  117 #endif
  118 
  119 /* List Macros */
  120 
  121 #define shsess_unset(s)         \
  122     do {                \
  123         (s)->n->p = (s)->p; \
  124         (s)->p->n = (s)->n; \
  125     } while (0)
  126 
  127 #define shsess_set_free(s)      \
  128     do {                \
  129         shsess_unset(s);    \
  130         (s)->p = &shctx->free;  \
  131         (s)->n = shctx->free.n; \
  132         shctx->free.n->p = s;   \
  133         shctx->free.n = s;  \
  134     } while (0)
  135 
  136 
  137 #define shsess_set_active(s)            \
  138     do {                    \
  139         shsess_unset(s);        \
  140         (s)->p = &shctx->active;    \
  141         (s)->n = shctx->active.n;   \
  142         shctx->active.n->p = s;     \
  143         shctx->active.n = s;        \
  144     } while (0)
  145 
  146 
  147 #define shsess_get_next()   \
  148     (shctx->free.p == shctx->free.n ? shctx->active.p : shctx->free.p)
  149 
  150 /* Tree Macros */
  151 
  152 #define shsess_tree_delete(s) ebmb_delete(&(s)->key)
  153 
  154 #define shsess_tree_insert(s) \
  155     (struct shared_session *)ebmb_insert(&shctx->active.key.node.branches, \
  156         &(s)->key, SSL_MAX_SSL_SESSION_ID_LENGTH);
  157 
  158 #define shsess_tree_lookup(k) \
  159     (struct shared_session *)ebmb_lookup(&shctx->active.key.node.branches, \
  160         (k), SSL_MAX_SSL_SESSION_ID_LENGTH);
  161 
  162 /* Copy-with-padding Macros */
  163 
  164 #define shsess_memcpypad(dst, dlen, src, slen)          \
  165     do {                            \
  166         assert((slen) <= (dlen));           \
  167         memcpy((dst), (src), (slen));           \
  168         if ((slen) < (dlen))                \
  169             memset((char *)(dst) + (slen), 0,   \
  170                 (dlen) - (slen));           \
  171     } while (0)
  172 
  173 #define shsess_set_key(s, k, l)                 \
  174     do {                            \
  175         shsess_memcpypad((s)->key_data,         \
  176             SSL_MAX_SSL_SESSION_ID_LENGTH, (k), (l));   \
  177     } while (0)
  178 
  179 /* SSL context callbacks */
  180 
  181 /* SSL callback used on new session creation */
  182 int
  183 shctx_new_cb(SSL *ssl, SSL_SESSION *sess)
  184 {
  185     struct shared_session *shsess;
  186     unsigned char *data,*p;
  187     const unsigned char *key;
  188     unsigned keylen;
  189     unsigned data_len;
  190     unsigned char encsess[SHSESS_MAX_ENCODED_LEN];
  191 
  192     AN(ssl);
  193 
  194     data_len = i2d_SSL_SESSION(sess, NULL);
  195     if (data_len > SHSESS_MAX_DATA_LEN)
  196         return (1);
  197 
  198     /* process ASN1 session encoding before the lock: lower cost */
  199     p = data = encsess+SSL_MAX_SSL_SESSION_ID_LENGTH;
  200     i2d_SSL_SESSION(sess, &p);
  201 
  202     shared_context_lock();
  203 
  204     shsess = shsess_get_next();
  205 
  206     shsess_tree_delete(shsess);
  207 
  208     key = SSL_SESSION_get_id(sess, &keylen);
  209     shsess_set_key(shsess, key, keylen);
  210 
  211     shsess = shsess_tree_insert(shsess);
  212     AN(shsess);
  213 
  214     /* store ASN1 encoded session into cache */
  215     shsess->data_len = data_len;
  216     memcpy(shsess->data, data, data_len);
  217 
  218     /* store creation date */
  219     shsess->c_date = SSL_SESSION_get_time(sess);
  220 
  221     shsess_set_active(shsess);
  222 
  223     shared_context_unlock();
  224 
  225     if (shared_session_new_cbk) { /* if user level callback is set */
  226         shsess_memcpypad(encsess, SSL_MAX_SSL_SESSION_ID_LENGTH,
  227             key, keylen);
  228 
  229         shared_session_new_cbk(encsess,
  230             SSL_MAX_SSL_SESSION_ID_LENGTH + data_len,
  231             SSL_SESSION_get_time(sess));
  232     }
  233 
  234     return (0); /* do not increment session reference count */
  235 }
  236 
  237 /* SSL callback used on lookup an existing session cause none found in internal cache */
  238 #if OPENSSL_VERSION_NUMBER < 0x10100000L
  239 static SSL_SESSION *
  240 shctx_get_cb(SSL *ssl, unsigned char *key, int key_len, int *do_copy)
  241 #else
  242 static SSL_SESSION *
  243 shctx_get_cb(SSL *ssl, const unsigned char *key, int key_len, int *do_copy)
  244 #endif
  245 {
  246     struct shared_session *shsess;
  247     unsigned char data[SHSESS_MAX_DATA_LEN], *p;
  248     unsigned char padded_key[SSL_MAX_SSL_SESSION_ID_LENGTH];
  249     unsigned data_len;
  250     long cdate;
  251     SSL_SESSION *sess;
  252 
  253     AN(ssl);
  254 
  255         /* allow the session to be freed automatically by openssl */
  256     *do_copy = 0;
  257 
  258     shsess_memcpypad(padded_key, sizeof padded_key, key, (size_t)key_len);
  259 
  260     shared_context_lock();
  261 
  262     shsess = shsess_tree_lookup(padded_key);
  263     if(shsess == NULL) {
  264         shared_context_unlock();
  265         return (NULL);
  266     }
  267 
  268     /* backup creation date to reset in session after ASN1 decode */
  269     cdate = shsess->c_date;
  270 
  271     /* copy ASN1 session data to decode outside the lock */
  272     data_len = shsess->data_len;
  273     memcpy(data, shsess->data, shsess->data_len);
  274 
  275     shsess_set_active(shsess);
  276 
  277     shared_context_unlock();
  278 
  279     /* decode ASN1 session */
  280         p = data;
  281     sess = d2i_SSL_SESSION(NULL, (const unsigned char **)&p, data_len);
  282 
  283     /* reset creation date */
  284     if (sess)
  285         SSL_SESSION_set_time(sess, cdate);
  286 
  287     return (sess);
  288 }
  289 
  290 /* SSL callback used to signal session is no more used in internal cache */
  291 void
  292 shctx_remove_cb(SSL_CTX *ctx, SSL_SESSION *sess)
  293 {
  294     struct shared_session *shsess;
  295     unsigned char padded_key[SSL_MAX_SSL_SESSION_ID_LENGTH];
  296     const unsigned char *key;
  297     unsigned keylen;
  298 
  299     AN(ctx);
  300 
  301     key = SSL_SESSION_get_id(sess, &keylen);
  302     shsess_memcpypad(padded_key, sizeof padded_key, key, (size_t)keylen);
  303 
  304     shared_context_lock();
  305 
  306     shsess = shsess_tree_lookup(padded_key);
  307     if (shsess != NULL)
  308         shsess_set_free(shsess);
  309 
  310     /* unlock cache */
  311     shared_context_unlock();
  312 }
  313 
  314 /* User level function called to add a session to the cache (remote updates) */
  315 void
  316 shctx_sess_add(const unsigned char *encsess, unsigned len, long cdate)
  317 {
  318     struct shared_session *shsess;
  319 
  320     /* check buffer is at least padded key long + 1 byte
  321         and data_len not too long */
  322     if (len <= SSL_MAX_SSL_SESSION_ID_LENGTH ||
  323         len > SHSESS_MAX_DATA_LEN + SSL_MAX_SSL_SESSION_ID_LENGTH)
  324         return;
  325 
  326     shared_context_lock();
  327 
  328     shsess = shsess_get_next();
  329     shsess_tree_delete(shsess);
  330     shsess_set_key(shsess, encsess, SSL_MAX_SSL_SESSION_ID_LENGTH);
  331 
  332     shsess = shsess_tree_insert(shsess);
  333     AN(shsess);
  334 
  335     /* store into cache and update earlier on session get events */
  336     if (cdate)
  337         shsess->c_date = (long)cdate;
  338 
  339     /* copy ASN1 session data into cache */
  340     shsess->data_len = len - SSL_MAX_SSL_SESSION_ID_LENGTH;
  341     memcpy(shsess->data, encsess+SSL_MAX_SSL_SESSION_ID_LENGTH, shsess->data_len);
  342 
  343     shsess_set_active(shsess);
  344 
  345     shared_context_unlock();
  346 }
  347 
  348 /* Function used to set a callback on new session creation */
  349 void
  350 shsess_set_new_cbk(shsess_new_f *func)
  351 {
  352 
  353     AN(func);
  354     shared_session_new_cbk = func;
  355 }
  356 
  357 /* Init shared memory context if not allocated and set SSL context callbacks
  358  * size is the max number of stored session
  359  * Returns: -1 on alloc failure, size if performs context alloc, and 0 if just perform
  360  * callbacks registration */
  361 
  362 static int
  363 shared_context_alloc(int size)
  364 {
  365     struct shared_session *prev,*cur;
  366 #ifndef USE_SYSCALL_FUTEX
  367     pthread_mutexattr_t attr;
  368 #endif
  369     int i;
  370 
  371     assert(size > 0);
  372 
  373     shctx = mmap(NULL,
  374         sizeof *shctx + (size * sizeof(struct shared_session)),
  375         PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  376 
  377     if (shctx == MAP_FAILED)
  378         return (-1);
  379 
  380 #ifdef USE_SYSCALL_FUTEX
  381     shctx->waiters = 0;
  382 #else
  383     pthread_mutexattr_init(&attr);
  384     pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
  385     pthread_mutex_init(&shctx->mutex, &attr);
  386 #endif
  387     memset(&shctx->active.key, 0, sizeof(struct ebmb_node));
  388     memset(&shctx->free.key, 0, sizeof(struct ebmb_node));
  389 
  390     /* No duplicate authorized in tree: */
  391     shctx->active.key.node.branches.b[1] = (void *)1;
  392 
  393     cur = &shctx->active;
  394     cur->n = cur->p = cur;
  395 
  396     cur = &shctx->free;
  397     for (i = 0 ; i < size ; i++) {
  398         prev = cur;
  399         cur++;
  400         prev->n = cur;
  401         cur->p = prev;
  402     }
  403     cur->n = &shctx->free;
  404     shctx->free.p = cur;
  405 
  406     return (size);
  407 }
  408 
  409 int
  410 shared_context_init(SSL_CTX *ctx, int size)
  411 {
  412     int ret = 0;
  413 
  414     AN(ctx);
  415 
  416     if (shctx == NULL)
  417         ret = shared_context_alloc(size);
  418 
  419     /* set SSL internal cache size to external cache / 8  + 123 */
  420     SSL_CTX_sess_set_cache_size(ctx, size >> 3 | 0x3ff);
  421 
  422     /* Set callbacks */
  423     SSL_CTX_sess_set_new_cb(ctx, shctx_new_cb);
  424     SSL_CTX_sess_set_get_cb(ctx, shctx_get_cb);
  425     SSL_CTX_sess_set_remove_cb(ctx, shctx_remove_cb);
  426 
  427     return (ret);
  428 }