Hi,

On 24-01-18 06:06, selva.n...@gmail.com wrote:
> From: Selva Nair <selva.n...@gmail.com>
> 
> - 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 <selva.n...@gmail.com>
> ---
>  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
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to