EVP: fix memleak in evp_pkey_downgrade()
[openssl.git] / crypto / evp / p_lib.c
index 85b5cc8127df84bdf0a05b66618658034768b8bb..9f04c72330414340be419a7ab24bc50019207d20 100644 (file)
@@ -96,6 +96,16 @@ int EVP_PKEY_save_parameters(EVP_PKEY *pkey, int mode)
     return 0;
 }
 
+int EVP_PKEY_set_ex_data(EVP_PKEY *key, int idx, void *arg)
+{
+    return CRYPTO_set_ex_data(&key->ex_data, idx, arg);
+}
+
+void *EVP_PKEY_get_ex_data(const EVP_PKEY *key, int idx)
+{
+    return CRYPTO_get_ex_data(&key->ex_data, idx);
+}
+
 int EVP_PKEY_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from)
 {
     /*
@@ -1090,10 +1100,20 @@ EVP_PKEY *EVP_PKEY_new(void)
     ret->lock = CRYPTO_THREAD_lock_new();
     if (ret->lock == NULL) {
         EVPerr(EVP_F_EVP_PKEY_NEW, ERR_R_MALLOC_FAILURE);
-        OPENSSL_free(ret);
-        return NULL;
+        goto err;
+    }
+#ifndef FIPS_MODE
+    if (!CRYPTO_new_ex_data(CRYPTO_EX_INDEX_EVP_PKEY, ret, &ret->ex_data)) {
+        EVPerr(EVP_F_EVP_PKEY_NEW, ERR_R_MALLOC_FAILURE);
+        goto err;
     }
+#endif
     return ret;
+
+ err:
+    CRYPTO_THREAD_lock_free(ret->lock);
+    OPENSSL_free(ret);
+    return NULL;
 }
 
 /*
@@ -1328,6 +1348,9 @@ void EVP_PKEY_free(EVP_PKEY *x)
         return;
     REF_ASSERT_ISNT(i < 0);
     evp_pkey_free_it(x);
+#ifndef FIPS_MODE
+    CRYPTO_free_ex_data(CRYPTO_EX_INDEX_EVP_PKEY, x, &x->ex_data);
+#endif
     CRYPTO_THREAD_lock_free(x->lock);
 #ifndef FIPS_MODE
     sk_X509_ATTRIBUTE_pop_free(x->attributes, X509_ATTRIBUTE_free);
@@ -1536,8 +1559,11 @@ int evp_pkey_downgrade(EVP_PKEY *pk)
     evp_pkey_free_it(pk);
     if (EVP_PKEY_set_type(pk, type)) {
         /* If the key is typed but empty, we're done */
-        if (keydata == NULL)
+        if (keydata == NULL) {
+            /* We're dropping the EVP_KEYMGMT */
+            EVP_KEYMGMT_free(keymgmt);
             return 1;
+        }
 
         if (pk->ameth->import_from == NULL) {
             ERR_raise_data(ERR_LIB_EVP, EVP_R_NO_IMPORT_FUNCTION,
@@ -1556,6 +1582,9 @@ int evp_pkey_downgrade(EVP_PKEY *pk)
 
             /* Synchronize the dirty count */
             pk->dirty_cnt_copy = pk->ameth->dirty_cnt(pk);
+
+            /* evp_keymgmt_export() increased the refcount... */
+            EVP_KEYMGMT_free(keymgmt);
             return 1;
         }
 
@@ -1574,6 +1603,8 @@ int evp_pkey_downgrade(EVP_PKEY *pk)
         ERR_raise(ERR_LIB_EVP, ERR_R_INTERNAL_ERROR);
         return 0;
     }
+    /* EVP_PKEY_set_type_by_keymgmt() increased the refcount... */
+    EVP_KEYMGMT_free(keymgmt);
     pk->keydata = keydata;
     evp_keymgmt_util_cache_keyinfo(pk);
     return 0;     /* No downgrade, but at least the key is restored */