Example #1
0
static int dtls_restart(struct openconnect_info *vpninfo)
{
	if (vpninfo->dtls_ssl) {
		SSL_free(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;
	}

	return connect_dtls_socket(vpninfo);
}
Example #2
0
int setup_dtls(struct openconnect_info *vpninfo)
{
	struct vpn_option *dtls_opt = vpninfo->dtls_options;
	int dtls_port = 0;

	while (dtls_opt) {
		vpn_progress(vpninfo, PRG_TRACE,
			     _("DTLS option %s : %s\n"),
			     dtls_opt->option, dtls_opt->value);

		if (!strcmp(dtls_opt->option + 7, "Port")) {
			dtls_port = atol(dtls_opt->value);
		} else if (!strcmp(dtls_opt->option + 7, "Keepalive")) {
			vpninfo->dtls_times.keepalive = atol(dtls_opt->value);
		} else if (!strcmp(dtls_opt->option + 7, "DPD")) {
			int j = atol(dtls_opt->value);
			if (j && (!vpninfo->dtls_times.dpd || j < vpninfo->dtls_times.dpd))
				vpninfo->dtls_times.dpd = j;
		} else if (!strcmp(dtls_opt->option + 7, "Rekey-Time")) {
			vpninfo->dtls_times.rekey = atol(dtls_opt->value);
		} else if (!strcmp(dtls_opt->option + 7, "CipherSuite")) {
			vpninfo->dtls_cipher = strdup(dtls_opt->value);
		}

		dtls_opt = dtls_opt->next;
	}
	if (!dtls_port) {
		vpninfo->dtls_attempt_period = 0;
		return -EINVAL;
	}

	vpninfo->dtls_addr = malloc(vpninfo->peer_addrlen);
	if (!vpninfo->dtls_addr) {
		vpninfo->dtls_attempt_period = 0;
		return -ENOMEM;
	}
	memcpy(vpninfo->dtls_addr, vpninfo->peer_addr, vpninfo->peer_addrlen);

	if (vpninfo->peer_addr->sa_family == AF_INET) {
		struct sockaddr_in *sin = (void *)vpninfo->dtls_addr;
		sin->sin_port = htons(dtls_port);
	} else if (vpninfo->peer_addr->sa_family == AF_INET6) {
		struct sockaddr_in6 *sin = (void *)vpninfo->dtls_addr;
		sin->sin6_port = htons(dtls_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;
		return -EINVAL;
	}

	if (connect_dtls_socket(vpninfo))
		return -EINVAL;

	vpn_progress(vpninfo, PRG_TRACE,
		     _("DTLS connected. DPD %d, Keepalive %d\n"),
		     vpninfo->dtls_times.dpd, vpninfo->dtls_times.keepalive);

	return 0;
}
Example #3
0
int openconnect_setup_dtls(struct openconnect_info *vpninfo, int dtls_attempt_period)
{
	struct oc_vpn_option *dtls_opt = vpninfo->dtls_options;
	int dtls_port = 0;

	vpninfo->dtls_attempt_period = dtls_attempt_period;
	if (!dtls_attempt_period)
		return 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"),
			     dtls_opt->option, dtls_opt->value);

		if (!strcmp(dtls_opt->option + 7, "Port")) {
			dtls_port = atol(dtls_opt->value);
		} else if (!strcmp(dtls_opt->option + 7, "Keepalive")) {
			vpninfo->dtls_times.keepalive = atol(dtls_opt->value);
		} else if (!strcmp(dtls_opt->option + 7, "DPD")) {
			int j = atol(dtls_opt->value);
			if (j && (!vpninfo->dtls_times.dpd || j < vpninfo->dtls_times.dpd))
				vpninfo->dtls_times.dpd = j;
		} else if (!strcmp(dtls_opt->option + 7, "Rekey-Time")) {
			vpninfo->dtls_times.rekey = atol(dtls_opt->value);
		} else if (!strcmp(dtls_opt->option + 7, "CipherSuite")) {
			vpninfo->dtls_cipher = strdup(dtls_opt->value);
		}

		dtls_opt = dtls_opt->next;
	}
	if (!dtls_port) {
		vpninfo->dtls_attempt_period = 0;
		return -EINVAL;
	}

	vpninfo->dtls_addr = malloc(vpninfo->peer_addrlen);
	if (!vpninfo->dtls_addr) {
		vpninfo->dtls_attempt_period = 0;
		return -ENOMEM;
	}
	memcpy(vpninfo->dtls_addr, vpninfo->peer_addr, vpninfo->peer_addrlen);

	if (vpninfo->peer_addr->sa_family == AF_INET) {
		struct sockaddr_in *sin = (void *)vpninfo->dtls_addr;
		sin->sin_port = htons(dtls_port);
	} else if (vpninfo->peer_addr->sa_family == AF_INET6) {
		struct sockaddr_in6 *sin = (void *)vpninfo->dtls_addr;
		sin->sin6_port = htons(dtls_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;
		return -EINVAL;
	}

	if (connect_dtls_socket(vpninfo))
		return -EINVAL;

	vpn_progress(vpninfo, PRG_TRACE,
		     _("DTLS initialised. DPD %d, Keepalive %d\n"),
		     vpninfo->dtls_times.dpd, vpninfo->dtls_times.keepalive);

	return 0;
}
Example #4
0
static int dtls_restart(struct openconnect_info *vpninfo)
{
	dtls_close(vpninfo, 0);
	return connect_dtls_socket(vpninfo);
}
Example #5
0
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
{
	int work_done = 0;
	char magic_pkt;

	if (vpninfo->dtls_state == DTLS_CONNECTING) {
		dtls_try_handshake(vpninfo);
		return 0;
	}

	if (vpninfo->dtls_state == DTLS_SLEEPING) {
		int when = vpninfo->new_dtls_started + vpninfo->dtls_attempt_period - time(NULL);

		if (when <= 0) {
			vpn_progress(vpninfo, PRG_DEBUG, _("Attempt new DTLS connection\n"));
			connect_dtls_socket(vpninfo);
		} else if ((when * 1000) < *timeout) {
			*timeout = when * 1000;
		}
		return 0;
	}

	while (1) {
		int len = vpninfo->ip_info.mtu;
		unsigned char *buf;

		if (!dtls_pkt || len > dtls_pkt_max) {
			realloc_inplace(dtls_pkt, sizeof(struct pkt) + len);
			if (!dtls_pkt) {
				vpn_progress(vpninfo, PRG_ERR, "Allocation failed\n");
				break;
			}
			dtls_pkt_max = len;
		}

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

		vpn_progress(vpninfo, PRG_TRACE,
			     _("Received DTLS packet 0x%02x of %d bytes\n"),
			     buf[0], len);

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

		switch (buf[0]) {
		case AC_PKT_DATA:
			dtls_pkt->len = len - 1;
			queue_packet(&vpninfo->incoming_queue, dtls_pkt);
			dtls_pkt = NULL;
			work_done = 1;
			break;

		case AC_PKT_DPD_OUT:
			vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD request\n"));

			/* FIXME: What if the packet doesn't get through? */
			magic_pkt = AC_PKT_DPD_RESP;
			if (DTLS_SEND(vpninfo->dtls_ssl, &magic_pkt, 1) != 1)
				vpn_progress(vpninfo, PRG_ERR,
					     _("Failed to send DPD response. Expect disconnect\n"));
			continue;

		case AC_PKT_DPD_RESP:
			vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS DPD response\n"));
			break;

		case AC_PKT_KEEPALIVE:
			vpn_progress(vpninfo, PRG_DEBUG, _("Got DTLS Keepalive\n"));
			break;

		default:
			vpn_progress(vpninfo, PRG_ERR,
				     _("Unknown DTLS packet type %02x, len %d\n"),
				     buf[0], len);
			if (1) {
				/* Some versions of OpenSSL have bugs with receiving out-of-order
				 * packets. Not only do they wrongly decide to drop packets if
				 * two packets get swapped in transit, but they also _fail_ to
				 * drop the packet in non-blocking mode; instead they return
				 * the appropriate length of garbage. So don't abort... for now. */
				break;
			} else {
				vpninfo->quit_reason = "Unknown packet received";
				return 1;
			}

		}
	}

	switch (keepalive_action(&vpninfo->dtls_times, timeout)) {
	case KA_REKEY: {
		int ret;

		vpn_progress(vpninfo, PRG_INFO, _("DTLS rekey due\n"));

		if (vpninfo->dtls_times.rekey_method == REKEY_SSL) {
			time(&vpninfo->new_dtls_started);
			vpninfo->dtls_state = DTLS_CONNECTING;
			ret = dtls_try_handshake(vpninfo);
			if (ret) {
				vpn_progress(vpninfo, PRG_ERR, _("DTLS Rehandshake failed; reconnecting.\n"));
				return connect_dtls_socket(vpninfo);
			}
		}

		return 1;
	}

	case KA_DPD_DEAD:
		vpn_progress(vpninfo, PRG_ERR, _("DTLS Dead Peer Detection detected dead peer!\n"));
		/* Fall back to SSL, and start a new DTLS connection */
		dtls_reconnect(vpninfo);
		return 1;

	case KA_DPD:
		vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS DPD\n"));

		magic_pkt = AC_PKT_DPD_OUT;
		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;
		break;

	case KA_KEEPALIVE:
		/* No need to send an explicit keepalive
		   if we have real data to send */
		if (vpninfo->outgoing_queue)
			break;

		vpn_progress(vpninfo, PRG_DEBUG, _("Send DTLS Keepalive\n"));

		magic_pkt = AC_PKT_KEEPALIVE;
		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;

	case KA_NONE:
		;
	}

	/* Service outgoing packet queue */
	unmonitor_write_fd(vpninfo, dtls);
	while (vpninfo->outgoing_queue) {
		struct pkt *this = vpninfo->outgoing_queue;
		int ret;

		vpninfo->outgoing_queue = this->next;
		vpninfo->outgoing_qlen--;

		/* One byte of header */
		this->hdr[7] = AC_PKT_DATA;

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

			if (ret == SSL_ERROR_WANT_WRITE) {
				monitor_write_fd(vpninfo, dtls);
				vpninfo->outgoing_queue = this;
				vpninfo->outgoing_qlen++;

			} else if (ret != SSL_ERROR_WANT_READ) {
				/* If it's a real error, kill the DTLS connection and
				   requeue the packet to be sent over SSL */
				vpn_progress(vpninfo, PRG_ERR,
					     _("DTLS got write error %d. Falling back to SSL\n"),
					     ret);
				openconnect_report_ssl_errors(vpninfo);
				dtls_reconnect(vpninfo);
				vpninfo->outgoing_queue = this;
				vpninfo->outgoing_qlen++;
				work_done = 1;
			}
			return work_done;
		}
#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) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("DTLS got write error: %s. Falling back to SSL\n"),
					     gnutls_strerror(ret));
				dtls_reconnect(vpninfo);
				vpninfo->outgoing_queue = this;
				vpninfo->outgoing_qlen++;
				work_done = 1;
			} else if (gnutls_record_get_direction(vpninfo->dtls_ssl)) {
				monitor_write_fd(vpninfo, dtls);
				vpninfo->outgoing_queue = this;
				vpninfo->outgoing_qlen++;
			}

			return work_done;
		}
#endif
		time(&vpninfo->dtls_times.last_tx);
		vpn_progress(vpninfo, PRG_TRACE,
			     _("Sent DTLS packet of %d bytes; DTLS send returned %d\n"),
			     this->len, ret);
		free(this);
	}

	return work_done;
}
Example #6
0
int dtls_reconnect(struct openconnect_info *vpninfo)
{
	dtls_close(vpninfo);
	vpninfo->dtls_state = DTLS_SLEEPING;
	return connect_dtls_socket(vpninfo);
}