Hi,
On 24-01-18 06:06, [email protected] wrote:
> From: Selva Nair <[email protected]>
>
> - Also add reference counting to CAPI_DATA (application data):
>
> When the application data is assigned to the private key
> we free it in the key's finish method. Proper error handling
> requires to keep track of whether data is assigned to the
> key or not before an error occurs. For this purpose, add a
> reference count to CAPI_DATA struct and increment it when it is
> assigned to the key or its method.
>
> CAPI_DATA_free now frees the data only if ref_count <= 0
>
> Signed-off-by: Selva Nair <[email protected]>
> ---
> src/openvpn/cryptoapi.c | 140
> ++++++++++++++++++++++++++++--------------------
> 1 file changed, 81 insertions(+), 59 deletions(-)
>
> diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c
> index 00e78b6..d6a9dd4 100644
> --- a/src/openvpn/cryptoapi.c
> +++ b/src/openvpn/cryptoapi.c
> @@ -106,12 +106,13 @@ typedef struct _CAPI_DATA {
> HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;
> DWORD key_spec;
> BOOL free_crypt_prov;
> + int ref_count;
> } CAPI_DATA;
>
> static void
> CAPI_DATA_free(CAPI_DATA *cd)
> {
> - if (!cd)
> + if (!cd || cd->ref_count-- > 0)
> {
> return;
> }
> @@ -467,14 +468,81 @@ find_certificate_in_store(const char *cert_prop,
> HCERTSTORE cert_store)
> return rv;
> }
>
> +static int
> +ssl_ctx_set_rsakey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey)
> +{
> + RSA *rsa = NULL, *pub_rsa;
> + RSA_METHOD *my_rsa_method = NULL;
> + bool rsa_method_set = false;
> +
> + my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method",
> + RSA_METHOD_FLAG_NO_CHECK);
> + check_malloc_return(my_rsa_method);
> + RSA_meth_set_pub_enc(my_rsa_method, rsa_pub_enc);
> + RSA_meth_set_pub_dec(my_rsa_method, rsa_pub_dec);
> + RSA_meth_set_priv_enc(my_rsa_method, rsa_priv_enc);
> + RSA_meth_set_priv_dec(my_rsa_method, rsa_priv_dec);
> + RSA_meth_set_init(my_rsa_method, NULL);
> + RSA_meth_set_finish(my_rsa_method, finish);
> + RSA_meth_set0_app_data(my_rsa_method, cd);
> +
> + rsa = RSA_new();
> + if (rsa == NULL)
> + {
> + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
> + goto err;
> + }
> +
> + pub_rsa = EVP_PKEY_get0_RSA(pkey);
Although it should not happen in the current code, I think it would be
good to check that this call succeeded before passing the result to
RSA_get0_key() below.
> +
> + /* Our private key is external, so we fill in only n and e from the
> public key */
> + const BIGNUM *n = NULL;
> + const BIGNUM *e = NULL;
> + RSA_get0_key(pub_rsa, &n, &e, NULL);
> + BIGNUM *rsa_n = BN_dup(n);
> + BIGNUM *rsa_e = BN_dup(e);
> + if (!rsa_n || !rsa_e || !RSA_set0_key(rsa, rsa_n, rsa_e, NULL))
> + {
> + BN_free(rsa_n); /* ok to free even if NULL */
> + BN_free(rsa_e);
Fixing potential memory leaks in the old code as you go, nice.
> + msg(M_NONFATAL, "ERROR: %s: out of memory", __func__);
> + goto err;
> + }
> + RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY);
> + if (!RSA_set_method(rsa, my_rsa_method))
> + {
> + goto err;
> + }
> + rsa_method_set = true; /* flag that method pointer will get freed with
> the key */
> + cd->ref_count++; /* with method, cd gets assigned to the key as
> well */
> +
> + if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
> + {
> + goto err;
> + }
> + /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so
> + * we decrease it here with RSA_free(), or it will never be cleaned up.
> */
> + RSA_free(rsa);
> + return 1;
> +
> +err:
> + if (rsa)
> + {
> + RSA_free(rsa);
> + }
> + if (my_rsa_method && !rsa_method_set)
> + {
> + RSA_meth_free(my_rsa_method);
> + }
> + return 0;
> +}
> +
> int
> SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop)
> {
> HCERTSTORE cs;
> X509 *cert = NULL;
> - RSA *rsa = NULL, *pub_rsa;
> CAPI_DATA *cd = calloc(1, sizeof(*cd));
> - RSA_METHOD *my_rsa_method = NULL;
>
> if (cd == NULL)
> {
> @@ -549,30 +617,13 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx,
> const char *cert_prop)
> }
> }
>
> - my_rsa_method = RSA_meth_new("Microsoft Cryptography API RSA Method",
> - RSA_METHOD_FLAG_NO_CHECK);
> - check_malloc_return(my_rsa_method);
> - RSA_meth_set_pub_enc(my_rsa_method, rsa_pub_enc);
> - RSA_meth_set_pub_dec(my_rsa_method, rsa_pub_dec);
> - RSA_meth_set_priv_enc(my_rsa_method, rsa_priv_enc);
> - RSA_meth_set_priv_dec(my_rsa_method, rsa_priv_dec);
> - RSA_meth_set_init(my_rsa_method, NULL);
> - RSA_meth_set_finish(my_rsa_method, finish);
> - RSA_meth_set0_app_data(my_rsa_method, cd);
> -
> - rsa = RSA_new();
> - if (rsa == NULL)
> - {
> - SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_MALLOC_FAILURE);
> - goto err;
> - }
> -
> /* Public key in cert is NULL until we call SSL_CTX_use_certificate(),
> * so we do it here then... */
> if (!SSL_CTX_use_certificate(ssl_ctx, cert))
> {
> goto err;
> }
> +
> /* the public key */
> EVP_PKEY *pkey = X509_get0_pubkey(cert);
>
> @@ -581,52 +632,23 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx,
> const char *cert_prop)
> X509_free(cert);
> cert = NULL;
>
> - if (!(pub_rsa = EVP_PKEY_get0_RSA(pkey)))
> - {
> - msg(M_WARN, "cryptoapicert requires an RSA certificate");
> - goto err;
> - }
> -
> - /* Our private key is external, so we fill in only n and e from the
> public key */
> - const BIGNUM *n = NULL;
> - const BIGNUM *e = NULL;
> - RSA_get0_key(pub_rsa, &n, &e, NULL);
> - if (!RSA_set0_key(rsa, BN_dup(n), BN_dup(e), NULL))
> + if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA)
> {
> - goto err;
> - }
> - RSA_set_flags(rsa, RSA_flags(rsa) | RSA_FLAG_EXT_PKEY);
> - if (!RSA_set_method(rsa, my_rsa_method))
> - {
> - goto err;
> + if (!ssl_ctx_set_rsakey(ssl_ctx, cd, pkey))
> + {
> + goto err;
> + }
> }
> -
> - if (!SSL_CTX_use_RSAPrivateKey(ssl_ctx, rsa))
> + else
> {
> + msg(M_WARN, "cryptoapicert requires an RSA certificate");
> goto err;
> }
> - /* SSL_CTX_use_RSAPrivateKey() increased the reference count in 'rsa', so
> - * we decrease it here with RSA_free(), or it will never be cleaned up. */
> - RSA_free(rsa);
> + cd->ref_count--; /* so that cd will get freed with the private key */
> return 1;
>
> err:
> - if (cert)
> - {
> - X509_free(cert);
> - }
> - if (rsa)
> - {
> - RSA_free(rsa);
> - }
> - else
> - {
> - if (my_rsa_method)
> - {
> - free(my_rsa_method);
> - }
> - CAPI_DATA_free(cd);
> - }
> + CAPI_DATA_free(cd);
> return 0;
> }
>
>
Otherwise looks good.
-Steffan
------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/openvpn-devel