Hi,

when long-running process, such as httpd, uses OpenSSL and loads new 
CRLs or certificates over the time, they are all staying in the 
X509_STORE's cache. It could be really nice to have a way how to clear 
the cache to free the CRls/certificates from the memory.

Attached patch against master does that by introducing new 
X509_STORE_clear_cache. Few things about the patch to point out:

1. It increases the refcount of X509_OBJECT returned by 
by_dir.c:get_cert_by_subject. This is needed to ensure that X509_OBJECT 
won't be freed when cache is cleared by another thread during 
get_cert_by_subject execution.

This change breaks the ABI because the get_cert_by_subject semantic 
changed, but I haven't found another way how to do that.

2. Other refcount changes should be safe and again ensures that 
X509_OBJECT is not freed by another thread while used.

3. The patch changes semantic of X509_STORE.cache variable, but this 
variable has not been used for anything before (It is just set to 1 
during initialization and never used again), so this should be safe too.


In the future, it would be nice to call X509_STORE_clear_cache 
automatically when caching is disabled, but so far I haven't found good 
place where to do such call.

Regards,
Jan Kaluza

diff --git a/crypto/x509/by_dir.c b/crypto/x509/by_dir.c
index 80444ff..ea3dd1b 100644
--- a/crypto/x509/by_dir.c
+++ b/crypto/x509/by_dir.c
@@ -379,8 +379,10 @@ static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
          */
         CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
         j = sk_X509_OBJECT_find(xl->store_ctx->objs, &stmp);
-        if (j != -1)
+        if (j != -1) {
             tmp = sk_X509_OBJECT_value(xl->store_ctx->objs, j);
+            X509_OBJECT_up_ref_count(tmp);
+        }
         else
             tmp = NULL;
         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
@@ -426,17 +428,14 @@ static int get_cert_by_subject(X509_LOOKUP *xl, int type, X509_NAME *name,
             ok = 1;
             ret->type = tmp->type;
             memcpy(&ret->data, &tmp->data, sizeof(ret->data));
-            /*
-             * If we were going to up the reference count, we would need to
-             * do it on a perl 'type' basis
-             */
-        /*- CRYPTO_add(&tmp->data.x509->references,1,
-                    CRYPTO_LOCK_X509);*/
             goto finish;
         }
     }
  finish:
     if (b != NULL)
         BUF_MEM_free(b);
+    if (tmp != NULL && !ok) {
+        X509_OBJECT_free_contents(tmp);
+    }
     return (ok);
 }
diff --git a/crypto/x509/x509_lu.c b/crypto/x509/x509_lu.c
index f77e59d..c8bc2a6 100644
--- a/crypto/x509/x509_lu.c
+++ b/crypto/x509/x509_lu.c
@@ -187,7 +187,7 @@ X509_STORE *X509_STORE_new(void)
     if ((ret = (X509_STORE *)OPENSSL_malloc(sizeof(X509_STORE))) == NULL)
         return NULL;
     ret->objs = sk_X509_OBJECT_new(x509_object_cmp);
-    ret->cache = 1;
+    ret->cache = X509_STORE_CACHE_ALL;
     ret->get_cert_methods = sk_X509_LOOKUP_new_null();
     ret->verify = 0;
     ret->verify_cb = 0;
@@ -303,6 +303,9 @@ int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
 
     CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
     tmp = X509_OBJECT_retrieve_by_subject(ctx->objs, type, name);
+   if (tmp) {
+       X509_OBJECT_up_ref_count(tmp);
+   }
     CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
 
     if (tmp == NULL || type == X509_LU_CRL) {
@@ -312,8 +315,14 @@ int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
             j = X509_LOOKUP_by_subject(lu, type, name, &stmp);
             if (j < 0) {
                 vs->current_method = j;
+                if (tmp) {
+                    X509_OBJECT_free_contents(tmp);
+                }
                 return j;
             } else if (j) {
+                if (tmp) {
+                    X509_OBJECT_free_contents(tmp);
+                }
                 tmp = &stmp;
                 break;
             }
@@ -329,8 +338,6 @@ int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
     ret->type = tmp->type;
     ret->data.ptr = tmp->data.ptr;
 
-    X509_OBJECT_up_ref_count(ret);
-
     return 1;
 }
 
