Hi,

On 17-02-17 23:00, log...@free.fr wrote:
> From: Emmanuel Deloget <log...@free.fr>
> 
> OpenSSL 1.1 does not allow us to directly access the internal of
> any data type, including X509. We have to use the defined
> functions to do so.
> 
> In x509_verify_ns_cert_type() in particular, this means that we
> cannot directly check for the extended flags to find whether the
> certificate should be used as a client or as a server certificate.
> We need to leverage the X509_check_purpose() API yet this API is
> far stricter than the currently implemented check. So far, I have
> not been able to find a situation where this stricter test fails
> (although I must admit that I haven't tested that very well).

The nsCertType x509 extension is very old, and replaced by keyUsage and
extendedKeyUsage (supported by OpenVPN via --remote-cert-tls or
--remote-cert-ku and --remote-cert-eku).

Changing this to the stricter API without warning will probably break
some people's setups with certs that do have nsCertType set, but not
keyUsage and/or extendedKeyUsage, while leaving them guessing why.

So, what I propose instead is:
 * remove all the nsCertType code (except the option in add_option())
 * update the help strings and man page to indicate that --ns-cert-type
is no longer supported and --remote-cert-tls should be used instead
 * in add_option(), if the option is enabled in a config file, act as if
--remote-cert-tls was specified correspondingly, and print a clear
warning that --ns-cert-type is no longer supported and stricter checks
are enabled instead.

> Compatibility with OpenSSL 1.0 is kept by defining the corresponding
> functions when they are not found in the library.
> 
> Signed-off-by: Emmanuel Deloget <log...@free.fr>
> ---
>  configure.ac                     |  1 +
>  src/openvpn/openssl_compat.h     | 15 +++++++++++++++
>  src/openvpn/ssl_openssl.c        |  3 ++-
>  src/openvpn/ssl_verify_openssl.c | 28 +++++++++++++++++++---------
>  4 files changed, 37 insertions(+), 10 deletions(-)
> 
> diff --git a/configure.ac b/configure.ac
> index 
> 6f31609d0aeedd2c7841d271ecadd1aa6f3b11da..c41db3effbb26318be4f44009a5055e808b89b56
>  100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -902,6 +902,7 @@ if test "${enable_crypto}" = "yes" -a 
> "${with_crypto_library}" = "openssl"; then
>               [ \
>                       SSL_CTX_get_default_passwd_cb \
>                       SSL_CTX_get_default_passwd_cb_userdata \
> +                     X509_get0_pubkey \
>                       X509_STORE_get0_objects \
>                       X509_OBJECT_free \
>                       X509_OBJECT_get_type \
> diff --git a/src/openvpn/openssl_compat.h b/src/openvpn/openssl_compat.h
> index 
> b1748754f821f472cf9ed7083ade918336c9b075..6a89b91b05e0370a50ac5a1cae20ae659e6c7634
>  100644
> --- a/src/openvpn/openssl_compat.h
> +++ b/src/openvpn/openssl_compat.h
> @@ -74,6 +74,21 @@ SSL_CTX_get_default_passwd_cb(SSL_CTX *ctx)
>  }
>  #endif
>  
> +#if !defined(HAVE_X509_GET0_PUBKEY)
> +/**
> + * Get the public key from a X509 certificate
> + *
> + * @param x                  X509 certificate
> + * @return                   The certificate public key
> + */
> +static inline EVP_PKEY *
> +X509_get0_pubkey(const X509 *x)
> +{
> +    return (x && x->cert_info && x->cert_info->key) ?
> +           x->cert_info->key->pkey : NULL;
> +}
> +#endif
> +
>  #if !defined(HAVE_X509_STORE_GET0_OBJECTS)
>  /**
>   * Fetch the X509 object stack from the X509 store
> diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
> index 
> f011e06702529ff34e91f6d0169d1adf8cc9d767..b683961d9e4e79b9ee04cfa7ecd1b377ade9651b
>  100644
> --- a/src/openvpn/ssl_openssl.c
> +++ b/src/openvpn/ssl_openssl.c
> @@ -1073,7 +1073,8 @@ tls_ctx_use_external_private_key(struct tls_root_ctx 
> *ctx,
>      }
>  
>      /* get the public key */
> -    ASSERT(cert->cert_info->key->pkey); /* NULL before 
> SSL_CTX_use_certificate() is called */
> +    EVP_PKEY *pkey = X509_get0_pubkey(cert);
> +    ASSERT(pkey); /* NULL before SSL_CTX_use_certificate() is called */
>      pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
>  
>      /* initialize RSA object */
> diff --git a/src/openvpn/ssl_verify_openssl.c 
> b/src/openvpn/ssl_verify_openssl.c
> index 
> 07975248035b48121d1383b47f40a56042bc7380..edc709b89eb05bca895639dde606b29f8e1f7024
>  100644
> --- a/src/openvpn/ssl_verify_openssl.c
> +++ b/src/openvpn/ssl_verify_openssl.c
> @@ -285,18 +285,20 @@ backend_x509_get_serial_hex(openvpn_x509_cert_t *cert, 
> struct gc_arena *gc)
>  struct buffer
>  x509_get_sha1_fingerprint(X509 *cert, struct gc_arena *gc)
>  {
> -    struct buffer hash = alloc_buf_gc(sizeof(cert->sha1_hash), gc);
> -    memcpy(BPTR(&hash), cert->sha1_hash, sizeof(cert->sha1_hash));
> -    ASSERT(buf_inc_len(&hash, sizeof(cert->sha1_hash)));
> +    const EVP_MD *sha1 = EVP_sha1();
> +    struct buffer hash = alloc_buf_gc(EVP_MD_size(sha1), gc);
> +    X509_digest(cert, EVP_sha1(), BPTR(&hash), NULL);
> +    ASSERT(buf_inc_len(&hash, EVP_MD_size(sha1)));
>      return hash;
>  }
>  
>  struct buffer
>  x509_get_sha256_fingerprint(X509 *cert, struct gc_arena *gc)
>  {
> -    struct buffer hash = alloc_buf_gc((EVP_sha256())->md_size, gc);
> +    const EVP_MD *sha256 = EVP_sha256();
> +    struct buffer hash = alloc_buf_gc(EVP_MD_size(sha256), gc);
>      X509_digest(cert, EVP_sha256(), BPTR(&hash), NULL);
> -    ASSERT(buf_inc_len(&hash, (EVP_sha256())->md_size));
> +    ASSERT(buf_inc_len(&hash, EVP_MD_size(sha256)));
>      return hash;
>  }
>  
> @@ -573,13 +575,21 @@ x509_verify_ns_cert_type(const openvpn_x509_cert_t 
> *peer_cert, const int usage)
>      }
>      if (usage == NS_CERT_CHECK_CLIENT)
>      {
> -        return ((peer_cert->ex_flags & EXFLAG_NSCERT)
> -                && (peer_cert->ex_nscert & NS_SSL_CLIENT)) ? SUCCESS : 
> FAILURE;
> +        /*
> +         * Unfortunately, X509_check_purpose() does some wierd thing that
> +         * prevent it to take a const argument
> +         */
> +        return X509_check_purpose((X509 *)peer_cert, 
> X509_PURPOSE_SSL_CLIENT, 0) ?
> +            SUCCESS : FAILURE;
>      }
>      if (usage == NS_CERT_CHECK_SERVER)
>      {
> -        return ((peer_cert->ex_flags & EXFLAG_NSCERT)
> -                && (peer_cert->ex_nscert & NS_SSL_SERVER))  ? SUCCESS : 
> FAILURE;
> +        /*
> +         * Unfortunately, X509_check_purpose() does some wierd thing that
> +         * prevent it to take a const argument
> +         */
> +        return X509_check_purpose((X509 *)peer_cert, 
> X509_PURPOSE_SSL_SERVER, 0) ?
> +            SUCCESS : FAILURE;
>      }
>  
>      return FAILURE;
> 

If we are to use X509_check_purpose() (see my comments above), we should
not cast away const here, but rather remove the const qualified from the
peer_cert argument.  Casting away const is bad, in particular if a
comment on the function states that it really can't take a const
argument because it changes the object.

-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