Skip to content

Commit

Permalink
Add DTLS support for GnuTLS
Browse files Browse the repository at this point in the history
This requires the patches I just sent to Nikos...

Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
  • Loading branch information
David Woodhouse authored and David Woodhouse committed Jun 7, 2012
1 parent 4824e10 commit 845e629
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 12 deletions.
2 changes: 2 additions & 0 deletions configure.ac
Expand Up @@ -202,6 +202,8 @@ elif test "$with_gnutls" = "shibboleet"; then
LIBS="$LIBS $GNUTLS_LIBS"
AC_CHECK_FUNC(gnutls_pkcs12_simple_parse,
[AC_DEFINE(HAVE_GNUTLS_PKCS12_SIMPLE_PARSE, 1)], [])
AC_CHECK_FUNC(gnutls_session_set_master,
[AC_DEFINE(HAVE_GNUTLS_SESSION_SET_MASTER, 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])
Expand Down
153 changes: 144 additions & 9 deletions dtls.c
Expand Up @@ -54,7 +54,8 @@ unsigned char unhex(const char *data)
return (nybble(data[0]) << 4) | nybble(data[1]);
}

#if defined (OPENCONNECT_OPENSSL) && defined (SSL_OP_CISCO_ANYCONNECT)
#ifdef HAVE_DTLS

#if 0
/*
* Useful for catching test cases, where we want everything to be
Expand Down Expand Up @@ -109,6 +110,9 @@ int RAND_bytes(char *buf, int len)
* their clients use anyway.
*/

#if defined (OPENCONNECT_OPENSSL)
#define DTLS_SEND SSL_write
#define DTLS_RECV SSL_read
static int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
{
STACK_OF(SSL_CIPHER) *ciphers;
Expand Down Expand Up @@ -324,6 +328,113 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
return -EINVAL;
}

#elif defined (OPENCONNECT_GNUTLS)
#define DTLS_SEND gnutls_record_send
#define DTLS_RECV gnutls_record_recv
static int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
{
gnutls_session_t dtls_ssl;
gnutls_datum_t master_secret, session_id;
int err;

gnutls_init(&dtls_ssl, GNUTLS_CLIENT|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK);
err = gnutls_priority_set_direct(dtls_ssl,
"NONE:+VERS-DTLS0.9:+COMP-NULL:+AES-128-CBC:+SHA1:+RSA:%COMPAT:%DISABLE_SAFE_RENEGOTIATION",
NULL);
if (err) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to set DTLS priority: %s\n"),
gnutls_strerror(err));
gnutls_deinit(dtls_ssl);
vpninfo->dtls_attempt_period = 0;
return -EINVAL;
}
gnutls_transport_set_ptr(dtls_ssl,
(gnutls_transport_ptr_t)(long) dtls_fd);
gnutls_record_disable_padding(dtls_ssl);
master_secret.data = vpninfo->dtls_secret;
master_secret.size = sizeof(vpninfo->dtls_secret);
session_id.data = vpninfo->dtls_session_id;
session_id.size = sizeof(vpninfo->dtls_session_id);
err = gnutls_session_set_master(dtls_ssl, GNUTLS_CLIENT, GNUTLS_DTLS0_9,
GNUTLS_KX_RSA, GNUTLS_CIPHER_AES_128_CBC,
GNUTLS_MAC_SHA1, GNUTLS_COMP_NULL,
&master_secret, &session_id);
if (err) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to set DTLS session parameters: %s\n"),
gnutls_strerror(err));
gnutls_deinit(dtls_ssl);
vpninfo->dtls_attempt_period = 0;
return -EINVAL;
}

vpninfo->new_dtls_ssl = dtls_ssl;
return 0;
}

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"));

if (vpninfo->dtls_ssl) {
/* We are replacing an old connection */
gnutls_deinit(vpninfo->dtls_ssl);
close(vpninfo->dtls_fd);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_rfds);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_wfds);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_efds);
}
vpninfo->dtls_ssl = vpninfo->new_dtls_ssl;
vpninfo->dtls_fd = vpninfo->new_dtls_fd;

