feat(core): Support older GnuTLS versions

Add version checking to GnuTLS and use manual host name verification
in case GnuTLS does not support this natively.

Because older GnuTLS versions do not support using the system trust,
add a new configuration option to pass the system CA bundle. This option
can also search for the default locations. If none is given, but the
the GnuTLS version does not support loading the default system trust,
compilation will fail.
pull/56/head
Nicolas Höft 2020-06-18 22:40:53 +02:00
parent d79191fe91
commit 059a4075d9
3 changed files with 172 additions and 30 deletions

90
configure vendored
View File

@ -18,6 +18,7 @@ ARG_FRAMEWORK_DIR=""
ARG_GSMAKE=`gnustep-config --variable=GNUSTEP_MAKEFILES`
ARG_CFGMAKE="$PWD/config.make"
ARG_CFGSSL="auto"
ARG_CABUNDLE="none"
ARG_FHSMAKE="$PWD/fhs-postinstall.make"
ARG_WITH_GNUSTEP=0
ARG_WITH_DEBUG=1
@ -75,6 +76,7 @@ Build flags:
--enable-debug turn on debugging and compile time warnings
--enable-strip turn on stripping of debug symbols
--with-ssl=SSL specify ssl library (none, ssl, gnutls, auto) [auto]
--ca-bundle=CA_BUNDLE specify path to ca bundle (none, path, auto) [none]
--enable-xml Enable xml support (auto if unspecified)
--enable-mysql Enable mysql support (auto if unspecified)
--enable-postgresql Enable postgresql support (auto if unspecified)
@ -92,24 +94,24 @@ printParas() {
if test $ARG_BEQUIET = 1; then echo " will be quite."; fi
if test $ARG_NOCREATE = 1; then echo " won't create files"; fi
if test "x$ARG_FRAMEWORK_DIR" != "x"; then
echo " FHS: install in frameworks directory";
echo " FHS: install in frameworks directory";
elif test $DARG_IS_FHS = 1; then
echo " FHS: install in FHS root";
echo " FHS: install in FHS root";
else
echo " FHS: install in GNUstep tree";
echo " FHS: install in GNUstep tree";
fi
if test $ARG_WITH_DEBUG = 1; then
if test $ARG_WITH_DEBUG = 1; then
echo " debug: yes";
else
echo " debug: no";
fi
if test $ARG_WITH_STRIP = 1; then
if test $ARG_WITH_STRIP = 1; then
echo " strip: yes";
else
echo " strip: no";
fi
echo " prefix: $ARG_PREFIX"
echo " frameworks: $ARG_FRAMEWORK_DIR"
echo " gstep: $ARG_GSMAKE"
@ -155,9 +157,9 @@ setupInternalGSMake() {
ENABLE_PCH_OPT="--enable-pch"
fi
pregsmdir="$PWD"
echo -n "configuring builtin gnustep-make environment (${SETUP_LOGNAME}) .."
cd "$GSTEPMAKE_SRCDIR"
./configure >${pregsmdir}/${SETUP_LOGNAME} \
${ENABLE_PCH_OPT} \
@ -171,7 +173,7 @@ setupInternalGSMake() {
echo -n ".. install .."
$MAKE install >>${pregsmdir}/${SETUP_LOGNAME}
ARG_GSMAKE="${INTERNAL_MAKEDIR}/Library/Makefiles/"
ARG_IS_FHS=1
DARG_IS_FHS=1
@ -180,7 +182,7 @@ setupInternalGSMake() {
if test "x$ARG_PREFIX" = "x"; then
ARG_PREFIX="/usr/local/"
fi
cd "$pregsmdir"
if test -f $ARG_GSMAKE/GNUstep.sh; then
echo ".. done (log in ${SETUP_LOGNAME})."
@ -229,7 +231,7 @@ setupAppleArgs() {
if test "x${USES_INTERNAL_MAKE}" = "no"; then
ARG_WITH_GNUSTEP=1
fi
# no reason to print a warning?
#if test "x${xLIBRARY_COMBO}" != "xapple-apple-nil"; then
# if test "x${LIBRARY_COMBO}" != "xapple-apple-apple"; then
@ -285,7 +287,7 @@ validateArgs() {
fi
;;
esac
if test $ARG_WITH_GNUSTEP = 1; then
if test $DARG_IS_FHS = 1; then
echo "error: configured for FHS root _and_ GNUstep tree. Choose one!"
@ -316,17 +318,17 @@ genConfigMake() {
# DYLD_LIBRARY_PATH
# GUILE_LOAD_PATH
# CLASSPATH
if test $ARG_BEQUIET != 1; then
echo "creating: $ARG_CFGMAKE"
fi
echo "# GNUstep environment configuration" > "${ARG_CFGMAKE}"
cfgwrite "# created by: '$CFG_ARGS'"
cfgwrite ""
cfgwrite "SOPE_ROOT=`pwd | sed 's/ /\\\ /g'`"
cfgwrite "include \${SOPE_ROOT}/Version"
cfgwrite "# Note: you can override any option as a 'make' parameter, eg:"
cfgwrite "# make debug=yes"
cfgwrite ""
@ -336,7 +338,7 @@ genConfigMake() {
#cfgwrite "all :: "
#cfgwrite " @echo Local GNUstep config.make is active"
#cfgwrite ""
# Note: GNUSTEP_TARGET_CPU is not yet available (set by common.make), so we
# only have environment variables
# Note: we can't set SYSTEM_LIB_DIR in this location, it gets overridden by
@ -358,8 +360,8 @@ genConfigMake() {
cfgwrite "endif"
cfgwrite "GNUSTEP_INSTALLATION_DOMAIN:=LOCAL"
cfgwrite "CONFIGURE_SYSTEM_LIB_DIR += -L/usr/\$(CGS_LIBDIR_NAME)/"
if test "x$ARG_FRAMEWORK_DIR" != "x"; then
cfgwrite "# configured to install in Frameworks directory"
cfgwrite "FRAMEWORK_INSTALL_DIR:=${ARG_FRAMEWORK_DIR}"
@ -396,7 +398,7 @@ genConfigMake() {
cfgwrite "SOPE_TOOLS=\${GNUSTEP_TOOLS}"
cfgwrite "SOPE_ADMIN_TOOLS=\${GNUSTEP_ADMIN_TOOLS}"
fi
if test $ARG_WITH_DEBUG = 1; then
cfgwrite "# configured to produce debugging code";
cfgwrite "debug:=yes"
@ -406,7 +408,7 @@ genConfigMake() {
cfgwrite "debug:=no"
fi
cfgwrite ""
if test $ARG_WITH_STRIP = 1; then
cfgwrite "# configured to produce stripped code";
cfgwrite "strip:=yes"
@ -419,7 +421,7 @@ genConfigMake() {
cfgwrite "# enforce shared libraries";
cfgwrite "shared:=yes"
cfgwrite ""
cfgwrite "# GNUstep environment variables:";
for i in `env | grep GNUSTEP_ | sort`; do
MAKE_ASSI="`echo $i | sed s/=/:=/`"
@ -434,7 +436,7 @@ checkLinking() {
# library-name => $1, type => $2
local oldpwd="${PWD}"
local tmpdir=".configure-test-$$"
mkdir $tmpdir
cd $tmpdir
cp ../maintenance/dummytool.c .
@ -443,7 +445,7 @@ checkLinking() {
for LIB in $1;do
LIBS="$LIBS -l${LIB}"
done
tmpmake="GNUmakefile"
echo >$tmpmake "-include ../config.make"
echo >>$tmpmake "include \$(GNUSTEP_MAKEFILES)/common.make"
@ -457,10 +459,10 @@ checkLinking() {
echo >>$tmpmake "SYSTEM_LIB_DIR += \$(CONFIGURE_SYSTEM_LIB_DIR)"
echo >>$tmpmake "SYSTEM_LIB_DIR += ${LINK_SYSLIBDIRS}"
echo >>$tmpmake "include \$(GNUSTEP_MAKEFILES)/ctool.make"
$MAKE -s messages=yes -f $tmpmake linktest >out.log 2>err.log
LINK_RESULT=$?
if test $LINK_RESULT = 0; then
echo "$2 library found: $1"
cfgwrite "HAS_LIBRARY_$1=yes"
@ -475,7 +477,7 @@ checkLinking() {
LIBS=$OLDLIBS
fi
fi
cd "${oldpwd}"
rm -rf $tmpdir
@ -514,6 +516,32 @@ checkDependencies() {
checkLinking "gnutls" required;
fi
if test "x$ARG_CABUNDLE" = "xauto"; then
while read f; do
if test -f $f; then
echo "found $f"
ARG_CABUNDLE="$f"
break
fi
# we need this odd syntax in order not to spawn a sub
# shell
done << EOF
/etc/ssl/certs/ca-certificates.crt
/etc/pki/tls/certs/ca-bundle.crt
/usr/share/ssl/certs/ca-bundle.crt
/usr/local/share/certs/ca-root-nss.crt
/etc/ssl/cert.pem
EOF
if test "x$ARG_CABUNDLE" = "xauto"; then
echo "failed to find CA root store"
exit 1
fi
fi
if test "x$ARG_CABUNDLE" != "xnone"; then
cfgwrite "CA_BUNDLE=$ARG_CABUNDLE"
echo "Using CA Bundle: $ARG_CABUNDLE"
fi
if test "x$ARG_WITH_POSTGRESQL" = "xauto" ; then
checkLinking "pq" optional;
elif test $ARG_WITH_POSTGRESQL = 1 ; then
@ -538,15 +566,15 @@ runIt() {
if test $ARG_BEQUIET != 1; then
printParas;
fi
if test $ARG_NOCREATE = 1; then
if test $ARG_NOCREATE = 1; then
if test $ARG_BEQUIET != 1; then
echo "not creating the config file ...";
fi
else
genConfigMake;
checkDependencies;
if test -x "${NGSTREAMS_DIR}/configure"; then
if test $ARG_BEQUIET != 1; then
echo -n "configuring NGStreams library .."
@ -613,6 +641,10 @@ processOption() {
extractFuncValue $1;
ARG_CFGSSL="$VALUE"
;;
x--ca-bundle=*)
extractFuncValue $1;
ARG_CABUNDLE="$VALUE"
;;
"x--enable-mysql")
ARG_WITH_MYSQL=1
;;

View File

@ -60,6 +60,11 @@ NGStreams_LIBRARIES_DEPEND_UPON += -lssl -lcrypto
endif
endif
# pass CA bundle
ifdef CA_BUNDLE
ADDITIONAL_CPPFLAGS += -DCA_BUNDLE="\"$(CA_BUNDLE)\""
endif
ADDITIONAL_CPPFLAGS += -Wall -Wno-protocol
# reentrant

View File

@ -57,6 +57,7 @@ DEALINGS IN THE SOFTWARE.
#if HAVE_GNUTLS
# include <gnutls/gnutls.h>
# include <gnutls/x509.h>
#define LOOP_CHECK(rval, cmd) \
do { \
rval = cmd; \
@ -106,6 +107,90 @@ DEALINGS IN THE SOFTWARE.
}
#if HAVE_GNUTLS
/* This function will verify the peer's certificate, and check
* if the hostname matches, as well as the activation, expiration dates.
*/
static int
_verify_certificate_callback (gnutls_session_t session)
{
unsigned int status;
const gnutls_datum_t *cert_list;
unsigned int cert_list_size;
int ret;
gnutls_x509_crt_t cert;
const char *hostname;
/* read hostname */
hostname = gnutls_session_get_ptr (session);
/* This verification function uses the trusted CAs in the credentials
* structure. So you must have installed one or more CA certificates.
*/
ret = gnutls_certificate_verify_peers2 (session, &status);
if (ret < 0)
{
NSLog(@"ERROR: verify_peer2 failed");
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
NSLog(@"ERROR: The certificate hasn't got a known issuer.");
if (status & GNUTLS_CERT_REVOKED)
NSLog(@"ERROR: The certificate has been revoked.");
if (status & GNUTLS_CERT_EXPIRED)
NSLog(@"ERROR: The certificate has expired");
if (status & GNUTLS_CERT_NOT_ACTIVATED)
NSLog(@"ERROR: The certificate is not yet activated");
if (status & GNUTLS_CERT_INVALID)
{
NSLog(@"ERROR: The certificate is not trusted.");
return GNUTLS_E_CERTIFICATE_ERROR;
}
/* Up to here the process is the same for X.509 certificates and
* OpenPGP keys. From now on X.509 certificates are assumed. This can
* be easily extended to work with openpgp keys as well.
*/
if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
return GNUTLS_E_CERTIFICATE_ERROR;
if (gnutls_x509_crt_init (&cert) < 0)
{
NSLog(@"WARNING: could not initialize gnutls certificate");
return GNUTLS_E_CERTIFICATE_ERROR;
}
cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
if (cert_list == NULL)
{
NSLog(@"WARNING: GnuTLS certificate not found");
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
{
NSLog(@"WARNING: Unable to parse certificate");
return GNUTLS_E_CERTIFICATE_ERROR;
}
if (!gnutls_x509_crt_check_hostname (cert, hostname))
{
NSLog(@"ERROR: Certificate does not match hostname '%s'", hostname);
return GNUTLS_E_CERTIFICATE_ERROR;
}
gnutls_x509_crt_deinit (cert);
/* notify gnutls to continue handshake normally */
return 0;
}
- (id)initWithDomain:(id<NGSocketDomain>)_domain
onHostName: (NSString *)_hostName
{
@ -132,8 +217,13 @@ DEALINGS IN THE SOFTWARE.
[self release];
return nil;
}
#ifdef CA_BUNDLE
ret = gnutls_certificate_set_x509_trust_file(self->cred, CA_BUNDLE, GNUTLS_X509_FMT_PEM);
#elif GNUTLS_VERSION_NUMBER >= 0x030020
ret = gnutls_certificate_set_x509_system_trust(self->cred);
#else
#error "Cant use default system trust, CA_BUNDLE needs to be passed"
#endif
if (ret < 0)
{
NSLog(@"ERROR(%s): could not set GnuTLS system trust (%s)",
@ -142,6 +232,10 @@ DEALINGS IN THE SOFTWARE.
return nil;
}
#if GNUTLS_VERSION_NUMBER < 0x030406
gnutls_certificate_set_verify_function(self->cred, _verify_certificate_callback);
#endif /* GNUTLS_VERSION_NUMBER >= 0x030406*/
self->session = NULL;
}
return self;
@ -232,7 +326,11 @@ DEALINGS IN THE SOFTWARE.
return NO;
}
#if GNUTLS_VERSION_NUMBER >= 0x030406
gnutls_session_set_verify_cert(sess, [hostName UTF8String], 0);
#else
gnutls_session_set_ptr(session, (void *) [hostName UTF8String]);
#endif /* GNUTLS_VERSION_NUMBER >= 0x030406*/
#if GNUTLS_VERSION_NUMBER < 0x030109
gnutls_transport_set_ptr(sess, (gnutls_transport_ptr_t)(long)self->fd);
@ -240,7 +338,9 @@ DEALINGS IN THE SOFTWARE.
gnutls_transport_set_int(sess, self->fd);
#endif /* GNUTLS_VERSION_NUMBER < 0x030109 */
#if GNUTLS_VERSION_NUMBER >= 0x030100
gnutls_handshake_set_timeout(sess, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
#endif
do {
ret = gnutls_handshake(sess);
} while (ret < 0 && gnutls_error_is_fatal(ret) == 0);
@ -404,7 +504,12 @@ static int cert_verify_callback(X509_STORE_CTX *x509_ctx, void *arg)
return nil;
}
// use system default trust store
#ifdef CA_BUNDLE
SSL_CTX_load_verify_locations(self->ctx, CA_BUNDLE, NULL);
#else
SSL_CTX_set_default_verify_paths(self->ctx);
#endif // CA_BUNDLE
if ((self->ssl = SSL_new(self->ctx)) == NULL) {
// should set exception !