#include <netinet/in.h>
#include <fcntl.h>
#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
#include "openconnect-internal.h"
-#ifdef HAVE_DTLS1_STOP_TIMER
-/* OpenSSL doesn't deliberately export this, but we need it to
- workaround a DTLS bug in versions < 1.0.0e */
-extern void dtls1_stop_timer (SSL *);
-#endif
-
static unsigned char nybble(unsigned char n)
{
if (n >= '0' && n <= '9') return n - '0';
* their clients use anyway.
*/
-#if defined (OPENCONNECT_OPENSSL)
+#if defined (DTLS_OPENSSL)
#define DTLS_SEND SSL_write
#define DTLS_RECV SSL_read
+
+#ifdef HAVE_DTLS1_STOP_TIMER
+/* OpenSSL doesn't deliberately export this, but we need it to
+ workaround a DTLS bug in versions < 1.0.0e */
+extern void dtls1_stop_timer (SSL *);
+#endif
+
static int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
{
STACK_OF(SSL_CIPHER) *ciphers;
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;
}
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 */
return -EINVAL;
}
-#elif defined (OPENCONNECT_GNUTLS)
+#elif defined (DTLS_GNUTLS)
+#include <gnutls/dtls.h>
+
struct {
const char *name;
gnutls_cipher_algorithm_t cipher;
int err = gnutls_handshake(vpninfo->new_dtls_ssl);
if (!err) {
- vpn_progress(vpninfo, PRG_INFO, _("Established DTLS connection\n"));
+#ifdef HAVE_GNUTLS_DTLS_SET_DATA_MTU
+ /* Make sure GnuTLS's idea of the MTU is sufficient to take
+ a full VPN MTU (with 1-byte header) in a data record. */
+ err = gnutls_dtls_set_data_mtu(vpninfo->new_dtls_ssl, vpninfo->actual_mtu + 1);
+ if (err) {
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Failed to set DTLS MTU: %s\n"),
+ gnutls_strerror(err));
+ goto error;
+ }
+#else
+ /* If we don't have gnutls_dtls_set_data_mtu() then make sure
+ we leave enough headroom by adding the worst-case overhead.
+ We only support AES128-CBC and DES-CBC3-SHA anyway, so
+ working out the worst case isn't hard. */
+ gnutls_dtls_set_mtu(vpninfo->new_dtls_ssl,
+ vpninfo->actual_mtu + 1 /* packet + header */
+ + 13 /* DTLS header */
+ + 20 /* biggest supported MAC (SHA1) */
+ + 16 /* biggest supported IV (AES-128) */
+ + 16 /* max padding */);
+#endif
+
+ vpn_progress(vpninfo, PRG_INFO, _("Established DTLS connection (using GnuTLS)\n"));
if (vpninfo->dtls_ssl) {
/* We are replacing an old connection */
vpn_progress(vpninfo, PRG_ERR, _("DTLS handshake failed: %s\n"),
gnutls_strerror(err));
+ goto error;
+ error:
/* Kill the new (failed) connection... */
gnutls_deinit(vpninfo->new_dtls_ssl);
FD_CLR(vpninfo->new_dtls_fd, &vpninfo->select_rfds);
return -EINVAL;
}
+ if (vpninfo->dtls_local_port) {
+ union {
+ struct sockaddr_in in;
+ struct sockaddr_in6 in6;
+ } dtls_bind_addr;
+ int dtls_bind_addrlen;
+ memset(&dtls_bind_addr, 0, sizeof(dtls_bind_addr));
+
+ if (vpninfo->peer_addr->sa_family == AF_INET) {
+ struct sockaddr_in *addr = &dtls_bind_addr.in;
+ dtls_bind_addrlen = sizeof(*addr);
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = INADDR_ANY;
+ addr->sin_port = htons(vpninfo->dtls_local_port);
+ } else if (vpninfo->peer_addr->sa_family == AF_INET6) {
+ struct sockaddr_in6 *addr = &dtls_bind_addr.in6;
+ dtls_bind_addrlen = sizeof(*addr);
+ addr->sin6_family = AF_INET6;
+ addr->sin6_addr = in6addr_any;
+ addr->sin6_port = htons(vpninfo->dtls_local_port);
+ } else {
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Unknown protocol family %d. Cannot do DTLS\n"),
+ vpninfo->peer_addr->sa_family);
+ vpninfo->dtls_attempt_period = 0;
+ close(dtls_fd);
+ return -EINVAL;
+ }
+
+ if (bind(dtls_fd, (struct sockaddr *)&dtls_bind_addr, dtls_bind_addrlen)) {
+ perror(_("Bind UDP socket for DTLS"));
+ close(dtls_fd);
+ return -EINVAL;
+ }
+ }
+
if (connect(dtls_fd, vpninfo->dtls_addr, vpninfo->peer_addrlen)) {
perror(_("UDP (DTLS) connect:\n"));
close(dtls_fd);
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);
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"),
char magic_pkt;
while (1) {
- int len = vpninfo->mtu;
+ int len = vpninfo->actual_mtu;
unsigned char *buf;
if (!dtls_pkt) {
/* 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);
}
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) {