vpninfo->new_dtls_ssl = NULL;
vpninfo->new_dtls_fd = -1;

vpninfo->dtls_times.last_rx = vpninfo->dtls_times.last_tx = time(NULL);

/* XXX: For OpenSSL we explicitly prevent retransmits here. */
return 0;
}

if (err == GNUTLS_E_AGAIN) {
if (time(NULL) < vpninfo->new_dtls_started + 5)
return 0;
vpn_progress(vpninfo, PRG_TRACE, _("DTLS handshake timed out\n"));
}

vpn_progress(vpninfo, PRG_ERR, _("DTLS handshake failed: %s\n"),
gnutls_strerror(err));

/* Kill the new (failed) connection... */
gnutls_deinit(vpninfo->new_dtls_ssl);
FD_CLR(vpninfo->new_dtls_fd, &vpninfo->select_rfds);
FD_CLR(vpninfo->new_dtls_fd, &vpninfo->select_efds);
close(vpninfo->new_dtls_fd);
vpninfo->new_dtls_ssl = NULL;
vpninfo->new_dtls_fd = -1;

/* ... and kill the old one too. The only time there'll be a valid
existing session is when it was a rekey, and in that case it's
time for the old one to die. */
if (vpninfo->dtls_ssl) {
gnutls_deinit(vpninfo->dtls_ssl);
close(vpninfo->dtls_fd);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_rfds);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_wfds);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_efds);
vpninfo->dtls_ssl = NULL;
vpninfo->dtls_fd = -1;
}

time(&vpninfo->new_dtls_started);
return -EINVAL;
}
#endif

int connect_dtls_socket(struct openconnect_info *vpninfo)
{
int dtls_fd, ret;
Expand Down Expand Up @@ -384,7 +495,11 @@ int connect_dtls_socket(struct openconnect_info *vpninfo)
static int dtls_restart(struct openconnect_info *vpninfo)
{
if (vpninfo->dtls_ssl) {
#if defined (OPENCONNECT_OPENSSL)
SSL_free(vpninfo->dtls_ssl);
#elif defined (OPENCONNECT_GNUTLS)
gnutls_deinit(vpninfo->dtls_ssl);
#endif
close(vpninfo->dtls_fd);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_rfds);
FD_CLR(vpninfo->dtls_fd, &vpninfo->select_wfds);
Expand Down Expand Up @@ -479,7 +594,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
}

buf = dtls_pkt->data - 1;
len = SSL_read(vpninfo->dtls_ssl, buf, len + 1);
len = DTLS_RECV(vpninfo->dtls_ssl, buf, len + 1);
if (len <= 0)
break;

Expand All @@ -502,7 +617,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)

/* FIXME: What if the packet doesn't get through? */
magic_pkt = AC_PKT_DPD_RESP;
if (SSL_write(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
vpn_progress(vpninfo, PRG_ERR,
_("Failed to send DPD response. Expect disconnect\n"));
continue;
Expand Down Expand Up @@ -564,7 +679,10 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
vpn_progress(vpninfo, PRG_TRACE, _("Send DTLS DPD\n"));

magic_pkt = AC_PKT_DPD_OUT;
SSL_write(vpninfo->dtls_ssl, &magic_pkt, 1);
if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
vpn_progress(vpninfo, PRG_ERR,
_("Failed to send DPD request. Expect disconnect\n"));

/* last_dpd will just have been set */
vpninfo->dtls_times.last_tx = vpninfo->dtls_times.last_dpd;
work_done = 1;
Expand All @@ -579,7 +697,9 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
vpn_progress(vpninfo, PRG_TRACE, _("Send DTLS Keepalive\n"));

magic_pkt = AC_PKT_KEEPALIVE;
SSL_write(vpninfo->dtls_ssl, &magic_pkt, 1);
if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
vpn_progress(vpninfo, PRG_ERR,
_("Failed to send keepalive request. Expect disconnect\n"));
time(&vpninfo->dtls_times.last_tx);
work_done = 1;
break;
Expand All @@ -599,6 +719,7 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
/* One byte of header */
this->hdr[7] = AC_PKT_DATA;

#if defined(OPENCONNECT_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 @@ -616,21 +737,35 @@ int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
}
return 1;
}
#elif defined (OPENCONNECT_GNUTLS)
ret = gnutls_record_send(vpninfo->dtls_ssl, &this->hdr[7], this->len + 1);
if (ret <= 0) {
if (ret != GNUTLS_E_AGAIN) {
vpn_progress(vpninfo, PRG_ERR,
_("DTLS got write error: %s. Falling back to SSL\n"),
gnutls_strerror(ret));
dtls_restart(vpninfo);
vpninfo->outgoing_queue = this;
vpninfo->outgoing_qlen++;
}
return 1;
}
#endif
time(&vpninfo->dtls_times.last_tx);
vpn_progress(vpninfo, PRG_TRACE,
_("Sent DTLS packet of %d bytes; SSL_write() returned %d\n"),
_("Sent DTLS packet of %d bytes; DTLS send returned %d\n"),
this->len, ret);
free(this);
}

