Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Allow building against GnuTLS (for TCP) and GnuTLS (for DTLS) simulta…
…neously

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Jun 11, 2012
1 parent 1fdc298 commit f89a643
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 66 deletions.
4 changes: 2 additions & 2 deletions Makefile.am
Expand Up @@ -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)
Expand Down
124 changes: 89 additions & 35 deletions configure.ac
Expand Up @@ -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)]))
Expand All @@ -187,33 +198,54 @@ 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)], [:])], [])
LIBS="$oldLIBS"
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"
Expand All @@ -229,35 +261,57 @@ 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"
AC_SUBST(OPENSSL_CFLAGS)
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
Expand Down Expand Up @@ -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

Expand Down
29 changes: 21 additions & 8 deletions dtls.c
Expand Up @@ -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"

Expand Down Expand Up @@ -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)
Expand All @@ -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;
}
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
Expand All @@ -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"),
Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand Down
21 changes: 13 additions & 8 deletions openconnect-internal.h
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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__)

Expand Down Expand Up @@ -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);
Expand All @@ -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,
Expand Down
13 changes: 0 additions & 13 deletions openssl.c
Expand Up @@ -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;
Expand Down

0 comments on commit f89a643

Please sign in to comment.