I am attaching a fix.
Still needs some discussion.
This patch does the following two checks:
1) Checks if the SSL_get_certificate is buggy
2) Checks it he workaround can be enabled.
Inside squid:
If the workaround can be used, enable it
else if the SSL_get_certificate is not buggy, use it
else hit an assertion
I select this approach:
1) because the workaround is significant faster than using the
SSL_get_certificate
2) to avoid the segfault if the SSL_get_certificate is buggy .
Problems:
I had problem with the LD_LIBRARY_PATH. For example if the user does
not want to use system libraries and use openSSL SDK installed under a
non standard directory, the test program will run with system libraries.
To avoid this someone should use the LD_LIBRARY_PATH in configure script:
./configure --with-openssl=/path/to/openssl/
LD_LIBRARY_PATH=/path/to/openssl/
I do not like this option, so in the test I am using the -wl,-rpath
compiler option to pass the openSSL libraries path.
But this option does not looks good too..
Also we may want to harden the workaround test to use a hardcoded
certificate instead of a NULL certificate. (I attached an example in a
previous mail)
Regards,
Christos
On 05/10/2013 06:18 PM, Alex Rousskov wrote:
> On 05/10/2013 09:03 AM, Tsantilas Christos wrote:
>> I am attaching a small utility which does all possible checks we can do.
>>
>>
>> On 05/10/2013 12:01 AM, Alex Rousskov wrote:
>>> On 05/09/2013 01:10 PM, Tsantilas Christos wrote:
>>>> On 05/09/2013 05:50 PM, Alex Rousskov wrote:
>>>> I can reporoduce the bug with the following simple program:
>>>>
>>>> int main(int argc, char *argv[])
>>>> {
>>>> SSLeay_add_ssl_algorithms();
>>>> SSL_CTX *sslContext = SSL_CTX_new(SSLv3_method());
>>>> SSL *ssl = SSL_new(sslContext);
>>>> X509 * cert = SSL_get_certificate(ssl);
>>>> return 0;
>>>> }
>>>>
>>>> This program crashes inside a function called by SSL_get_certificate.
>>>> I suppose we can check if the program exited normally or not, to decide
>>>> if the openSSL SDK is OK or have the bug.
>>>
>>> That sounds like a good idea to me. I would extend that test code to
>>> also include code to test that our workaround compiles. Here is a sketch:
>>>
>>> int main() {
>>> ...
>>> X509 *cert1 = workaroundCode(...);
>>> X509 *cert2 = directCode(...); // may crash!
>>> return cert1 == cert2 ? 0 : 1;
>>> }
>>>
>>> I assume that both cert1 and cert2 will be nil in your test case when
>>> everything works correctly, but that does not matter.
>>
>> Well checking with null certificates maybe is not enough good to detect
>> problems. The SSL and SSL_CTX structures used here mostly uninitialized
>> so it is possible to get a wrong field which is just zeroed.
>>
>> But maybe we can add in the check program a hard coded certificate. I am
>> using it in the check toll I am attaching.
>> Then to check if the workaround code works required the following:
>> const char *certTxt = "-----BEGIN CERTIFICATE----\n" .....;
>>
>> SSLeay_add_ssl_algorithms();
>> BIO *certBio = BIO_new(BIO_s_mem());
>> BIO_puts(certBio, certTxt);
>> X509 * cert = NULL;
>> PEM_read_bio_X509(certBio, &cert, 0, 0));
>> SSL_CTX *sslContext = SSL_CTX_new(SSLv3_method());
>> SSL_CTX_use_certificate(sslContext, cert));
>> X509 ***pCert = (X509 ***)sslContext->cert;
>> X509 *sslCtxCert = pCert && *pCert ? **pCert : NULL;
>> return (sslCtxCert != cert ? 1 : 0);
>>
>>
>>>
>>> Then, we can use one of the following two strategies:
>>>
>>> Conservative (work around the bug if possible and clearly necessary):
>>> If the test compiles but crashes, enable workaround.
>>> Otherwise, disable workaround.
>>>
>>> Midway (work around the bug if necessary or if work around works):
>>> If the test compiles but crashes, enable workaround.
>>> If the test compiles and returns 0, enable workaround.
>>> Otherwise, disable workaround.
>>>
>>> Aggressive (work around the bug if possible):
>>> If the test compiles, enable workaround.
>>> Otherwise, disable workaround.
>>>
>>>
>>> I think we should use either the Midway or the Aggressive approach to
>>> accommodate more cases, but I am not sure. What do you think?
>>
>> I think the best is the aggressive with some additions:
>>
>> - Check if the workaround work, using a code similar to the above.
>> - If not work try to use SSL object and SSL_get_certificate function. If
>> this one crashes, disable SSL bumping (only here needed).
>
> I am worried that we are over-engineering this, which will result in
> test failures for reasons other than OpenSSL bugs. However, if you
> prefer to add real certificate check, I am not objecting to it. If we do
> run into problems with this more complex check, we can backtrack and
> simplify it. Just do whatever you think is best.
>
>
> Thank you,
>
> Alex.
>
>
=== added file 'acinclude/ssl_get_certificate.m4'
--- acinclude/ssl_get_certificate.m4 1970-01-01 00:00:00 +0000
+++ acinclude/ssl_get_certificate.m4 2013-05-13 17:46:04 +0000
@@ -0,0 +1,92 @@
+dnl
+dnl AUTHOR: Squid Web Cache team
+dnl
+dnl SQUID Web Proxy Cache http://www.squid-cache.org/
+dnl ----------------------------------------------------------
+dnl Squid is the result of efforts by numerous individuals from
+dnl the Internet community; see the CONTRIBUTORS file for full
+dnl details. Many organizations have provided support for Squid's
+dnl development; see the SPONSORS file for full details. Squid is
+dnl Copyrighted (C) 2001 by the Regents of the University of
+dnl California; see the COPYRIGHT file for full details. Squid
+dnl incorporates software developed and/or copyrighted by other
+dnl sources; see the CREDITS file for full details.
+dnl
+dnl This program is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl
+dnl This program is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+dnl GNU General Public License for more details.
+dnl
+dnl You should have received a copy of the GNU General Public License
+dnl along with this program; if not, write to the Free Software
+dnl Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
+
+dnl Checks whether the OpenSSL SSL_get_certificate crashes squid and if a
+dnl workaround can be used instead of using the SSL_get_certificate
+
+AC_DEFUN([SQUID_CHECK_OPENSSL_GETCERTIFICATE_WORKS],[
+ AH_TEMPLATE(SQUID_SSLGETCERTIFICATE_BUGGY, "Define to 1 if the SSL_get_certificate crashes squid")
+ AH_TEMPLATE(SQUID_USE_SSLGETCERTIFICATE_HACK, "Define to 1 to use squid workaround for SSL_get_certificate")
+ OLDLIBS="$LIBS"
+ LIBS="$LIBS $SSLLIB"
+ if test "x$SSLLIBDIR" != "x"; then
+ LIBS="$LIBS -Wl,-rpath -Wl,$SSLLIBDIR"
+ fi
+
+ AC_MSG_CHECKING(whether the SSL_get_certificate is buggy)
+ AC_RUN_IFELSE([
+ AC_LANG_PROGRAM(
+ [
+ #include <openssl/ssl.h>
+ #include <openssl/err.h>
+ ],
+ [
+ SSLeay_add_ssl_algorithms();
+ SSL_CTX *sslContext = SSL_CTX_new(SSLv3_method());
+ SSL *ssl = SSL_new(sslContext);
+ X509* cert = SSL_get_certificate(ssl);
+ return 0;
+ ])
+ ],
+ [
+ AC_MSG_RESULT([no])
+ ],
+ [
+ AC_DEFINE(SQUID_SSLGETCERTIFICATE_BUGGY, 1)
+ AC_MSG_RESULT([yes])
+ ],
+ [])
+
+ AC_MSG_CHECKING(whether the workaround for SSL_get_certificate works)
+ AC_RUN_IFELSE([
+ AC_LANG_PROGRAM(
+ [
+ #include <openssl/ssl.h>
+ #include <openssl/err.h>
+ ],
+ [
+ SSLeay_add_ssl_algorithms();
+ SSL_CTX *sslContext = SSL_CTX_new(SSLv3_method());
+ X509 ***pCert = (X509 ***)sslContext->cert;
+ X509 *sslCtxCert = pCert && *pCert ? **pCert : (X509 *)0x1;
+ if (sslCtxCert != NULL)
+ return 1;
+ return 0;
+ ])
+ ],
+ [
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(SQUID_USE_SSLGETCERTIFICATE_HACK, 1)
+ ],
+ [
+ AC_MSG_RESULT([no])
+ ],
+[])
+
+LIBS=$OLDLIBS
+])
=== modified file 'configure.ac'
--- configure.ac 2013-01-22 06:29:59 +0000
+++ configure.ac 2013-05-13 15:50:41 +0000
@@ -1,40 +1,41 @@
AC_INIT([Squid Web Proxy],[3.HEAD-BZR],[http://bugs.squid-cache.org/],[squid])
AC_PREREQ(2.61)
AC_CONFIG_HEADERS([include/autoconf.h])
AC_CONFIG_AUX_DIR(cfgaux)
AC_CONFIG_SRCDIR([src/main.cc])
AM_INIT_AUTOMAKE([tar-ustar nostdinc])
AC_REVISION($Revision$)dnl
AC_PREFIX_DEFAULT(/usr/local/squid)
AM_MAINTAINER_MODE
m4_include([acinclude/init.m4])
m4_include([acinclude/squid-util.m4])
m4_include([acinclude/compiler-flags.m4])
m4_include([acinclude/os-deps.m4])
m4_include([acinclude/krb5.m4])
m4_include([acinclude/pam.m4])
m4_include([acinclude/pkg.m4])
m4_include([acinclude/lib-checks.m4])
m4_include([acinclude/ax_cxx_compile_stdcxx_0x.m4])
m4_include([acinclude/ax_cxx_0x_types.m4])
+m4_include([acinclude/ssl_get_certificate.m4])
PRESET_CFLAGS="$CFLAGS"
PRESET_CXXFLAGS="$CXXFLAGS"
PRESET_LDFLAGS="$LDFLAGS"
dnl Set default LDFLAGS
if test "x$LDFLAGS" = "x" ; then
LDFLAGS="-g"
fi
# Check for GNU cc
AC_PROG_CC
AM_PROG_CC_C_O
AC_PROG_CXX
AC_LANG([C++])
AC_CANONICAL_HOST
# might be cross-compiling
if test "x$HOSTCXX" = "x"; then
HOSTCXX="$CXX"
@@ -1238,40 +1239,43 @@
[Define this to include code for SSL gatewaying support])
AC_MSG_NOTICE([Using OpenSSL MD5 implementation: ${with_openssl:=no}])
SQUID_DEFINE_BOOL(USE_OPENSSL,${with_openssl},
[Define this to make use of the OpenSSL libraries for MD5 calculation rather than Squid-supplied MD5 implementation or if building with SSL encryption])
if test "x$enable_ssl" = "xyes"; then
if test "x$SSLLIB" = "x"; then
SSLLIB="-lcrypto" # for MD5 routines
fi
# This is a workaround for RedHat 9 brain damage..
if test -d /usr/kerberos/include -a "x$SSLLIBDIR" = "x" -a -f /usr/include/openssl/kssl.h; then
AC_MSG_NOTICE([OpenSSL depends on Kerberos])
SSLLIBDIR="/usr/kerberos/lib"
CPPFLAGS="$CPPFLAGS -I/usr/kerberos/include"
fi
fi
if test "x$SSLLIBDIR" != "x" ; then
SSLLIB="-L$SSLLIBDIR $SSLLIB"
fi
AC_SUBST(SSLLIB)
+if test "x$with_openssl" = "xyes"; then
+SQUID_CHECK_OPENSSL_GETCERTIFICATE_WORKS
+fi
AC_ARG_ENABLE(forw-via-db,
AS_HELP_STRING([--enable-forw-via-db],[Enable Forw/Via database]), [
SQUID_YESNO([$enableval],[unrecognized argument to --enable-forw-via-db: $enableval])
])
SQUID_DEFINE_BOOL(USE_FORW_VIA_DB,${enable_forw_via_db:=no},
[Enable Forw/Via database])
AC_MSG_NOTICE([Forw/Via database enabled: $enable_forw_via_db])
AC_ARG_ENABLE(cache-digests,
AS_HELP_STRING([--enable-cache-digests],
[Use Cache Digests. See http://wiki.squid-cache.org/SquidFaq/CacheDigests]),
[
SQUID_YESNO($enableval,
[unrecognized argument to --enable-cache-digests: $enableval])
])
SQUID_DEFINE_BOOL(USE_CACHE_DIGESTS,${enable_cache_digests:=no},
[Use Cache Digests for locating objects in neighbor caches.])
AC_MSG_NOTICE([Cache Digests enabled: $enable_cache_digests])
=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc 2013-01-30 15:39:37 +0000
+++ src/ssl/support.cc 2013-05-13 18:01:30 +0000
@@ -1433,43 +1433,55 @@
SSL_CTX *
Ssl::generateSslContext(CertificateProperties const &properties, AnyP::PortCfg &port)
{
Ssl::X509_Pointer cert;
Ssl::EVP_PKEY_Pointer pkey;
if (!generateSslCertificate(cert, pkey, properties))
return NULL;
if (!cert)
return NULL;
if (!pkey)
return NULL;
return createSSLContext(cert, pkey, port);
}
bool Ssl::verifySslCertificate(SSL_CTX * sslContext, CertificateProperties const &properties)
{
+ // SSL_get_certificate is buggy in openssl versions 1.0.1d and 1.0.1e
+ // Try to retrieve certificate directly from SSL_CTX object
+#if defined(SQUID_USE_SSLGETCERTIFICATE_HACK)
+ X509 ***pCert = (X509 ***)sslContext->cert;
+ X509 * cert = pCert && *pCert ? **pCert : NULL;
+#elif defined(SQUID_SSLGETCERTIFICATE_BUGGY)
+ X509 * cert = NULL;
+ assert(0);
+#else
// Temporary ssl for getting X509 certificate from SSL_CTX.
Ssl::SSL_Pointer ssl(SSL_new(sslContext));
X509 * cert = SSL_get_certificate(ssl.get());
+#endif
+ if (!cert)
+ return false;
ASN1_TIME * time_notBefore = X509_get_notBefore(cert);
ASN1_TIME * time_notAfter = X509_get_notAfter(cert);
bool ret = (X509_cmp_current_time(time_notBefore) < 0 && X509_cmp_current_time(time_notAfter) > 0);
if (!ret)
return false;
return certificateMatchesProperties(cert, properties);
}
bool
Ssl::setClientSNI(SSL *ssl, const char *fqdn)
{
//The SSL_CTRL_SET_TLSEXT_HOSTNAME is a openssl macro which indicates
// if the TLS servername extension (SNI) is enabled in openssl library.
#if defined(SSL_CTRL_SET_TLSEXT_HOSTNAME)
if (!SSL_set_tlsext_host_name(ssl, fqdn)) {
const int ssl_error = ERR_get_error();
debugs(83, 3, "WARNING: unable to set TLS servername extension (SNI): " <<
ERR_error_string(ssl_error, NULL) << "\n");
return false;