return work_done;
}
#else /* No DTLS support in OpenSSL */
#warning Your version of OpenSSL does not seem to support Cisco DTLS compatibility
#else /* !HAVE_DTLS */
#warning Your SSL library does not seem to support Cisco DTLS compatibility
int setup_dtls(struct openconnect_info *vpninfo)
{
vpn_progress(vpninfo, PRG_ERR,
_("Built against OpenSSL with no Cisco DTLS support\n"));
_("Built against SSL library with no Cisco DTLS support\n"));
return -EINVAL;
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion mainloop.c
Expand Up @@ -79,7 +79,7 @@ int vpn_mainloop(struct openconnect_info *vpninfo)
struct timeval tv;
fd_set rfds, wfds, efds;

#if defined(OPENCONNECT_OPENSSL) && defined(SSL_OP_CISCO_ANYCONNECT)
#ifdef HAVE_DTLS
if (vpninfo->new_dtls_ssl)
dtls_try_handshake(vpninfo);

Expand Down
13 changes: 12 additions & 1 deletion openconnect-internal.h
Expand Up @@ -182,7 +182,13 @@ struct openconnect_info {
SSL *new_dtls_ssl;
SSL_SESSION *dtls_session;
#elif defined(OPENCONNECT_GNUTLS)
/* FIXME */
/* 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
differently named to the OpenSSL variant, and forcing us to
have ifdefs or accessor macros for them. */
gnutls_session_t dtls_ssl;
gnutls_session_t new_dtls_ssl;
#endif
struct keepalive_info dtls_times;
unsigned char dtls_session_id[32];
Expand Down Expand Up @@ -242,6 +248,11 @@ struct openconnect_info {
openconnect_progress_vfn progress;
};

#if (defined (OPENCONNECT_OPENSSL) && defined (SSL_OP_CISCO_ANYCONNECT)) || \
(defined(OPENCONNECT_GNUTLS) && defined (HAVE_GNUTLS_SESSION_SET_MASTER))
#define HAVE_DTLS 1
#endif

/* Packet types */

#define AC_PKT_DATA 0 /* Uncompressed data */
Expand Down
2 changes: 1 addition & 1 deletion www/changelog.xml
Expand Up @@ -17,7 +17,7 @@
<ul>
<li><b>OpenConnect HEAD</b>
<ul>
<li>Allow building against GnuTLS <i>(experimental, no DTLS support yet).</i></li>
<li>Allow building against GnuTLS, including DTLS support.</li>
<li>Add <tt>--with-pkgconfigdir=</tt> option to <tt>configure</tt> for FreeBSD's benefit <i>(<a href="https://bugs.freedesktop.org/show_bug.cgi?id=48743">fd#48743</a>)</i>.</li>
</ul><br/>
</li>
Expand Down

0 comments on commit 845e629

Please sign in to comment.