Replumbing: add fallback provider capability
authorRichard Levitte <levitte@openssl.org>
Thu, 14 Mar 2019 09:53:27 +0000 (10:53 +0100)
committerRichard Levitte <levitte@openssl.org>
Tue, 19 Mar 2019 13:06:58 +0000 (14:06 +0100)
To ensure that old applications aren't left without any provider, and
at the same time not forcing any default provider on applications that
know how to deal with them, we device the concept of fallback
providers, which are automatically activated if no other provider is
already activated.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/8480)

crypto/cpt_err.c
crypto/err/openssl.txt
crypto/provider_core.c
doc/internal/man3/ossl_provider_new.pod
include/internal/provider.h
include/openssl/cryptoerr.h

index bf7985c..88bee48 100644 (file)
@@ -57,6 +57,8 @@ static const ERR_STRING_DATA CRYPTO_str_functs[] = {
      "pkey_poly1305_init"},
     {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_PKEY_SIPHASH_INIT, 0),
      "pkey_siphash_init"},
+    {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_PROVIDER_ACTIVATE, 0),
+     "provider_activate"},
     {ERR_PACK(ERR_LIB_CRYPTO, CRYPTO_F_SK_RESERVE, 0), "sk_reserve"},
     {0, NULL}
 };
index 4853a05..9f84dbc 100644 (file)
@@ -396,6 +396,7 @@ CRYPTO_F_OSSL_PROVIDER_NEW:131:ossl_provider_new
 CRYPTO_F_PKEY_HMAC_INIT:123:pkey_hmac_init
 CRYPTO_F_PKEY_POLY1305_INIT:124:pkey_poly1305_init
 CRYPTO_F_PKEY_SIPHASH_INIT:125:pkey_siphash_init
+CRYPTO_F_PROVIDER_ACTIVATE:134:provider_activate
 CRYPTO_F_SK_RESERVE:129:sk_reserve
 CT_F_CTLOG_NEW:117:CTLOG_new
 CT_F_CTLOG_NEW_FROM_BASE64:118:CTLOG_new_from_base64
