[Adding dev@apr, with a little abstract]

On Mon, Sep 12, 2016 at 10:31 AM, Ewald Dieterich <ew...@mailbox.org> wrote:
>
> Looks like the problem is this:
>
> * In session_crypto_init() a crypto context is created from a global pool
> (server->pconf).
> * In encrypt_string() and decrypt_string() a key is created from the context
> via apr_crypto_passphrase() using the global pool for allocating memory for
> the key.
> * Multiple threads might access the global pool at the same time.

Unless given an already allocated key, apr_crypto_passphrase() and
apr_crypto_key() will create/allocate the key from the pool of an
apr_crypto_t (the *const* context), not the given pool.

So we tried to allocate the key before the call to apr_crypto_passphrase()...

On Tue, Oct 4, 2016 at 6:21 PM, Yann Ylavic <ylavic....@gmail.com> wrote:
> On Tue, Oct 4, 2016 at 5:29 PM, Graham Leggett <minf...@sharp.fm>
> wrote:
>> On 4 Oct 2016, at 15:47, Paul Spangler <paul.spang...@ni.com>
>> wrote:
>>
>>> From my understanding, apr_crypto_key_t is an opaque struct
>>> defined separately by each crypto provider, so mod_session_crypto
>>> will not be able to do the sizeof.
>>
>> That's a sizeof a pointer to apr_crypto_key_t, not the sizeof
>> apr_crypto_key_t itself.
>
> I think Paul is correct, apr_crypto_passphrase() requires its given
> *(apr_crypto_key_t**)key to be not NULL, otherwise it will allocate
> one from its (providers's) array, which is not thread safe.
>
> How are we supposed to have a *key not NULL given apr_crypto_key_t is
> opaque?

... no way.

>>
>> Keys are read at server start and reused. Trying to regenerate the
>> key on every request has performance implications.

Looks like the heavy operation is creating the context (apr_crypto_t),
not the key, but anyway the keys need to be generated at run time,
taking the salt into account.

So I don't see the point of caching (and allocating) the keys in the
context while we could use the given runtime pool.

If the user wanted some cache s/he could still handle it based on
her/his own pool, but:

> It seems that apr_crypto_passphrase()'s **key is updated for each
> call, though...

so we can't really cache it anyway.

Hence I propose to remove the keys' cache in apr_crypto, and let the
things work with the given pool(s)...

Patch attached, WDYT?

Regards,
Yann.
Index: crypto/apr_crypto_commoncrypto.c
===================================================================
--- crypto/apr_crypto_commoncrypto.c	(revision 1763363)
+++ crypto/apr_crypto_commoncrypto.c	(working copy)
@@ -41,7 +41,6 @@ struct apr_crypto_t
     apr_pool_t *pool;
     const apr_crypto_driver_t *provider;
     apu_err_t *result;
-    apr_array_header_t *keys;
     apr_hash_t *types;
     apr_hash_t *modes;
     apr_random_t *rng;
@@ -206,11 +205,6 @@ static apr_status_t crypto_make(apr_crypto_t **ff,
         return APR_ENOMEM;
     }
 
-    f->keys = apr_array_make(pool, 10, sizeof(apr_crypto_key_t));
-    if (!f->keys) {
-        return APR_ENOMEM;
-    }
-
     f->types = apr_hash_make(pool);
     if (!f->types) {
         return APR_ENOMEM;
@@ -388,7 +382,7 @@ static apr_status_t crypto_key(apr_crypto_key_t **
     apr_crypto_key_t *key = *k;
 
     if (!key) {
-        *k = key = apr_array_push(f->keys);
+        *k = key = apr_pcalloc(p, sizeof *key);
     }
     if (!key) {
         return APR_ENOMEM;
@@ -480,11 +474,11 @@ static apr_status_t crypto_passphrase(apr_crypto_k
     apr_crypto_key_t *key = *k;
 
     if (!key) {
-        *k = key = apr_array_push(f->keys);
+        *k = key = apr_pcalloc(p, sizeof *key);
+        if (!key) {
+            return APR_ENOMEM;
+        }
     }
-    if (!key) {
-        return APR_ENOMEM;
-    }
 
     key->f = f;
     key->provider = f->provider;
Index: crypto/apr_crypto_nss.c
===================================================================
--- crypto/apr_crypto_nss.c	(revision 1763363)
+++ crypto/apr_crypto_nss.c	(working copy)
@@ -50,7 +50,6 @@ struct apr_crypto_t {
     apr_pool_t *pool;
     const apr_crypto_driver_t *provider;
     apu_err_t *result;
-    apr_array_header_t *keys;
     apr_crypto_config_t *config;
     apr_hash_t *types;
     apr_hash_t *modes;
@@ -266,6 +265,15 @@ static apr_status_t crypto_block_cleanup_helper(vo
     return crypto_block_cleanup(block);
 }
 
+static apr_status_t crypto_key_cleanup(void *data)
+{
+    apr_crypto_key_t *key = data;
+    if (key->symKey) {
+        PK11_FreeSymKey(key->symKey);
+        key->symKey = NULL;
+    }
+    return APR_SUCCESS;
+}
 /**
  * @brief Clean encryption / decryption context.
  * @note After cleanup, a context is free to be reused if necessary.
@@ -274,15 +282,6 @@ static apr_status_t crypto_block_cleanup_helper(vo
  */
 static apr_status_t crypto_cleanup(apr_crypto_t *f)
 {
-    apr_crypto_key_t *key;
-    if (f->keys) {
-        while ((key = apr_array_pop(f->keys))) {
-            if (key->symKey) {
-                PK11_FreeSymKey(key->symKey);
-                key->symKey = NULL;
-            }
-        }
-    }
     return APR_SUCCESS;
 }
 
@@ -326,7 +325,6 @@ static apr_status_t crypto_make(apr_crypto_t **ff,
     if (!f->result) {
         return APR_ENOMEM;
     }
-    f->keys = apr_array_make(pool, 10, sizeof(apr_crypto_key_t));
 
     f->types = apr_hash_make(pool);
     if (!f->types) {
@@ -491,11 +489,13 @@ static apr_status_t crypto_key(apr_crypto_key_t **
 
     key = *k;
     if (!key) {
-        *k = key = apr_array_push(f->keys);
+        *k = key = apr_pcalloc(p, sizeof *key);
+        if (!key) {
+            return APR_ENOMEM;
+        }
+        apr_pool_cleanup_register(p, key, crypto_key_cleanup,
+                                  apr_pool_cleanup_null);
     }
-    if (!key) {
-        return APR_ENOMEM;
-    }
 
     key->f = f;
     key->provider = f->provider;
@@ -683,11 +683,13 @@ static apr_status_t crypto_passphrase(apr_crypto_k
     apr_crypto_key_t *key = *k;
 
     if (!key) {
-        *k = key = apr_array_push(f->keys);
+        *k = key = apr_pcalloc(p, sizeof *key);
+        if (!key) {
+            return APR_ENOMEM;
+        }
+        apr_pool_cleanup_register(p, key, crypto_key_cleanup,
+                                  apr_pool_cleanup_null);
     }
-    if (!key) {
-        return APR_ENOMEM;
-    }
 
     key->f = f;
     key->provider = f->provider;
Index: crypto/apr_crypto_openssl.c
===================================================================
--- crypto/apr_crypto_openssl.c	(revision 1763363)
+++ crypto/apr_crypto_openssl.c	(working copy)
@@ -40,7 +40,6 @@ struct apr_crypto_t {
     apr_pool_t *pool;
     const apr_crypto_driver_t *provider;
     apu_err_t *result;
-    apr_array_header_t *keys;
     apr_crypto_config_t *config;
     apr_hash_t *types;
     apr_hash_t *modes;
@@ -268,11 +267,6 @@ static apr_status_t crypto_make(apr_crypto_t **ff,
         return APR_ENOMEM;
     }
 
-    f->keys = apr_array_make(pool, 10, sizeof(apr_crypto_key_t));
-    if (!f->keys) {
-        return APR_ENOMEM;
-    }
-
     f->types = apr_hash_make(pool);
     if (!f->types) {
         return APR_ENOMEM;
@@ -432,11 +426,11 @@ static apr_status_t crypto_key(apr_crypto_key_t **
     apr_status_t rv;
 
     if (!key) {
-        *k = key = apr_array_push(f->keys);
+        *k = key = apr_pcalloc(p, sizeof *key);
+        if (!key) {
+            return APR_ENOMEM;
+        }
     }
-    if (!key) {
-        return APR_ENOMEM;
-    }
 
     key->f = f;
     key->provider = f->provider;
@@ -533,11 +527,11 @@ static apr_status_t crypto_passphrase(apr_crypto_k
     apr_status_t rv;
 
     if (!key) {
-        *k = key = apr_array_push(f->keys);
+        *k = key = apr_pcalloc(p, sizeof *key);
+        if (!key) {
+            return APR_ENOMEM;
+        }
     }
-    if (!key) {
-        return APR_ENOMEM;
-    }
 
     key->f = f;
     key->provider = f->provider;

Reply via email to