Commit f89a643e authored by David Woodhouse's avatar David Woodhouse

Allow building against GnuTLS (for TCP) and GnuTLS (for DTLS) simultaneously

Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 1fdc2982
......@@ -14,8 +14,8 @@ man8_MANS = openconnect.8
AM_CPPFLAGS = -DLOCALEDIR="\"$(localedir)\""
openconnect_SOURCES = xml.c main.c dtls.c cstp.c mainloop.c tun.c
openconnect_CFLAGS = $(SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS)
openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(ZLIB_LIBS) $(LIBINTL)
openconnect_CFLAGS = $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS)
openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(DTLS_SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(ZLIB_LIBS) $(LIBINTL)
library_srcs = ssl.c http.c auth.c library.c compat.c @SSL_LIBRARY@.c
libopenconnect_la_SOURCES = version.c $(library_srcs)
......
......@@ -179,6 +179,17 @@ if test "$USE_NLS" = "yes"; then
fi
AM_CONDITIONAL(USE_NLS, [test "$USE_NLS" = "yes"])
# We will use GnuTLS if it's requested, and if GnuTLS doesn't have DTLS
# support then we'll *also* use OpenSSL for that, but it appears *only*
# only in the openconnect executable and not the library (hence shouldn't
# be a problem for GPL'd programs using libopenconnect).
#
# If built with --with-gnutls --without-openssl then we'll even eschew
# OpenSSL for DTLS support and will build without any DTLS support at all
# if GnuTLS cannot manage.
#
# The default (for now) is to use OpenSSL for everything.
AC_ARG_WITH([gnutls],
AS_HELP_STRING([--with-gnutls],
[Use GnuTLS instead of OpenSSL (EXPERIMENTAL)]))
......@@ -187,24 +198,46 @@ AC_ARG_WITH([openssl],
[Location of OpenSSL build dir]))
ssl_library=
if test "$with_gnutls" = "yes" || test "$with_gnutls" = "shibboleet"; then
if test "$with_openssl" != "no" && test "$with_openssl" != ""; then
AC_MSG_ERROR([Cannot use both OpenSSL and GnuTLS simultaneously])
fi
if test "$with_gnutls" = "yes"; then
PKG_CHECK_MODULES(GNUTLS, gnutls)
if ! $PKG_CONFIG --atleast-version=2.12.16 gnutls; then
AC_MSG_ERROR([Your GnuTLS is too old. At least v2.12.16 is required])
fi
with_openssl=no
ssl_library=gnutls
oldlibs="$LIBS"
LIBS="$LIBS $GNUTLS_LIBS"
AC_CHECK_FUNC(gnutls_certificate_set_x509_system_trust,
[AC_DEFINE(HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST, 1)], [])
AC_CHECK_FUNC(gnutls_pkcs12_simple_parse,
[AC_DEFINE(HAVE_GNUTLS_PKCS12_SIMPLE_PARSE, 1)], [])
AC_CHECK_FUNC(gnutls_session_set_premaster,
[AC_DEFINE(HAVE_GNUTLS_SESSION_SET_PREMASTER, 1)], [])
if test "$with_openssl" != "" || test "$with_openssl" = "no"; then
AC_CHECK_FUNC(gnutls_session_set_premaster,
[have_gnutls_dtls=yes], [have_gnutls_dtls=no])
else
have_gnutls_dtls=no
fi
if test "$have_gnutls_dtls" = "yes"; then
if test "$with_openssl" = "" || test "$with_openssl" = "no"; then
# They either said no OpenSSL or didn't specify, and GnuTLS can
# do DTLS, so just use GnuTLS.
AC_DEFINE(HAVE_GNUTLS_SESSION_SET_PREMASTER, 1)
ssl_library=gnutls
with_openssl=no
else
# They specifically asked for OpenSSL, so use it for DTLS even
# though GnuTLS could manage.
ssl_library=both
fi
else
if test "$with_openssl" = "no"; then
# GnuTLS doesn't have DTLS, but they don't want OpenSSL. So build
# without DTLS support at all.
ssl_library=gnutls
else
# GnuTLS doesn't have DTLS so use OpenSSL for it, but GnuTLS for
# the TCP connection (and thus in the library).
ssl_library=both
fi
fi
AC_CHECK_FUNC(gnutls_pkcs11_add_provider,
[PKG_CHECK_MODULES(P11KIT, p11-kit-1, [AC_DEFINE(HAVE_P11KIT)
AC_SUBST(P11KIT_PC, p11-kit-1)], [:])], [])
......@@ -212,8 +245,7 @@ if test "$with_gnutls" = "yes" || test "$with_gnutls" = "shibboleet"; then
elif test "$with_gnutls" != "" && test "$with_gnutls" != "no"; then
AC_MSG_ERROR([Values other than 'yes' or 'no' for --with-gnutls are not supported])
fi
if test "$with_openssl" = "yes" || test "$with_openssl" = "" ; then
if test "$with_openssl" = "yes" || test "$with_openssl" = "" || test "$ssl_library" = "both"; then
PKG_CHECK_MODULES(OPENSSL, openssl, [],
[oldLIBS="$LIBS"
LIBS="$LIBS -lssl -lcrypto"
......@@ -229,9 +261,15 @@ if test "$with_openssl" = "yes" || test "$with_openssl" = "" ; then
AC_SUBST([OPENSSL_LIBS], ["-lssl -lcrypto"])
AC_SUBST([OPENSSL_CFLAGS], [])],
[AC_MSG_RESULT(no)
AC_ERROR([Could not build against OpenSSL])])
if test "$ssl_library" = "both"; then
ssl_library="gnutls";
else
AC_ERROR([Could not build against OpenSSL]);
fi])
LIBS="$oldLIBS"])
ssl_library=openssl
if test "$ssl_library" != "both" && test "$ssl_library" != "gnutls"; then
ssl_library=openssl
fi
elif test "$with_openssl" != "no" ; then
OPENSSL_CFLAGS="-I${with_openssl}/include"
OPENSSL_LIBS="${with_openssl}/libssl.a ${with_openssl}/libcrypto.a -ldl -lz"
......@@ -239,25 +277,41 @@ elif test "$with_openssl" != "no" ; then
AC_SUBST(OPENSSL_LIBS)
enable_static=yes
enable_shared=no
ssl_library=openssl
AC_DEFINE(DTLS_OPENSSL, 1)
if test "$ssl_library" != "both"; then
ssl_library=openssl
fi
fi
case "$ssl_library" in
gnutls)
AC_DEFINE(OPENCONNECT_GNUTLS, 1)
AC_SUBST(SSL_LIBS, [$GNUTLS_LIBS])
AC_SUBST(SSL_CFLAGS, [$GNUTLS_CFLAGS])
AC_DEFINE(DTLS_GNUTLS, 1)
AC_SUBST(SSL_LIBRARY, [gnutls])
AC_SUBST(SSL_LIBS, ['$(GNUTLS_LIBS)'])
AC_SUBST(SSL_CFLAGS, ['$(GNUTLS_CFLAGS)'])
;;
openssl)
AC_DEFINE(OPENCONNECT_OPENSSL, 1)
AC_SUBST(SSL_LIBS, [$OPENSSL_LIBS])
AC_SUBST(SSL_CFLAGS, [$OPENSSL_CFLAGS])
AC_DEFINE(DTLS_OPENSSL, 1)
AC_SUBST(SSL_LIBRARY, [openssl])
AC_SUBST(SSL_LIBS, ['$(OPENSSL_LIBS)'])
AC_SUBST(SSL_CFLAGS, ['$(OPENSSL_CFLAGS)'])
;;
both)
# GnuTLS for TCP, OpenSSL for DTLS
AC_DEFINE(OPENCONNECT_GNUTLS, 1)
AC_DEFINE(DTLS_OPENSSL, 1)
AC_SUBST(SSL_LIBRARY, [gnutls])
AC_SUBST(SSL_LIBS, ['$(GNUTLS_LIBS)'])
AC_SUBST(SSL_CFLAGS, ['$(GNUTLS_CFLAGS)'])
AC_SUBST(DTLS_SSL_LIBS, ['$(OPENSSL_LIBS)'])
AC_SUBST(DTLS_SSL_CFLAGS, ['$(OPENSSL_CFLAGS)'])
;;
*)
AC_MSG_ERROR([Neither OpenSSL nor GnuTLS selected for SSL.])
;;
esac
AC_SUBST(SSL_LIBRARY, $ssl_library)
# Needs to happen after we default to static/shared libraries based on OpenSSL
AC_PROG_LIBTOOL
......@@ -337,28 +391,28 @@ AC_CHECK_HEADER([if_tun.h],
[AC_CHECK_HEADER([net/tun/if_tun.h],
[AC_DEFINE([IF_TUN_HDR], ["net/tun/if_tun.h"])])])])])
if test "${ssl_library}" = "openssl"; then
if test "$ssl_library" = "openssl" || test "$ssl_library" = "both"; then
oldLIBS="$LIBS"
LIBS="$LIBS $OPENSSL_LIBS"
AC_MSG_CHECKING([for ENGINE_by_id() in OpenSSL])
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[#include <openssl/engine.h>],
[ENGINE_by_id("foo");])],
[AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_ENGINE, [1], [OpenSSL has ENGINE support])],
[AC_MSG_RESULT(no)
AC_MSG_NOTICE([Building without OpenSSL TPM ENGINE support])])
if test "$ssl_library" = "openssl"; then
AC_MSG_CHECKING([for ENGINE_by_id() in OpenSSL])
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <openssl/engine.h>],
[ENGINE_by_id("foo");])],
[AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_ENGINE, [1], [OpenSSL has ENGINE support])],
[AC_MSG_RESULT(no)
AC_MSG_NOTICE([Building without OpenSSL TPM ENGINE support])])
fi
AC_MSG_CHECKING([for dtls1_stop_timer() in OpenSSL])
AC_LINK_IFELSE([AC_LANG_PROGRAM(
[#include <openssl/ssl.h>
#include <stdlib.h>
extern void dtls1_stop_timer(SSL *);],
[dtls1_stop_timer(NULL);])],
[AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_DTLS1_STOP_TIMER, [1], [OpenSSL has dtls1_stop_timer() function])],
[AC_MSG_RESULT(no)])
AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <openssl/ssl.h>
#include <stdlib.h>
extern void dtls1_stop_timer(SSL *);],
[dtls1_stop_timer(NULL);])],
[AC_MSG_RESULT(yes)
AC_DEFINE(HAVE_DTLS1_STOP_TIMER, [1], [OpenSSL has dtls1_stop_timer() function])],
[AC_MSG_RESULT(no)])
LIBS="$oldLIBS"
fi
......
......@@ -30,6 +30,9 @@
#include <netinet/in.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "openconnect-internal.h"
......@@ -108,7 +111,7 @@ int RAND_bytes(char *buf, int len)
* their clients use anyway.
*/
#if defined (OPENCONNECT_OPENSSL)
#if defined (DTLS_OPENSSL)
#define DTLS_SEND SSL_write
#define DTLS_RECV SSL_read
static int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
......@@ -125,6 +128,7 @@ static int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
if (!vpninfo->dtls_ctx) {
vpn_progress(vpninfo, PRG_ERR,
_("Initialise DTLSv1 CTX failed\n"));
openconnect_report_ssl_errors(vpninfo);
vpninfo->dtls_attempt_period = 0;
return -EINVAL;
}
......@@ -213,7 +217,7 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
int ret = SSL_do_handshake(vpninfo->new_dtls_ssl);
if (ret == 1) {
vpn_progress(vpninfo, PRG_INFO, _("Established DTLS connection\n"));
vpn_progress(vpninfo, PRG_INFO, _("Established DTLS connection (using OpenSSL)\n"));
if (vpninfo->dtls_ssl) {
/* We are replacing an old connection */
......@@ -326,7 +330,7 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
return -EINVAL;
}
#elif defined (OPENCONNECT_GNUTLS)
#elif defined (DTLS_GNUTLS)
struct {
const char *name;
gnutls_cipher_algorithm_t cipher;
......@@ -400,7 +404,7 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
int err = gnutls_handshake(vpninfo->new_dtls_ssl);
if (!err) {
vpn_progress(vpninfo, PRG_INFO, _("Established DTLS connection\n"));
vpn_progress(vpninfo, PRG_INFO, _("Established DTLS connection (using GnuTLS)\n"));
if (vpninfo->dtls_ssl) {
/* We are replacing an old connection */
......@@ -517,9 +521,9 @@ int connect_dtls_socket(struct openconnect_info *vpninfo)
static int dtls_restart(struct openconnect_info *vpninfo)
{
if (vpninfo->dtls_ssl) {
#if defined (OPENCONNECT_OPENSSL)
#if defined (DTLS_OPENSSL)
SSL_free(vpninfo->dtls_ssl);
#elif defined (OPENCONNECT_GNUTLS)
#elif defined (DTLS_GNUTLS)
gnutls_deinit(vpninfo->dtls_ssl);
#endif
close(vpninfo->dtls_fd);
......@@ -539,6 +543,15 @@ int setup_dtls(struct openconnect_info *vpninfo)
struct vpn_option *dtls_opt = vpninfo->dtls_options;
int dtls_port = 0;
#if defined (OPENCONNECT_GNUTLS) && defined (DTLS_OPENSSL)
/* If we're using GnuTLS for authentication but OpenSSL for DTLS,
we'll need to initialise OpenSSL now... */
SSL_library_init ();
ERR_clear_error ();
SSL_load_error_strings ();
OpenSSL_add_all_algorithms ();
#endif
while (dtls_opt) {
vpn_progress(vpninfo, PRG_TRACE,
_("DTLS option %s : %s\n"),
......@@ -741,7 +754,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
/* One byte of header */
this->hdr[7] = AC_PKT_DATA;
#if defined(OPENCONNECT_OPENSSL)
#if defined(DTLS_OPENSSL)
ret = SSL_write(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1);
if (ret <= 0) {
ret = SSL_get_error(vpninfo->dtls_ssl, ret);
......@@ -759,7 +772,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
}
return 1;
}
#elif defined (OPENCONNECT_GNUTLS)
#elif defined (DTLS_GNUTLS)
ret = gnutls_record_send(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1);
if (ret <= 0) {
if (ret != GNUTLS_E_AGAIN) {
......
......@@ -28,9 +28,11 @@
#include "openconnect.h"
#if defined (OPENCONNECT_OPENSSL)
#if defined (OPENCONNECT_OPENSSL) || defined(DTLS_OPENSSL)
#include <openssl/ssl.h>
#elif defined (OPENCONNECT_GNUTLS)
#include <openssl/err.h>
#endif
#if defined (OPENCONNECT_GNUTLS)
#include <gnutls/gnutls.h>
#include <gnutls/x509.h>
#endif
......@@ -183,12 +185,12 @@ struct openconnect_info {
int reconnect_interval;
int dtls_attempt_period;
time_t new_dtls_started;
#if defined(OPENCONNECT_OPENSSL)
#if defined(DTLS_OPENSSL)
SSL_CTX *dtls_ctx;
SSL *dtls_ssl;
SSL *new_dtls_ssl;
SSL_SESSION *dtls_session;
#elif defined(OPENCONNECT_GNUTLS)
#elif defined(DTLS_GNUTLS)
/* Call these *_ssl rather than *_sess because they're just
pointers, and generic code (in mainloop.c for example)
wants to check if they're NULL or not. No point in being
......@@ -256,8 +258,8 @@ struct openconnect_info {
openconnect_progress_vfn progress;
};
#if (defined (OPENCONNECT_OPENSSL) && defined (SSL_OP_CISCO_ANYCONNECT)) || \
(defined(OPENCONNECT_GNUTLS) && defined (HAVE_GNUTLS_SESSION_SET_PREMASTER))
#if (defined (DTLS_OPENSSL) && defined (SSL_OP_CISCO_ANYCONNECT)) || \
(defined (DTLS_GNUTLS) && defined (HAVE_GNUTLS_SESSION_SET_PREMASTER))
#define HAVE_DTLS 1
#endif
......@@ -272,11 +274,13 @@ struct openconnect_info {
#define AC_PKT_TERM_SERVER 9 /* Server kick */
/* Ick */
#ifdef DTLS_OPENSSL
#if OPENSSL_VERSION_NUMBER >= 0x00909000L
#define method_const const
#else
#define method_const
#endif
#endif
#define vpn_progress(vpninfo, ...) (vpninfo)->progress ((vpninfo)->cbdata, __VA_ARGS__)
......@@ -322,6 +326,9 @@ int request_passphrase(struct openconnect_info *vpninfo,
char **response, const char *fmt, ...);
int __attribute__ ((format (printf, 2, 3)))
openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...);
#if defined(OPENCONNECT_OPENSSL) || defined (DTLS_OPENSSL)
void openconnect_report_ssl_errors(struct openconnect_info *vpninfo);
#endif
/* ${SSL_LIBRARY}.c */
int openconnect_SSL_gets(struct openconnect_info *vpninfo, char *buf, size_t len);
......@@ -331,8 +338,6 @@ int openconnect_open_https(struct openconnect_info *vpninfo);
void openconnect_close_https(struct openconnect_info *vpninfo, int final);
int get_cert_md5_fingerprint(struct openconnect_info *vpninfo, OPENCONNECT_X509 *cert,
char *buf);
/* This one is actually OpenSSL-specific */
void openconnect_report_ssl_errors(struct openconnect_info *vpninfo);
int openconnect_sha1(unsigned char *result, void *data, int len);
int openconnect_random(void *bytes, int len);
int openconnect_local_cert_md5(struct openconnect_info *vpninfo,
......
......@@ -163,19 +163,6 @@ int openconnect_SSL_read(struct openconnect_info *vpninfo, char *buf, size_t len
return done;
}
static int print_err(const char *str, size_t len, void *ptr)
{
struct openconnect_info *vpninfo = ptr;
vpn_progress(vpninfo, PRG_ERR, "%s", str);
return 0;
}
void openconnect_report_ssl_errors(struct openconnect_info *vpninfo)
{
ERR_print_errors_cb(print_err, vpninfo);
}
int openconnect_SSL_gets(struct openconnect_info *vpninfo, char *buf, size_t len)
{
int i = 0;
......
......@@ -353,3 +353,21 @@ int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
return 0;
}
#endif
#if defined(OPENCONNECT_OPENSSL) || defined (DTLS_OPENSSL)
/* We put this here rather than in openssl.c because it might be needed
for OpenSSL DTLS support even when GnuTLS is being used for HTTPS */
#include <openssl/err.h>
static int print_err(const char *str, size_t len, void *ptr)
{
struct openconnect_info *vpninfo = ptr;
vpn_progress(vpninfo, PRG_ERR, "%s", str);
return 0;
}
void openconnect_report_ssl_errors(struct openconnect_info *vpninfo)
{
ERR_print_errors_cb(print_err, vpninfo);
}
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment