Beispiel #1
0
int dtls_try_handshake(struct openconnect_info *vpninfo)
{
	int err = gnutls_handshake(vpninfo->dtls_ssl);

	if (!err) {
#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->dtls_ssl, vpninfo->ip_info.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->dtls_ssl,
				    vpninfo->ip_info.mtu + 1 /* packet + header */
				    + 13 /* DTLS header */
				    + 20 /* biggest supported MAC (SHA1) */
				    + 16 /* biggest supported IV (AES-128) */
				    + 16 /* max padding */);
#endif

		vpninfo->dtls_state = DTLS_CONNECTED;
		vpn_progress(vpninfo, PRG_INFO,
			     _("Established DTLS connection (using GnuTLS). Ciphersuite %s.\n"),
			     vpninfo->dtls_cipher);

		vpninfo->dtls_times.last_rekey = 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 + 12)
			return 0;
		vpn_progress(vpninfo, PRG_DEBUG, _("DTLS handshake timed out\n"));
	}

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

 error:
	dtls_close(vpninfo);

	vpninfo->dtls_state = DTLS_SLEEPING;
	time(&vpninfo->new_dtls_started);
	return -EINVAL;
}
Beispiel #2
0
static int
read_from_peer(struct dtls_context_t *ctx, 
	       session_t *session, uint8 *data, size_t len) {
  size_t i;
  for (i = 0; i < len; i++)
    printf("%c", data[i]);
  if (len >= strlen(DTLS_SERVER_CMD_CLOSE) &&
      !memcmp(data, DTLS_SERVER_CMD_CLOSE, strlen(DTLS_SERVER_CMD_CLOSE))) {
    printf("server: closing connection\n");
    dtls_close(ctx, session);
    return len;
  } else if (len >= strlen(DTLS_SERVER_CMD_RENEGOTIATE) &&
      !memcmp(data, DTLS_SERVER_CMD_RENEGOTIATE, strlen(DTLS_SERVER_CMD_RENEGOTIATE))) {
    printf("server: renegotiate connection\n");
    dtls_renegotiate(ctx, session);
    return len;
  }

  return dtls_write(ctx, session, data, len);
}
Beispiel #3
0
void dtls_shutdown(struct openconnect_info *vpninfo)
{
	dtls_close(vpninfo);
	SSL_CTX_free(vpninfo->dtls_ctx);
}
Beispiel #4
0
int dtls_try_handshake(struct openconnect_info *vpninfo)
{
	int ret = SSL_do_handshake(vpninfo->dtls_ssl);

	if (ret == 1) {
		const char *c;

		if (!strcmp(vpninfo->dtls_cipher, "PSK-NEGOTIATE")) {
			/* For PSK-NEGOTIATE, we have to determine the tunnel MTU
			 * for ourselves based on the base MTU */
			int data_mtu = vpninfo->cstp_basemtu;
			if (vpninfo->peer_addr->sa_family == IPPROTO_IPV6)
				data_mtu -= 40; /* IPv6 header */
			else
				data_mtu -= 20; /* Legacy IP header */
			data_mtu -= 8; /* UDP header */
			if (data_mtu < 0) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Peer MTU %d too small to allow DTLS\n"),
					     vpninfo->cstp_basemtu);
				goto nodtls;
			}
			/* Reduce it by one because that's the payload header *inside*
			 * the encryption */
			data_mtu = dtls_set_mtu(vpninfo, data_mtu) - 1;
			if (data_mtu < 0)
				goto nodtls;
			if (data_mtu < vpninfo->ip_info.mtu) {
				vpn_progress(vpninfo, PRG_INFO,
					     _("DTLS MTU reduced to %d\n"),
					     data_mtu);
				vpninfo->ip_info.mtu = data_mtu;
			}
		} else if (!SSL_session_reused(vpninfo->dtls_ssl)) {
			/* Someone attempting to hijack the DTLS session?
			 * A real server would never allow a full session
			 * establishment instead of the agreed resume. */
			vpn_progress(vpninfo, PRG_ERR,
				     _("DTLS session resume failed; possible MITM attack. Disabling DTLS.\n"));
		nodtls:
			dtls_close(vpninfo);
			SSL_CTX_free(vpninfo->dtls_ctx);
			vpninfo->dtls_ctx = NULL;
			vpninfo->dtls_attempt_period = 0;
			vpninfo->dtls_state = DTLS_DISABLED;
			return -EIO;
		}

		vpninfo->dtls_state = DTLS_CONNECTED;
		vpn_progress(vpninfo, PRG_INFO,
			     _("Established DTLS connection (using OpenSSL). Ciphersuite %s.\n"),
			     SSL_get_cipher(vpninfo->dtls_ssl));

		c = openconnect_get_dtls_compression(vpninfo);
		if (c) {
			vpn_progress(vpninfo, PRG_INFO,
				     _("DTLS connection compression using %s.\n"), c);
		}

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

		/* From about 8.4.1(11) onwards, the ASA seems to get
		   very unhappy if we resend ChangeCipherSpec messages
		   after the initial setup. This was "fixed" in OpenSSL
		   1.0.0e for RT#2505, but it's not clear if that was
		   the right fix. What happens if the original packet
		   *does* get lost? Surely we *wanted* the retransmits,
		   because without them the server will never be able
		   to decrypt anything we send?
		   Oh well, our retransmitted packets upset the server
		   because we don't get the Cisco-compatibility right
		   (this is one of the areas in which Cisco's DTLS differs
		   from the RFC4347 spec), and DPD should help us notice
		   if *nothing* is getting through. */
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
		/* OpenSSL 1.1.0 or above. Do nothing. The SSLeay() function
		   got renamed, and it's a pointless check in this case
		   anyway because there's *no* chance that we linked against
		   1.1.0 and are running against something older than 1.0.0e. */
#elif OPENSSL_VERSION_NUMBER >= 0x1000005fL
		/* OpenSSL 1.0.0e or above doesn't resend anyway; do nothing.
		   However, if we were *built* against 1.0.0e or newer, but at
		   runtime we find that we are being run against an older
		   version, warn about it. */
		if (SSLeay() < 0x1000005fL) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Your OpenSSL is older than the one you built against, so DTLS may fail!"));
		}
#elif defined(HAVE_DTLS1_STOP_TIMER)
		/*
		 * This works for any normal OpenSSL that supports
		 * Cisco DTLS compatibility (0.9.8m to 1.0.0d inclusive,
		 * and even later versions although it isn't needed there.
		 */
		dtls1_stop_timer(vpninfo->dtls_ssl);
#elif defined(BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT)
		/*
		 * Debian restricts visibility of dtls1_stop_timer()
		 * so do it manually. This version also works on all
		 * sane versions of OpenSSL:
		 */
		memset(&(vpninfo->dtls_ssl->d1->next_timeout), 0,
		       sizeof((vpninfo->dtls_ssl->d1->next_timeout)));
		vpninfo->dtls_ssl->d1->timeout_duration = 1;
		BIO_ctrl(SSL_get_rbio(vpninfo->dtls_ssl),
			 BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
			 &(vpninfo->dtls_ssl->d1->next_timeout));
#elif defined(BIO_CTRL_DGRAM_SET_TIMEOUT)
		/*
		 * OK, here it gets more fun... this shoul handle the case
		 * of older OpenSSL which has the Cisco DTLS compatibility
		 * backported, but *not* the fix for RT#1922.
		 */
		BIO_ctrl(SSL_get_rbio(vpninfo->dtls_ssl),
			 BIO_CTRL_DGRAM_SET_TIMEOUT, 0, NULL);
#else
		/*
		 * And if they don't have any of the above, they probably
		 * don't have RT#1829 fixed either, but that's OK because
		 * that's the "fix" that *introduces* the timeout we're
		 * trying to disable. So do nothing...
		 */
#endif
		dtls_detect_mtu(vpninfo);
		return 0;
	}

	ret = SSL_get_error(vpninfo->dtls_ssl, ret);
	if (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ) {
		static int badossl_bitched = 0;
		if (time(NULL) < vpninfo->new_dtls_started + 12)
			return 0;
		if (((OPENSSL_VERSION_NUMBER >= 0x100000b0L && OPENSSL_VERSION_NUMBER <= 0x100000c0L) || \
		     (OPENSSL_VERSION_NUMBER >= 0x10001040L && OPENSSL_VERSION_NUMBER <= 0x10001060L) || \
		     OPENSSL_VERSION_NUMBER == 0x10002000L) && !badossl_bitched) {
			badossl_bitched = 1;
			vpn_progress(vpninfo, PRG_ERR, _("DTLS handshake timed out\n"));
			vpn_progress(vpninfo, PRG_ERR, _("This is probably because your OpenSSL is broken\n"
				"See http://rt.openssl.org/Ticket/Display.html?id=2984\n"));
		} else {
			vpn_progress(vpninfo, PRG_DEBUG, _("DTLS handshake timed out\n"));
		}
	}

	vpn_progress(vpninfo, PRG_ERR, _("DTLS handshake failed: %d\n"), ret);
	openconnect_report_ssl_errors(vpninfo);

	dtls_close(vpninfo);

	vpninfo->dtls_state = DTLS_SLEEPING;
	time(&vpninfo->new_dtls_started);
	return -EINVAL;
}
Beispiel #5
0
static int dtls_restart(struct openconnect_info *vpninfo)
{
	dtls_close(vpninfo, 0);
	return connect_dtls_socket(vpninfo);
}
Beispiel #6
0
int dtls_try_handshake(struct openconnect_info *vpninfo)
{
	int err = gnutls_handshake(vpninfo->new_dtls_ssl);

	if (!err) {
#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->ip_info.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->ip_info.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"));

		dtls_close(vpninfo, 0);
		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));

 error:
	/* Kill both the new (failed) connection and 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. */
	dtls_close(vpninfo, 1);

	time(&vpninfo->new_dtls_started);
	return -EINVAL;
}
Beispiel #7
0
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 (using OpenSSL)\n"));

		dtls_close(vpninfo, 0);
		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);

		/* From about 8.4.1(11) onwards, the ASA seems to get
		   very unhappy if we resend ChangeCipherSpec messages
		   after the initial setup. This was "fixed" in OpenSSL
		   1.0.0e for RT#2505, but it's not clear if that was
		   the right fix. What happens if the original packet
		   *does* get lost? Surely we *wanted* the retransmits,
		   because without them the server will never be able
		   to decrypt anything we send?
		   Oh well, our retransmitted packets upset the server
		   because we don't get the Cisco-compatibility right
		   (this is one of the areas in which Cisco's DTLS differs
		   from the RFC4347 spec), and DPD should help us notice
		   if *nothing* is getting through. */
#if OPENSSL_VERSION_NUMBER >= 0x1000005fL
		/* OpenSSL 1.0.0e or above doesn't resend anyway; do nothing.
		   However, if we were *built* against 1.0.0e or newer, but at
		   runtime we find that we are being run against an older
		   version, warn about it. */
		if (SSLeay() < 0x1000005fL) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Your OpenSSL is older than the one you built against, so DTLS may fail!"));
		}
#elif defined(HAVE_DTLS1_STOP_TIMER)
		/*
		 * This works for any normal OpenSSL that supports
		 * Cisco DTLS compatibility (0.9.8m to 1.0.0d inclusive,
		 * and even later versions although it isn't needed there.
		 */
		dtls1_stop_timer(vpninfo->dtls_ssl);
#elif defined(BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT)
		/*
		 * Debian restricts visibility of dtls1_stop_timer()
		 * so do it manually. This version also works on all
		 * sane versions of OpenSSL:
		 */
		memset(&(vpninfo->dtls_ssl->d1->next_timeout), 0,
		       sizeof((vpninfo->dtls_ssl->d1->next_timeout)));
		vpninfo->dtls_ssl->d1->timeout_duration = 1;
		BIO_ctrl(SSL_get_rbio(vpninfo->dtls_ssl),
			 BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT, 0,
			 &(vpninfo->dtls_ssl->d1->next_timeout));
#elif defined(BIO_CTRL_DGRAM_SET_TIMEOUT)
		/*
		 * OK, here it gets more fun... this shoul handle the case
		 * of older OpenSSL which has the Cisco DTLS compatibility
		 * backported, but *not* the fix for RT#1922.
		 */
		BIO_ctrl(SSL_get_rbio(vpninfo->dtls_ssl),
			 BIO_CTRL_DGRAM_SET_TIMEOUT, 0, NULL);
#else
		/*
		 * And if they don't have any of the above, they probably
		 * don't have RT#1829 fixed either, but that's OK because
		 * that's the "fix" that *introduces* the timeout we're
		 * trying to disable. So do nothing...
		 */
#endif
		return 0;
	}

	ret = SSL_get_error(vpninfo->new_dtls_ssl, ret);
	if (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ) {
		static int badossl_bitched = 0;
		if (time(NULL) < vpninfo->new_dtls_started + 5)
			return 0;
		if (((OPENSSL_VERSION_NUMBER >= 0x100000b0L && OPENSSL_VERSION_NUMBER <= 0x100000c0L) || \
		     (OPENSSL_VERSION_NUMBER >= 0x10001040L && OPENSSL_VERSION_NUMBER <= 0x10001060L) || \
		     OPENSSL_VERSION_NUMBER == 0x10002000L) && !badossl_bitched) {
			badossl_bitched = 1;
			vpn_progress(vpninfo, PRG_ERR, _("DTLS handshake timed out\n"));
			vpn_progress(vpninfo, PRG_ERR, _("This is probably because your OpenSSL is broken\n"
				"See http://rt.openssl.org/Ticket/Display.html?id=2984\n"));
		} else {
			vpn_progress(vpninfo, PRG_TRACE, _("DTLS handshake timed out\n"));
		}
	}

	vpn_progress(vpninfo, PRG_ERR, _("DTLS handshake failed: %d\n"), ret);
	openconnect_report_ssl_errors(vpninfo);

	/* Kill both the new (failed) connection and 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. */
	dtls_close(vpninfo, 1);

	time(&vpninfo->new_dtls_started);
	return -EINVAL;
}
Beispiel #8
0
int dtls_reconnect(struct openconnect_info *vpninfo)
{
	dtls_close(vpninfo);
	vpninfo->dtls_state = DTLS_SLEEPING;
	return connect_dtls_socket(vpninfo);
}
Beispiel #9
0
void dtls_shutdown(struct openconnect_info *vpninfo)
{
	dtls_close(vpninfo);
}