diff --git a/configure b/configure index 9cefbe2..0789ae6 100755 --- a/configure +++ b/configure @@ -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 ;; diff --git a/sope-core/NGStreams/GNUmakefile.preamble b/sope-core/NGStreams/GNUmakefile.preamble index 5f85e65..a680f8c 100644 --- a/sope-core/NGStreams/GNUmakefile.preamble +++ b/sope-core/NGStreams/GNUmakefile.preamble @@ -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 diff --git a/sope-core/NGStreams/NGActiveSSLSocket.m b/sope-core/NGStreams/NGActiveSSLSocket.m index 78b495f..88c4d5f 100644 --- a/sope-core/NGStreams/NGActiveSSLSocket.m +++ b/sope-core/NGStreams/NGActiveSSLSocket.m @@ -57,6 +57,7 @@ DEALINGS IN THE SOFTWARE. #if HAVE_GNUTLS # include +# include #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)_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 !