index 8af5b1f..c136e42 100644 (file)
@@ -25,6 +25,7 @@ struct provider_store_st;        /* Forward declaration */
 struct ossl_provider_st {
     /* Flag bits */
     unsigned int flag_initialized:1;
+    unsigned int flag_fallback:1;
 
     /* OpenSSL library side data */
     CRYPTO_REF_COUNT refcnt;
@@ -32,6 +33,7 @@ struct ossl_provider_st {
     char *name;
     DSO *module;
     OSSL_provider_init_fn *init_function;
+    struct provider_store_st *store; /* The store this instance belongs to */
 
     /* Provider side functions */
     OSSL_provider_teardown_fn *teardown;
@@ -58,6 +60,7 @@ static int ossl_provider_cmp(const OSSL_PROVIDER * const *a,
 struct provider_store_st {
     STACK_OF(OSSL_PROVIDER) *providers;
     CRYPTO_RWLOCK *lock;
+    unsigned int use_fallbacks:1;
 };
 static int provider_store_index = -1;
 
@@ -80,8 +83,9 @@ static void *provider_store_new(void)
         || (store->providers = sk_OSSL_PROVIDER_new(ossl_provider_cmp)) == NULL
         || (store->lock = CRYPTO_THREAD_lock_new()) == NULL) {
         provider_store_free(store);
-        store = NULL;
+        return NULL;
     }
+    store->use_fallbacks = 1;
     return store;
 }
 
@@ -185,6 +189,8 @@ OSSL_PROVIDER *ossl_provider_new(OPENSSL_CTX *libctx, const char *name,
         ossl_provider_free(prov); /* -1 Store reference */
         ossl_provider_free(prov); /* -1 Reference that was to be returned */
         prov = NULL;
+    } else {
+        prov->store = store;
     }
     CRYPTO_THREAD_unlock(store->lock);
 
@@ -207,11 +213,13 @@ void ossl_provider_free(OSSL_PROVIDER *prov)
         CRYPTO_DOWN_REF(&prov->refcnt, &ref, prov->refcnt_lock);
 
         /*
-         * When the refcount drops down to one, there is only one reference,
-         * the store.
+         * When the refcount drops below two, the store is the only
+         * possible reference, or it has already been taken away from
+         * the store (this may happen if a provider was activated
+         * because it's a fallback, but isn't currently used)
          * When that happens, the provider is inactivated.
          */
-        if (ref == 1 && prov->flag_initialized) {
+        if (ref < 2 && prov->flag_initialized) {
             if (prov->teardown != NULL)
                 prov->teardown();
             prov->flag_initialized = 0;
@@ -246,7 +254,12 @@ void ossl_provider_free(OSSL_PROVIDER *prov)
  */
 static const OSSL_DISPATCH *core_dispatch; /* Define further down */
 
-int ossl_provider_activate(OSSL_PROVIDER *prov)
+/*
+ * Internal version that doesn't affect the store flags, and thereby avoid
+ * locking.  Direct callers must remember to set the store flags when
+ * appropriate
+ */
+static int provider_activate(OSSL_PROVIDER *prov)
 {
     const OSSL_DISPATCH *provider_dispatch = NULL;
 
@@ -295,7 +308,7 @@ int ossl_provider_activate(OSSL_PROVIDER *prov)
 
     if (prov->init_function == NULL
         || !prov->init_function(prov, core_dispatch, &provider_dispatch)) {
-        CRYPTOerr(CRYPTO_F_OSSL_PROVIDER_ACTIVATE, ERR_R_INIT_FAIL);
+        CRYPTOerr(CRYPTO_F_PROVIDER_ACTIVATE, ERR_R_INIT_FAIL);
         ERR_add_error_data(2, "name=", prov->name);
         DSO_free(prov->module);
         prov->module = NULL;
@@ -329,6 +342,46 @@ int ossl_provider_activate(OSSL_PROVIDER *prov)
     return 1;
 }
 
+int ossl_provider_activate(OSSL_PROVIDER *prov)
+{
+    if (provider_activate(prov)) {
+        CRYPTO_THREAD_write_lock(prov->store->lock);
+        prov->store->use_fallbacks = 0;
+        CRYPTO_THREAD_unlock(prov->store->lock);
+        return 1;
+    }
+
+    return 0;
+}
+
+
+static int provider_forall_loaded(struct provider_store_st *store,
+                                  int *found_activated,
+                                  int (*cb)(OSSL_PROVIDER *provider,
+                                            void *cbdata),
+                                  void *cbdata)
+{
+    int i;
+    int ret = 1;
+    int num_provs = sk_OSSL_PROVIDER_num(store->providers);
+
+    if (found_activated != NULL)
+        *found_activated = 0;
+    for (i = 0; i < num_provs; i++) {
+        OSSL_PROVIDER *prov =
+            sk_OSSL_PROVIDER_value(store->providers, i);
+
+        if (prov->flag_initialized) {
+            if (found_activated != NULL)
+                *found_activated = 1;
+            if (!(ret = cb(prov, cbdata)))
+                break;
+        }
+    }
+
+    return ret;
+}
+
 int ossl_provider_forall_loaded(OPENSSL_CTX *ctx,
                                 int (*cb)(OSSL_PROVIDER *provider,
                                           void *cbdata),
@@ -339,13 +392,50 @@ int ossl_provider_forall_loaded(OPENSSL_CTX *ctx,
     struct provider_store_st *store = get_provider_store(ctx);
 
     if (store != NULL) {
+        int found_activated = 0;
+
         CRYPTO_THREAD_read_lock(store->lock);
-        for (i = 0; i < sk_OSSL_PROVIDER_num(store->providers); i++) {
-            OSSL_PROVIDER *prov = sk_OSSL_PROVIDER_value(store->providers, i);
+        ret = provider_forall_loaded(store, &found_activated, cb, cbdata);
 
-            if (prov->flag_initialized
-                && !(ret = cb(prov, cbdata)))
-                break;
+        /*
+         * If there's nothing activated ever in this store, try to activate
+         * all fallbacks.
+         */
+        if (!found_activated && store->use_fallbacks) {
+            int num_provs = sk_OSSL_PROVIDER_num(store->providers);
+            int activated_fallback_count = 0;
+
+            for (i = 0; i < num_provs; i++) {
+                OSSL_PROVIDER *prov =
+                    sk_OSSL_PROVIDER_value(store->providers, i);
+
+                /*
+                 * Note that we don't care if the activation succeeds or
+                 * not.  If it doesn't succeed, then the next loop will
+                 * fail anyway.
+                 */
+                if (prov->flag_fallback) {
+                    activated_fallback_count++;
+                    provider_activate(prov);
+                }
+            }
+
+            if (activated_fallback_count > 0) {
+                /*
+                 * We assume that all fallbacks have been added to the store
+                 * before any fallback is activated.
+                 * TODO: We may have to reconsider this, IF we find ourselves
+                 * adding fallbacks after any previous fallback has been
+                 * activated.
+                 */
+                store->use_fallbacks = 0;
+
+                /*
+                 * Now that we've activated available fallbacks, try a
+                 * second sweep
+                 */
+                ret = provider_forall_loaded(store, NULL, cb, cbdata);
+            }
         }
         CRYPTO_THREAD_unlock(store->lock);
     }
@@ -353,6 +443,16 @@ int ossl_provider_forall_loaded(OPENSSL_CTX *ctx,
     return ret;
 }
 
+/* Setters of Provider Object data */
+int ossl_provider_set_fallback(OSSL_PROVIDER *prov)
+{
+    if (prov == NULL)
+        return 0;
+
+    prov->flag_fallback = 1;
+    return 1;
+}
+
 /* Getters of Provider Object data */
 const char *ossl_provider_name(OSSL_PROVIDER *prov)
 {
index 7633e0e..aa984c9 100644 (file)
@@ -4,7 +4,8 @@
 
 ossl_provider_find, ossl_provider_new, ossl_provider_upref,
 ossl_provider_free, ossl_provider_add_module_location,
-ossl_provider_activate, ossl_provider_forall_loaded,
+ossl_provider_set_fallback, ossl_provider_activate,
+ossl_provider_forall_loaded,
 ossl_provider_name, ossl_provider_dso,
 ossl_provider_module_name, ossl_provider_module_path,
 ossl_provider_teardown, ossl_provider_get_param_types,
@@ -23,6 +24,7 @@ ossl_provider_get_params, ossl_provider_query_operation
 
  /* Setters */
  int ossl_provider_add_module_location(OSSL_PROVIDER *prov, const char *loc);
+ int ossl_provider_set_fallback(OSSL_PROVIDER *prov);
 
  /* Load and initialize the Provider */
  int ossl_provider_activate(OSSL_PROVIDER *prov);
@@ -71,7 +73,7 @@ times as ossl_provider_activate() has.
 
 ossl_provider_find() finds an existing I<provider object> in the
 I<provider object> store by C<name>.
-The I<provider object> it finds gets it's reference count
+The I<provider object> it finds gets its reference count
 incremented.
 
 ossl_provider_new() creates a new I<provider object> and stores it in
@@ -84,14 +86,20 @@ To indicate a built-in provider, the C<init_function> argument must
 point at the provider initialization function for that provider.
 
 ossl_provider_free() decrements a I<provider object>'s reference
-counter; if it drops to one, the I<provider object> will be
-inactivated (it's teardown function is called) but kept in the store;
-if it drops down to zero, the associated module will be unloaded if
-one was loaded, and the I<provider object> will be freed.
+counter; if it drops below 2, the I<provider object> is assumed to
+have fallen out of use and will be inactivated (its teardown function
+is called); if it drops down to zero, the I<provider object> is
+assumed to have been taken out of the store, and the associated module
+will be unloaded if one was loaded, and the I<provider object> will be
+freed.
 
 ossl_provider_add_module_location() adds a location to look for a
 provider module.
 
+ossl_provider_set_fallback() marks an available provider as fallback.
+Note that after this call, the I<provider object> pointer that was
+used can simply be dropped, but not freed.
+
 ossl_provider_activate() "activates" the provider for the given
 I<provider object>.
 What "activates" means depends on what type of I<provider object> it
@@ -115,6 +123,8 @@ be located in that module, and called.
 
 ossl_provider_forall_loaded() iterates over all the currently
 "activated" providers, and calls C<cb> for each of them.
+If no providers have been "activated" yet, it tries to activate all
+available fallback providers and tries another iteration.
 
 ossl_provider_name() returns the name that was given with
 ossl_provider_new().
@@ -178,8 +188,8 @@ it has been incremented.
 
 ossl_provider_free() doesn't return any value.
 
-ossl_provider_add_module_location() and ossl_provider_activate()
-return 1 on success, or 0 on error.
+ossl_provider_add_module_location(), ossl_provider_set_fallback() and
+ossl_provider_activate() return 1 on success, or 0 on error.
 
 ossl_provider_name(), ossl_provider_dso(),
 ossl_provider_module_name(), and ossl_provider_module_path() return a
index ac70fcc..8af20a7 100644 (file)
@@ -34,6 +34,7 @@ void ossl_provider_free(OSSL_PROVIDER *prov);
 
 /* Setters */
 int ossl_provider_add_module_location(OSSL_PROVIDER *prov, const char *loc);
+int ossl_provider_set_fallback(OSSL_PROVIDER *prov);
 
 /*
  * Activate the Provider
index c27f05c..42fd3b6 100644 (file)
@@ -49,6 +49,7 @@ int ERR_load_CRYPTO_strings(void);
 # define CRYPTO_F_PKEY_HMAC_INIT                          123
 # define CRYPTO_F_PKEY_POLY1305_INIT                      124
 # define CRYPTO_F_PKEY_SIPHASH_INIT                       125
+# define CRYPTO_F_PROVIDER_ACTIVATE                       134
 # define CRYPTO_F_SK_RESERVE                              129
 
 /*