Hi Steffan, On Wed, Feb 22, 2017 at 11:13 PM, Steffan Karger <stef...@karger.me> wrote: > 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 RSA_METHOD. We have to use the defined >> functions to do so. >> >> 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 | 9 +++ >> src/openvpn/openssl_compat.h | 186 >> +++++++++++++++++++++++++++++++++++++++++++ >> src/openvpn/ssl_openssl.c | 22 ++--- >> 3 files changed, 206 insertions(+), 11 deletions(-) >> >> diff --git a/configure.ac b/configure.ac >> index >> 789ad08fbaa3b3fc4c95d2b7a22332c0a93aeab4..6f31609d0aeedd2c7841d271ecadd1aa6f3b11da >> 100644 >> --- a/configure.ac >> +++ b/configure.ac >> @@ -905,6 +905,15 @@ if test "${enable_crypto}" = "yes" -a >> "${with_crypto_library}" = "openssl"; then >> X509_STORE_get0_objects \ >> X509_OBJECT_free \ >> X509_OBJECT_get_type \ >> + RSA_meth_new \ >> + RSA_meth_free \ >> + RSA_meth_set_pub_enc \ >> + RSA_meth_set_pub_dec \ >> + RSA_meth_set_priv_enc \ >> + RSA_meth_set_priv_dec \ >> + RSA_meth_set_init \ >> + RSA_meth_set_finish \ >> + RSA_meth_set0_app_data \ >> ], >> , >> [] >> diff --git a/src/openvpn/openssl_compat.h b/src/openvpn/openssl_compat.h >> index >> 458a6adbe2b3fcd5ea63dcea6596cc24315d463c..b1748754f821f472cf9ed7083ade918336c9b075 >> 100644 >> --- a/src/openvpn/openssl_compat.h >> +++ b/src/openvpn/openssl_compat.h >> @@ -41,6 +41,8 @@ >> #include "config-msvc.h" >> #endif >> >> +#include "buffer.h" >> + >> #include <openssl/ssl.h> >> #include <openssl/x509.h> >> >> @@ -117,4 +119,188 @@ X509_OBJECT_get_type(const X509_OBJECT *obj) >> } >> #endif >> >> +#if !defined(HAVE_RSA_METH_NEW) >> +/** >> + * Allocate a new RSA method object >> + * >> + * @param name The object name >> + * @param flags Configuration flags >> + * @return A new RSA method object >> + */ >> +static inline RSA_METHOD * >> +RSA_meth_new(const char *name, int flags) >> +{ >> + RSA_METHOD *rsa_meth = NULL; >> + ALLOC_OBJ_CLEAR(rsa_meth, RSA_METHOD); >> + rsa_meth->name = name; >> + rsa_meth->flags = flags; >> + return rsa_meth; >> +} >> +#endif >> + >> +#if !defined(HAVE_RSA_METH_FREE) >> +/** >> + * Free an existing RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + */ >> +static inline void >> +RSA_meth_free(RSA_METHOD *meth) >> +{ >> + free(meth); >> +} >> +#endif > > I think it would be nicer to more closely mimic the 1.1 behaviour in > RSA_meth_{new,free}(), and copy the name string in new() and free it > again in free(). That could prevent a future use-after-free that would > occur for pre-1.1.0, but not 1.1.0+:
I failed to see that when I implemented my solution. I'll give a look as soon as possible. > char *mystring = calloc(50, 1); > RSA_METHOD *meth = RSA_meth_new(mystring, 0); > free(mystring); > > meth.smoke(); > ^^ might cause problems > > (Hint: use string_alloc(x, NULL).) > >> + >> +#if !defined(HAVE_RSA_METH_SET_PUB_ENC) >> +/** >> + * Set the public encoding function of an RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + * @param pub_enc the public encoding function >> + * @return 1 on success, 0 on error >> + */ >> +static inline int >> +RSA_meth_set_pub_enc(RSA_METHOD *meth, >> + int (*pub_enc) (int flen, const unsigned char *from, >> + unsigned char *to, RSA *rsa, >> + int padding)) >> +{ >> + if (meth) >> + { >> + meth->rsa_pub_enc = pub_enc; >> + return 1; >> + } >> + return 0; >> +} >> +#endif >> + >> +#if !defined(HAVE_RSA_METH_SET_PUB_DEC) >> +/** >> + * Set the public decoding function of an RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + * @param pub_dec the public decoding function >> + * @return 1 on success, 0 on error >> + */ >> +static inline int >> +RSA_meth_set_pub_dec(RSA_METHOD *meth, >> + int (*pub_dec) (int flen, const unsigned char *from, >> + unsigned char *to, RSA *rsa, >> + int padding)) >> +{ >> + if (meth) >> + { >> + meth->rsa_pub_dec = pub_dec; >> + return 1; >> + } >> + return 0; >> +} >> +#endif >> + >> +#if !defined(HAVE_RSA_METH_SET_PRIV_ENC) >> +/** >> + * Set the private encoding function of an RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + * @param priv_enc the private encoding function >> + * @return 1 on success, 0 on error >> + */ >> +static inline int >> +RSA_meth_set_priv_enc(RSA_METHOD *meth, >> + int (*priv_enc) (int flen, const unsigned char *from, >> + unsigned char *to, RSA *rsa, >> + int padding)) >> +{ >> + if (meth) >> + { >> + meth->rsa_priv_enc = priv_enc; >> + return 1; >> + } >> + return 0; >> +} >> +#endif >> + >> +#if !defined(HAVE_RSA_METH_SET_PRIV_DEC) >> +/** >> + * Set the private decoding function of an RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + * @param priv_dec the private decoding function >> + * @return 1 on success, 0 on error >> + */ >> +static inline int >> +RSA_meth_set_priv_dec(RSA_METHOD *meth, >> + int (*priv_dec) (int flen, const unsigned char *from, >> + unsigned char *to, RSA *rsa, >> + int padding)) >> +{ >> + if (meth) >> + { >> + meth->rsa_priv_dec = priv_dec; >> + return 1; >> + } >> + return 0; >> +} >> +#endif >> + >> +#if !defined(HAVE_RSA_METH_SET_INIT) >> +/** >> + * Set the init function of an RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + * @param init the init function >> + * @return 1 on success, 0 on error >> + */ >> +static inline int >> +RSA_meth_set_init(RSA_METHOD *meth, int (*init) (RSA *rsa)) >> +{ >> + if (meth) >> + { >> + meth->init = init; >> + return 1; >> + } >> + return 0; >> +} >> +#endif >> + >> +#if !defined(HAVE_RSA_METH_SET_FINISH) >> +/** >> + * Set the finish function of an RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + * @param finish the finish function >> + * @return 1 on success, 0 on error >> + */ >> +static inline int >> +RSA_meth_set_finish(RSA_METHOD *meth, int (*finish) (RSA *rsa)) >> +{ >> + if (meth) >> + { >> + meth->finish = finish; >> + return 1; >> + } >> + return 0; >> +} >> +#endif >> + >> +#if !defined(HAVE_RSA_METH_SET0_APP_DATA) >> +/** >> + * Set the application data of an RSA_METHOD object >> + * >> + * @param meth The RSA_METHOD object >> + * @param app_data Application data >> + * @return 1 on success, 0 on error >> + */ >> +static inline int >> +RSA_meth_set0_app_data(RSA_METHOD *meth, void *app_data) >> +{ >> + if (meth) >> + { >> + meth->app_data = app_data; >> + return 1; >> + } >> + return 0; >> +} >> +#endif >> + >> #endif /* OPENSSL_COMPAT_H_ */ >> diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c >> index >> bf0f643f25439f71cbfe71bf5a7e8eb834b0f012..f011e06702529ff34e91f6d0169d1adf8cc9d767 >> 100644 >> --- a/src/openvpn/ssl_openssl.c >> +++ b/src/openvpn/ssl_openssl.c >> @@ -978,7 +978,7 @@ rsa_priv_dec(int flen, const unsigned char *from, >> unsigned char *to, RSA *rsa, i >> static int >> rsa_finish(RSA *rsa) >> { >> - free((void *)rsa->meth); >> + RSA_meth_free(rsa->meth); >> rsa->meth = NULL; >> return 1; >> } > > This change still works, but he follow up change in this method in 07/15 > causes problems: > > static int > rsa_finish(RSA *rsa) > { > - RSA_meth_free(rsa->meth); > - rsa->meth = NULL; > + RSA_METHOD *meth = (RSA_METHOD *)RSA_get_method(rsa); > + RSA_meth_free(meth); > + RSA_set_method(rsa, NULL); > return 1; > } > > Casting away const on the object returned by RSA_get_method() is a > smell, but it really fails because RSA_set_method(rsa, NULL) calls > rsa->meth->finish(), which is implemented by this very function. That > means that RSA_meth_free() will perform a double free on meth->name. > > I briefly looked into a fix, but didn't immediately see a nice solution > here. At least the RSA_set_method(rsa, RSA_get_default_method()) > doesn't work either, because you'll end up with rsa_finish() and > RSA_set_method() calling each other infinitely... Ouch. I failed to see that as well. Sorry :) I'll try to find a better solution to this issue as well. > (Noting this here, because the fix for 07 might cause changes to this > patch too.) > >> @@ -1053,16 +1053,16 @@ tls_ctx_use_external_private_key(struct tls_root_ctx >> *ctx, >> ASSERT(NULL != cert); >> >> /* allocate custom RSA method object */ >> - ALLOC_OBJ_CLEAR(rsa_meth, RSA_METHOD); >> - rsa_meth->name = "OpenVPN external private key RSA Method"; >> - rsa_meth->rsa_pub_enc = rsa_pub_enc; >> - rsa_meth->rsa_pub_dec = rsa_pub_dec; >> - rsa_meth->rsa_priv_enc = rsa_priv_enc; >> - rsa_meth->rsa_priv_dec = rsa_priv_dec; >> - rsa_meth->init = NULL; >> - rsa_meth->finish = rsa_finish; >> - rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; >> - rsa_meth->app_data = NULL; >> + rsa_meth = RSA_meth_new("OpenVPN external private key RSA Method", >> + RSA_METHOD_FLAG_NO_CHECK); >> + check_malloc_return(rsa_meth); >> + RSA_meth_set_pub_enc(rsa_meth, rsa_pub_enc); >> + RSA_meth_set_pub_dec(rsa_meth, rsa_pub_dec); >> + RSA_meth_set_priv_enc(rsa_meth, rsa_priv_enc); >> + RSA_meth_set_priv_dec(rsa_meth, rsa_priv_dec); >> + RSA_meth_set_init(rsa_meth, NULL); >> + RSA_meth_set_finish(rsa_meth, rsa_finish); >> + RSA_meth_set0_app_data(rsa_meth, NULL); >> >> /* allocate RSA object */ >> rsa = RSA_new(); >> > > -Steffan > Best regards, -- Emmanuel Deloget ------------------------------------------------------------------------------ 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