@@ -502,8 +509,8 @@ STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm)
             sk_X509_free(sk);
             return NULL;
         }
-        X509_OBJECT_free_contents(&xobj);
         CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+        X509_OBJECT_free_contents(&xobj);
         idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_X509, nm, &cnt);
         if (idx < 0) {
             CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
@@ -546,8 +553,8 @@ STACK_OF(X509_CRL) *X509_STORE_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm)
         sk_X509_CRL_free(sk);
         return NULL;
     }
-    X509_OBJECT_free_contents(&xobj);
     CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+    X509_OBJECT_free_contents(&xobj);
     idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_CRL, nm, &cnt);
     if (idx < 0) {
         CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
@@ -673,6 +680,59 @@ int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
     return ret;
 }
 
+void X509_STORE_clear_cache(X509_STORE *ctx)
+{
+   X509_OBJECT *a;
+   int i;
+
+   if (ctx->objs == NULL) {
+       return;
+   }
+
+   CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
+
+    for (i = 0 ; i < sk_X509_OBJECT_num(ctx->objs); i++) {
+        a = (X509_OBJECT *) sk_X509_OBJECT_value(ctx->objs, i);
+        if (a == NULL) {
+            continue;
+        }
+        switch (a->type) {
+        case X509_LU_X509:
+            if (ctx->cache & X509_STORE_CACHE_CERT) {
+                break;
+            }
+            if (a->data.x509->references == 1) {
+                X509_free(a->data.x509);
+                OPENSSL_free(a);
+                sk_X509_OBJECT_delete(ctx->objs, i);
+                --i;
+            }
+            break;
+        case X509_LU_CRL:
+            if (ctx->cache & X509_STORE_CACHE_CRL) {
+                break;
+            }
+            if (a->data.crl->references == 1) {
+                X509_CRL_free(a->data.crl);
+                OPENSSL_free(a);
+                sk_X509_OBJECT_delete(ctx->objs, i);
+                --i;
+            }
+            break;
+        default:
+            break;
+        }
+    }
+
+    CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
+}
+
+int X509_STORE_set_cache(X509_STORE *ctx, int cache)
+{
+   ctx->cache = cache;
+   return 1;
+}
+
 int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags)
 {
     return X509_VERIFY_PARAM_set_flags(ctx->param, flags);
diff --git a/include/openssl/x509_vfy.h b/include/openssl/x509_vfy.h
index e41b5e2..d88f6bf 100644
--- a/include/openssl/x509_vfy.h
+++ b/include/openssl/x509_vfy.h
@@ -166,6 +166,11 @@ typedef struct X509_VERIFY_PARAM_st {
 
 DECLARE_STACK_OF(X509_VERIFY_PARAM)
 
+#define X509_STORE_NO_CACHE		0
+#define X509_STORE_CACHE_CRL	1
+#define X509_STORE_CACHE_CERT	2
+#define X509_STORE_CACHE_ALL	(X509_STORE_CACHE_CRL | X509_STORE_CACHE_CERT)
+
 /*
  * This is used to hold everything.  It is used for all certificate
  * validation.  Once we have a certificate chain, the 'verify' function is
@@ -203,6 +208,8 @@ struct x509_store_st {
 } /* X509_STORE */ ;
 
 int X509_STORE_set_depth(X509_STORE *store, int depth);
+int X509_STORE_set_cache(X509_STORE *store, int cache);
+void X509_STORE_clear_cache(X509_STORE *store);
 
 # define X509_STORE_set_verify_cb_func(ctx,func) ((ctx)->verify_cb=(func))
 # define X509_STORE_set_verify_func(ctx,func)    ((ctx)->verify=(func))
_______________________________________________
openssl-dev mailing list
To unsubscribe: https://mta.openssl.org/mailman/listinfo/openssl-dev

Reply via email to