Пример #1
0
static int init_esp_ciphers(struct openconnect_info *vpninfo, struct esp *esp,
			    const EVP_MD *macalg, const EVP_CIPHER *encalg, int decrypt)
{
	int ret;

	destroy_esp_ciphers(esp);

	EVP_CIPHER_CTX_init(&esp->cipher);
	if (decrypt)
		ret = EVP_DecryptInit_ex(&esp->cipher, encalg, NULL, esp->secrets, NULL);
	else
		ret = EVP_EncryptInit_ex(&esp->cipher, encalg, NULL, esp->secrets, NULL);

	if (!ret) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to initialise ESP cipher:\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EIO;
	}
	EVP_CIPHER_CTX_set_padding(&esp->cipher, 0);

	HMAC_CTX_init(&esp->hmac);
	if (!HMAC_Init_ex(&esp->hmac, esp->secrets + EVP_CIPHER_key_length(encalg),
			  EVP_MD_size(macalg), macalg, NULL)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to initialize ESP HMAC\n"));

		openconnect_report_ssl_errors(vpninfo);
		destroy_esp_ciphers(esp);
	}
	esp->seq = 0;
	esp->seq_backlog = 0;
	return 0;
}
Пример #2
0
static int load_tpm_certificate(struct openconnect_info *vpninfo)
{
	ENGINE *e;
	EVP_PKEY *key;
	UI_METHOD *meth = NULL;
	int ret = 0;

	ENGINE_load_builtin_engines();

	e = ENGINE_by_id("tpm");
	if (!e) {
		vpn_progress(vpninfo, PRG_ERR, _("Can't load TPM engine.\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EINVAL;
	}
	if (!ENGINE_init(e) || !ENGINE_set_default_RSA(e) ||
	    !ENGINE_set_default_RAND(e)) {
		vpn_progress(vpninfo, PRG_ERR, _("Failed to init TPM engine\n"));
		openconnect_report_ssl_errors(vpninfo);
		ENGINE_free(e);
		return -EINVAL;
	}

	if (vpninfo->cert_password) {
		if (!ENGINE_ctrl_cmd(e, "PIN", strlen(vpninfo->cert_password),
				     vpninfo->cert_password, NULL, 0)) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to set TPM SRK password\n"));
			openconnect_report_ssl_errors(vpninfo);
		}
		vpninfo->cert_password = NULL;
		free(vpninfo->cert_password);
	} else {
		/* Provide our own UI method to handle the PIN callback. */
		meth = create_openssl_ui(vpninfo);
	}
	key = ENGINE_load_private_key(e, vpninfo->sslkey, meth, NULL);
	if (meth)
		UI_destroy_method(meth);
	if (!key) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to load TPM private key\n"));
		openconnect_report_ssl_errors(vpninfo);
		ret = -EINVAL;
		goto out;
	}
	if (!SSL_CTX_use_PrivateKey(vpninfo->https_ctx, key)) {
		vpn_progress(vpninfo, PRG_ERR, _("Add key from TPM failed\n"));
		openconnect_report_ssl_errors(vpninfo);
		ret = -EINVAL;
	}
	EVP_PKEY_free(key);
 out:
	ENGINE_finish(e);
	ENGINE_free(e);
	return ret;
}
Пример #3
0
int encrypt_esp_packet(struct openconnect_info *vpninfo, struct pkt *pkt)
{
	int i, padlen;
	const int blksize = 16;
	unsigned int hmac_len = 20;
	int crypt_len;
	HMAC_CTX hmac_ctx;

	/* This gets much more fun if the IV is variable-length */
	pkt->esp.spi = vpninfo->esp_out.spi;
	pkt->esp.seq = htonl(vpninfo->esp_out.seq++);
	if (!RAND_bytes((void *)&pkt->esp.iv, sizeof(pkt->esp.iv))) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to generate random IV for ESP packet:\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EIO;
	}

	padlen = blksize - 1 - ((pkt->len + 1) % blksize);
	for (i=0; i<padlen; i++)
		pkt->data[pkt->len + i] = i + 1;
	pkt->data[pkt->len + padlen] = padlen;
	pkt->data[pkt->len + padlen + 1] = 0x04; /* Legacy IP */
	
	if (!EVP_EncryptInit_ex(&vpninfo->esp_out.cipher, NULL, NULL, NULL,
				pkt->esp.iv)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to set up encryption context for ESP packet:\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EINVAL;
	}

	crypt_len = pkt->len + padlen + 2;
	if (!EVP_EncryptUpdate(&vpninfo->esp_out.cipher, pkt->data, &crypt_len,
			       pkt->data, crypt_len)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to encrypt ESP packet:\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EINVAL;
	}

	HMAC_CTX_copy(&hmac_ctx, &vpninfo->esp_out.hmac);
	HMAC_Update(&hmac_ctx, (void *)&pkt->esp, sizeof(pkt->esp) + crypt_len);
	HMAC_Final(&hmac_ctx, pkt->data + crypt_len, &hmac_len);
	HMAC_CTX_cleanup(&hmac_ctx);

	return sizeof(pkt->esp) + crypt_len + 12;
}
Пример #4
0
static int reload_pem_cert(struct openconnect_info *vpninfo)
{
	BIO *b = BIO_new(BIO_s_file_internal());
	char buf[200];

	if (!b)
		return -ENOMEM;

	if (BIO_read_filename(b, vpninfo->cert) <= 0) {
	err:
		BIO_free(b);
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to reload X509 cert for expiry check\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EIO;
	}
	vpninfo->cert_x509 = PEM_read_bio_X509_AUX(b, NULL, NULL, NULL);
	BIO_free(b);
	if (!vpninfo->cert_x509)
		goto err;

	X509_NAME_oneline(X509_get_subject_name(vpninfo->cert_x509), buf, sizeof(buf));
	vpn_progress(vpninfo, PRG_INFO,
			     _("Using client certificate '%s'\n"), buf);

	return 0;
}
Пример #5
0
int openconnect_SSL_read(struct openconnect_info *vpninfo, char *buf, size_t len)
{
	int done;

	while ((done = SSL_read(vpninfo->https_ssl, buf, len)) == -1) {
		int err = SSL_get_error(vpninfo->https_ssl, done);
		fd_set wr_set, rd_set;
		int maxfd = vpninfo->ssl_fd;

		FD_ZERO(&wr_set);
		FD_ZERO(&rd_set);

		if (err == SSL_ERROR_WANT_READ)
			FD_SET(vpninfo->ssl_fd, &rd_set);
		else if (err == SSL_ERROR_WANT_WRITE)
			FD_SET(vpninfo->ssl_fd, &wr_set);
		else {
			vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket\n"));
			openconnect_report_ssl_errors(vpninfo);
			return -EIO;
		}
		cmd_fd_set(vpninfo, &rd_set, &maxfd);
		select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
		if (is_cancel_pending(vpninfo, &rd_set)) {
			vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
			return -EINTR;
		}
	}
	return done;
}
Пример #6
0
/* pkt->len shall be the *payload* length. Omitting the header and the 12-byte HMAC */
int decrypt_esp_packet(struct openconnect_info *vpninfo, struct esp *esp, struct pkt *pkt)
{
	unsigned char hmac_buf[20];
	unsigned int hmac_len = sizeof(hmac_buf);
	int crypt_len = pkt->len;
	HMAC_CTX hmac_ctx;

	HMAC_CTX_copy(&hmac_ctx, &esp->hmac);
	HMAC_Update(&hmac_ctx, (void *)&pkt->esp, sizeof(pkt->esp) + pkt->len);
	HMAC_Final(&hmac_ctx, hmac_buf, &hmac_len);
	HMAC_CTX_cleanup(&hmac_ctx);

	if (memcmp(hmac_buf, pkt->data + pkt->len, 12)) {
		vpn_progress(vpninfo, PRG_DEBUG,
			     _("Received ESP packet with invalid HMAC\n"));
		return -EINVAL;
	}

	/* Why in $DEITY's name would you ever *not* set this? Perhaps we
	 * should do th check anyway, but only warn instead of discarding
	 * the packet? */
	if (vpninfo->esp_replay_protect &&
	    verify_packet_seqno(vpninfo, esp, ntohl(pkt->esp.seq)))
		return -EINVAL;


	if (!EVP_DecryptInit_ex(&esp->cipher, NULL, NULL, NULL,
				pkt->esp.iv)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to set up decryption context for ESP packet:\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EINVAL;
	}

	if (!EVP_DecryptUpdate(&esp->cipher, pkt->data, &crypt_len,
			       pkt->data, pkt->len)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to decrypt ESP packet:\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EINVAL;
	}

	return 0;
}
Пример #7
0
int openconnect_SSL_gets(struct openconnect_info *vpninfo, char *buf, size_t len)
{
	int i = 0;
	int ret;

	if (len < 2)
		return -EINVAL;

	while (1) {
		ret = SSL_read(vpninfo->https_ssl, buf + i, 1);
		if (ret == 1) {
			if (buf[i] == '\n') {
				buf[i] = 0;
				if (i && buf[i-1] == '\r') {
					buf[i-1] = 0;
					i--;
				}
				return i;
			}
			i++;

			if (i >= len - 1) {
				buf[i] = 0;
				return i;
			}
		} else {
			fd_set rd_set, wr_set;
			int maxfd = vpninfo->ssl_fd;

			FD_ZERO(&rd_set);
			FD_ZERO(&wr_set);

			ret = SSL_get_error(vpninfo->https_ssl, ret);
			if (ret == SSL_ERROR_WANT_READ)
				FD_SET(vpninfo->ssl_fd, &rd_set);
			else if (ret == SSL_ERROR_WANT_WRITE)
				FD_SET(vpninfo->ssl_fd, &wr_set);
			else {
				vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket\n"));
				openconnect_report_ssl_errors(vpninfo);
				ret = -EIO;
				break;
			}
			cmd_fd_set(vpninfo, &rd_set, &maxfd);
			select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
			if (is_cancel_pending(vpninfo, &rd_set)) {
				vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
				ret = -EINTR;
				break;
			}
		}
	}
	buf[i] = 0;
	return i ?: ret;
}
Пример #8
0
static SSL_SESSION *generate_dtls_session(struct openconnect_info *vpninfo,
					  int dtlsver, const SSL_CIPHER *cipher)
{
	struct oc_text_buf *buf = buf_alloc();
	SSL_SESSION *dtls_session;
	const unsigned char *asn;
	uint16_t cid;

	buf_append_bytes(buf, "\x30\x80", 2); // SEQUENCE, indeterminate length
	buf_append_INTEGER(buf, 1 /* SSL_SESSION_ASN1_VERSION */);
	buf_append_INTEGER(buf, dtlsver);
	store_be16(&cid, SSL_CIPHER_get_id(cipher) & 0xffff);
	buf_append_OCTET_STRING(buf, &cid, 2);
	buf_append_OCTET_STRING(buf, vpninfo->dtls_session_id,
				sizeof(vpninfo->dtls_session_id));
	buf_append_OCTET_STRING(buf, vpninfo->dtls_secret,
				sizeof(vpninfo->dtls_secret));
	/* If the length actually fits in one byte (which it should), do
	 * it that way.  Else, leave it indeterminate and add two
	 * end-of-contents octets to mark the end of the SEQUENCE. */
	if (!buf_error(buf) && buf->pos <= 0x80)
		buf->data[1] = buf->pos - 2;
	else
		buf_append_bytes(buf, "\0\0", 2);

	if (buf_error(buf)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to create SSL_SESSION ASN.1 for OpenSSL: %s\n"),
			     strerror(buf_error(buf)));
		buf_free(buf);
		return NULL;
	}

	asn = (void *)buf->data;
	dtls_session = d2i_SSL_SESSION(NULL, &asn, buf->pos);
	buf_free(buf);
	if (!dtls_session) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("OpenSSL failed to parse SSL_SESSION ASN.1\n"));
		openconnect_report_ssl_errors(vpninfo);
		return NULL;
	}

	return dtls_session;
}
Пример #9
0
static int is_pem_password_error(struct openconnect_info *vpninfo)
{
	unsigned long err = ERR_peek_error();

	openconnect_report_ssl_errors(vpninfo);

#ifndef EVP_F_EVP_DECRYPTFINAL_EX
#define EVP_F_EVP_DECRYPTFINAL_EX EVP_F_EVP_DECRYPTFINAL
#endif
	/* If the user fat-fingered the passphrase, try again */
	if (ERR_GET_LIB(err) == ERR_LIB_EVP &&
	    ERR_GET_FUNC(err) == EVP_F_EVP_DECRYPTFINAL_EX &&
	    ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Loading private key failed (wrong passphrase?)\n"));
		ERR_clear_error();
		return 1;
	}

	vpn_progress(vpninfo, PRG_ERR,
		     _("Loading private key failed (see above errors)\n"));
	return 0;
}
Пример #10
0
static int load_pkcs12_certificate(struct openconnect_info *vpninfo, PKCS12 *p12)
{
	EVP_PKEY *pkey = NULL;
	X509 *cert = NULL;
	STACK_OF(X509) *ca;
	int ret = 0;
	char *pass;

	pass = vpninfo->cert_password;
	vpninfo->cert_password = NULL;
 retrypass:
	/* We do this every time round the loop, to work around a bug in
	   OpenSSL < 1.0.0-beta2 -- where the stack at *ca will be freed
	   when PKCS12_parse() returns an error, but *ca is left pointing
	   to the freed memory. */
	ca = NULL;
	if (!pass && request_passphrase(vpninfo, "openconnect_pkcs12", &pass,
					_("Enter PKCS#12 pass phrase:")) < 0) {
		PKCS12_free(p12);
		return -EINVAL;
	}
	if (!PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
		unsigned long err = ERR_peek_error();

		openconnect_report_ssl_errors(vpninfo);

		if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
		    ERR_GET_FUNC(err) == PKCS12_F_PKCS12_PARSE &&
		    ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Parse PKCS#12 failed (wrong passphrase?)\n"));
			free(pass);
			pass = NULL;
			goto retrypass;
		}

		vpn_progress(vpninfo, PRG_ERR,
			     _("Parse PKCS#12 failed (see above errors)\n"));
		PKCS12_free(p12);
		free(pass);
		return -EINVAL;
	}
	free(pass);
	if (cert) {
		char buf[200];
		vpninfo->cert_x509 = cert;
		SSL_CTX_use_certificate(vpninfo->https_ctx, cert);
		X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
		vpn_progress(vpninfo, PRG_INFO,
			     _("Using client certificate '%s'\n"), buf);
	} else {
		vpn_progress(vpninfo, PRG_ERR,
			     _("PKCS#12 contained no certificate!"));
		ret = -EINVAL;
	}

	if (pkey) {
		SSL_CTX_use_PrivateKey(vpninfo->https_ctx, pkey);
		EVP_PKEY_free(pkey);
	} else {
		vpn_progress(vpninfo, PRG_ERR,
			     _("PKCS#12 contained no private key!"));
		ret = -EINVAL;
	}

	/* Only include supporting certificates which are actually necessary */
	if (ca) {
		int i;
	next:
		for (i = 0; i < sk_X509_num(ca); i++) {
			X509 *cert2 = sk_X509_value(ca, i);
			if (X509_check_issued(cert2, cert) == X509_V_OK) {
				char buf[200];

				if (cert2 == cert)
					break;
				if (X509_check_issued(cert2, cert2) == X509_V_OK)
					break;

				X509_NAME_oneline(X509_get_subject_name(cert2),
						  buf, sizeof(buf));
				vpn_progress(vpninfo, PRG_DEBUG,
					     _("Extra cert from PKCS#12: '%s'\n"), buf);
				CRYPTO_add(&cert2->references, 1, CRYPTO_LOCK_X509);
				SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert2);
				cert = cert2;
				goto next;
			}
		}
		sk_X509_pop_free(ca, X509_free);
	}

	PKCS12_free(p12);
	return ret;
}
Пример #11
0
int openconnect_open_https(struct openconnect_info *vpninfo)
{
	method_const SSL_METHOD *ssl3_method;
	SSL *https_ssl;
	BIO *https_bio;
	int ssl_sock;
	int err;

	if (vpninfo->https_ssl)
		return 0;

	if (vpninfo->peer_cert) {
		X509_free(vpninfo->peer_cert);
		vpninfo->peer_cert = NULL;
	}

	ssl_sock = connect_https_socket(vpninfo);
	if (ssl_sock < 0)
		return ssl_sock;

	ssl3_method = TLSv1_client_method();
	if (!vpninfo->https_ctx) {
		vpninfo->https_ctx = SSL_CTX_new(ssl3_method);

		/* Some servers (or their firewalls) really don't like seeing
		   extensions. */
#ifdef SSL_OP_NO_TICKET
		SSL_CTX_set_options(vpninfo->https_ctx, SSL_OP_NO_TICKET);
#endif

		if (vpninfo->cert) {
			err = load_certificate(vpninfo);
			if (err) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Loading certificate failed. Aborting.\n"));
				SSL_CTX_free(vpninfo->https_ctx);
				vpninfo->https_ctx = NULL;
				close(ssl_sock);
				return err;
			}
			check_certificate_expiry(vpninfo);
		}

		/* We just want to do:
		   SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY);
		   ... but it doesn't work with OpenSSL < 0.9.8k because of
		   problems with inheritance (fixed in v1.1.4.6 of
		   crypto/x509/x509_vpm.c) so we have to play silly buggers
		   instead. This trick doesn't work _either_ in < 0.9.7 but
		   I don't know of _any_ workaround which will, and can't
		   be bothered to find out either. */
#if OPENSSL_VERSION_NUMBER >= 0x00908000
		SSL_CTX_set_cert_verify_callback(vpninfo->https_ctx,
						 ssl_app_verify_callback, NULL);
#endif
		SSL_CTX_set_default_verify_paths(vpninfo->https_ctx);

#ifdef ANDROID_KEYSTORE
		if (vpninfo->cafile && !strncmp(vpninfo->cafile, "keystore:", 9)) {
			STACK_OF(X509_INFO) *stack;
			X509_STORE *store;
			X509_INFO *info;
			BIO *b = BIO_from_keystore(vpninfo, vpninfo->cafile);

			if (!b) {
				SSL_CTX_free(vpninfo->https_ctx);
				vpninfo->https_ctx = NULL;
				close(ssl_sock);
				return -EINVAL;
			}

			stack = PEM_X509_INFO_read_bio(b, NULL, NULL, NULL);
			BIO_free(b);

			if (!stack) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Failed to read certs from CA file '%s'\n"),
					     vpninfo->cafile);
				openconnect_report_ssl_errors(vpninfo);
				SSL_CTX_free(vpninfo->https_ctx);
				vpninfo->https_ctx = NULL;
				close(ssl_sock);
				return -ENOENT;
			}

			store = SSL_CTX_get_cert_store(vpninfo->https_ctx);

			while ((info = sk_X509_INFO_pop(stack))) {
				if (info->x509)
					X509_STORE_add_cert(store, info->x509);
				if (info->crl)
					X509_STORE_add_crl(store, info->crl);
				X509_INFO_free(info);
			}
			sk_X509_INFO_free(stack);
		} else
#endif
		if (vpninfo->cafile) {
			if (!SSL_CTX_load_verify_locations(vpninfo->https_ctx, vpninfo->cafile, NULL)) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Failed to open CA file '%s'\n"),
					     vpninfo->cafile);
				openconnect_report_ssl_errors(vpninfo);
				SSL_CTX_free(vpninfo->https_ctx);
				vpninfo->https_ctx = NULL;
				close(ssl_sock);
				return -EINVAL;
			}
		}

	}
	https_ssl = SSL_new(vpninfo->https_ctx);
	workaround_openssl_certchain_bug(vpninfo, https_ssl);

	https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE);
	BIO_set_nbio(https_bio, 1);
	SSL_set_bio(https_ssl, https_bio, https_bio);

	vpn_progress(vpninfo, PRG_INFO, _("SSL negotiation with %s\n"),
		     vpninfo->hostname);

	while ((err = SSL_connect(https_ssl)) <= 0) {
		fd_set wr_set, rd_set;
		int maxfd = ssl_sock;

		FD_ZERO(&wr_set);
		FD_ZERO(&rd_set);

		err = SSL_get_error(https_ssl, err);
		if (err == SSL_ERROR_WANT_READ)
			FD_SET(ssl_sock, &rd_set);
		else if (err == SSL_ERROR_WANT_WRITE)
			FD_SET(ssl_sock, &wr_set);
		else {
			vpn_progress(vpninfo, PRG_ERR, _("SSL connection failure\n"));
			openconnect_report_ssl_errors(vpninfo);
			SSL_free(https_ssl);
			close(ssl_sock);
			return -EINVAL;
		}

		cmd_fd_set(vpninfo, &rd_set, &maxfd);
		select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
		if (is_cancel_pending(vpninfo, &rd_set)) {
			vpn_progress(vpninfo, PRG_ERR, _("SSL connection cancelled\n"));
			SSL_free(https_ssl);
			close(ssl_sock);
			return -EINVAL;
		}
	}

	if (verify_peer(vpninfo, https_ssl)) {
		SSL_free(https_ssl);
		close(ssl_sock);
		return -EINVAL;
	}

	vpninfo->ssl_fd = ssl_sock;
	vpninfo->https_ssl = https_ssl;

	/* Stash this now, because it might not be available later if the
	   server has disconnected. */
	vpninfo->peer_cert = SSL_get_peer_certificate(vpninfo->https_ssl);

	vpn_progress(vpninfo, PRG_INFO, _("Connected to HTTPS on %s\n"),
		     vpninfo->hostname);

	return 0;
}
Пример #12
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;
}
Пример #13
0
int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
{
	STACK_OF(SSL_CIPHER) *ciphers;
	method_const SSL_METHOD *dtls_method;
	SSL_SESSION *dtls_session;
	SSL *dtls_ssl;
	BIO *dtls_bio;
	int dtlsver = DTLS1_BAD_VER;
	const char *cipher = vpninfo->dtls_cipher;

#ifdef HAVE_DTLS12
	if (!strcmp(cipher, "OC-DTLS1_2-AES128-GCM")) {
		dtlsver = DTLS1_2_VERSION;
		cipher = "AES128-GCM-SHA256";
	} else if (!strcmp(cipher, "OC-DTLS1_2-AES256-GCM")) {
		dtlsver = DTLS1_2_VERSION;
		cipher = "AES256-GCM-SHA384";
#ifndef OPENSSL_NO_PSK
	} else if (!strcmp(cipher, "PSK-NEGOTIATE")) {
		dtlsver = 0; /* Let it negotiate */
#endif
	}
#endif

	if (!vpninfo->dtls_ctx) {
#ifdef HAVE_DTLS12
		dtls_method = DTLS_client_method();
#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
		if (dtlsver == DTLS1_BAD_VER)
			dtls_method = DTLSv1_client_method();
#ifdef HAVE_DTLS12
		else if (dtlsver == DTLS1_2_VERSION)
			dtls_method = DTLSv1_2_client_method();
#endif
#endif
		vpninfo->dtls_ctx = SSL_CTX_new(dtls_method);
		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;
		}
		if (dtlsver) {
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
			if (dtlsver == DTLS1_BAD_VER)
				SSL_CTX_set_options(vpninfo->dtls_ctx, SSL_OP_CISCO_ANYCONNECT);
#else
			if (!SSL_CTX_set_min_proto_version(vpninfo->dtls_ctx, dtlsver) ||
			    !SSL_CTX_set_max_proto_version(vpninfo->dtls_ctx, dtlsver)) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Set DTLS CTX version failed\n"));
				openconnect_report_ssl_errors(vpninfo);
				SSL_CTX_free(vpninfo->dtls_ctx);
				vpninfo->dtls_ctx = NULL;
				vpninfo->dtls_attempt_period = 0;
				return -EINVAL;
			}
#endif
#if defined (HAVE_DTLS12) && !defined(OPENSSL_NO_PSK)
		} else {
			SSL_CTX_set_psk_client_callback(vpninfo->dtls_ctx, psk_callback);
			/* For PSK we override the DTLS master secret with one derived
			 * from the HTTPS session. */
			if (!SSL_export_keying_material(vpninfo->https_ssl,
							vpninfo->dtls_secret, PSK_KEY_SIZE,
							PSK_LABEL, PSK_LABEL_SIZE, NULL, 0, 0)) {
				vpn_progress(vpninfo, PRG_ERR,
					     _("Failed to generate DTLS key\n"));
				openconnect_report_ssl_errors(vpninfo);
				SSL_CTX_free(vpninfo->dtls_ctx);
				vpninfo->dtls_ctx = NULL;
				vpninfo->dtls_attempt_period = 0;
				return -EINVAL;
			}
			SSL_CTX_add_client_custom_ext(vpninfo->dtls_ctx, DTLS_APP_ID_EXT,
						      pskident_add, pskident_free, vpninfo,
						      pskident_parse, vpninfo);
			/* For SSL_CTX_set_cipher_list() */
			cipher = "PSK";
#endif
		}
		/* If we don't readahead, then we do short reads and throw
		   away the tail of data packets. */
		SSL_CTX_set_read_ahead(vpninfo->dtls_ctx, 1);

		if (!SSL_CTX_set_cipher_list(vpninfo->dtls_ctx, cipher)) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Set DTLS cipher list failed\n"));
			SSL_CTX_free(vpninfo->dtls_ctx);
			vpninfo->dtls_ctx = NULL;
			vpninfo->dtls_attempt_period = 0;
			return -EINVAL;
		}
	}

	dtls_ssl = SSL_new(vpninfo->dtls_ctx);
	SSL_set_connect_state(dtls_ssl);
	SSL_set_app_data(dtls_ssl, vpninfo);

	if (dtlsver) {
		ciphers = SSL_get_ciphers(dtls_ssl);
		if (dtlsver != 0 && sk_SSL_CIPHER_num(ciphers) != 1) {
			vpn_progress(vpninfo, PRG_ERR, _("Not precisely one DTLS cipher\n"));
			SSL_CTX_free(vpninfo->dtls_ctx);
			SSL_free(dtls_ssl);
			vpninfo->dtls_ctx = NULL;
			vpninfo->dtls_attempt_period = 0;
			return -EINVAL;
		}

		/* We're going to "resume" a session which never existed. Fake it... */
		dtls_session = generate_dtls_session(vpninfo, dtlsver,
						     sk_SSL_CIPHER_value(ciphers, 0));
		if (!dtls_session) {
			SSL_CTX_free(vpninfo->dtls_ctx);
			SSL_free(dtls_ssl);
			vpninfo->dtls_ctx = NULL;
			vpninfo->dtls_attempt_period = 0;
			return -EINVAL;
		}

		/* Add the generated session to the SSL */
		if (!SSL_set_session(dtls_ssl, dtls_session)) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("SSL_set_session() failed with old protocol version 0x%x\n"
				       "Are you using a version of OpenSSL older than 0.9.8m?\n"
				       "See http://rt.openssl.org/Ticket/Display.html?id=1751\n"
				       "Use the --no-dtls command line option to avoid this message\n"),
				     DTLS1_BAD_VER);
			SSL_CTX_free(vpninfo->dtls_ctx);
			SSL_free(dtls_ssl);
			vpninfo->dtls_ctx = NULL;
			vpninfo->dtls_attempt_period = 0;
			SSL_SESSION_free(dtls_session);
			return -EINVAL;
		}

		/* We don't need our own refcount on it any more */
		SSL_SESSION_free(dtls_session);
	}

	dtls_bio = BIO_new_socket(dtls_fd, BIO_NOCLOSE);
	/* Set non-blocking */
	BIO_set_nbio(dtls_bio, 1);
	SSL_set_bio(dtls_ssl, dtls_bio, dtls_bio);

	vpninfo->dtls_ssl = dtls_ssl;

	return 0;
}
Пример #14
0
int setup_esp_keys(struct openconnect_info *vpninfo)
{
	struct esp *esp_in;
	const EVP_CIPHER *encalg;
	const EVP_MD *macalg;
	int ret;

	if (vpninfo->dtls_state == DTLS_DISABLED)
		return -EOPNOTSUPP;
	if (!vpninfo->dtls_addr)
		return -EINVAL;

	switch (vpninfo->esp_enc) {
	case 0x02:
		encalg = EVP_aes_128_cbc();
		break;
	case 0x05:
		encalg = EVP_aes_256_cbc();
		break;
	default:
		return -EINVAL;
	}

	switch (vpninfo->esp_hmac) {
	case 0x01:
		macalg = EVP_md5();
		break;
	case 0x02:
		macalg = EVP_sha1();
		break;
	default:
		return -EINVAL;
	}

	vpninfo->old_esp_maxseq = vpninfo->esp_in[vpninfo->current_esp_in].seq + 32;
	vpninfo->current_esp_in ^= 1;
	esp_in = &vpninfo->esp_in[vpninfo->current_esp_in];

	if (!RAND_bytes((void *)&esp_in->spi, sizeof(esp_in->spi)) ||
	    !RAND_bytes((void *)&esp_in->secrets, sizeof(esp_in->secrets))) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to generate random keys for ESP:\n"));
		openconnect_report_ssl_errors(vpninfo);
		return -EIO;
	}

	ret = init_esp_ciphers(vpninfo, &vpninfo->esp_out, macalg, encalg, 0);
	if (ret)
		return ret;

	ret = init_esp_ciphers(vpninfo, esp_in, macalg, encalg, 1);
	if (ret) {
		destroy_esp_ciphers(&vpninfo->esp_out);
		return ret;
	}

	if (vpninfo->dtls_state == DTLS_NOSECRET)
		vpninfo->dtls_state = DTLS_SECRET;
	vpninfo->pkt_trailer = 16 + 20; /* 16 for pad, 20 for HMAC (of which we use 16) */
	return 0;
}
Пример #15
0
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout)
{
	int work_done = 0;
	char magic_pkt;

	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_TRACE, _("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_TRACE, _("Got DTLS DPD response\n"));
			break;

		case AC_PKT_KEEPALIVE:
			vpn_progress(vpninfo, PRG_TRACE, _("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"));

		/* There ought to be a method of rekeying DTLS without tearing down
		   the CSTP session and restarting, but we don't (yet) know it */
		ret = cstp_reconnect(vpninfo);
		if (ret) {
			vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n"));
			vpninfo->quit_reason = "CSTP reconnect failed";
			return ret;
		}

		if (dtls_restart(vpninfo))
			vpn_progress(vpninfo, PRG_ERR, _("DTLS rekey failed\n"));
		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_restart(vpninfo);
		return 1;

	case KA_DPD:
		vpn_progress(vpninfo, PRG_TRACE, _("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_TRACE, _("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 */
	FD_CLR(vpninfo->dtls_fd, &vpninfo->select_wfds);
	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) {
				FD_SET(vpninfo->dtls_fd, &vpninfo->select_wfds);
				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_restart(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_restart(vpninfo);
				vpninfo->outgoing_queue = this;
				vpninfo->outgoing_qlen++;
				work_done = 1;
			} else if (gnutls_record_get_direction(vpninfo->dtls_ssl)) {
				FD_SET(vpninfo->dtls_fd, &vpninfo->select_wfds);
				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;
}
Пример #16
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;
}
Пример #17
0
static int load_certificate(struct openconnect_info *vpninfo)
{
	if (!strncmp(vpninfo->sslkey, "pkcs11:", 7) ||
	    !strncmp(vpninfo->cert, "pkcs11:", 7)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("This binary built without PKCS#11 support\n"));
		return -EINVAL;
	}

	vpn_progress(vpninfo, PRG_TRACE,
		     _("Using certificate file %s\n"), vpninfo->cert);

	if (strncmp(vpninfo->cert, "keystore:", 9) &&
	    (vpninfo->cert_type == CERT_TYPE_PKCS12 ||
	     vpninfo->cert_type == CERT_TYPE_UNKNOWN)) {
		FILE *f;
		PKCS12 *p12;

		f = fopen(vpninfo->cert, "r");
		if (!f) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to open certificate file %s: %s\n"),
				     vpninfo->cert, strerror(errno));
			return -ENOENT;
		}
		p12 = d2i_PKCS12_fp(f, NULL);
		fclose(f);
		if (p12)
			return load_pkcs12_certificate(vpninfo, p12);

		/* Not PKCS#12 */
		if (vpninfo->cert_type == CERT_TYPE_PKCS12) {
			vpn_progress(vpninfo, PRG_ERR, _("Read PKCS#12 failed\n"));
			openconnect_report_ssl_errors(vpninfo);
			return -EINVAL;
		}
		/* Clear error and fall through to see if it's a PEM file... */
		ERR_clear_error();
	}

	/* It's PEM or TPM now, and either way we need to load the plain cert: */
#ifdef ANDROID_KEYSTORE
	if (!strncmp(vpninfo->cert, "keystore:", 9)) {
		BIO *b = BIO_from_keystore(vpninfo, vpninfo->cert);
		if (!b)
			return -EINVAL;
		vpninfo->cert_x509 = PEM_read_bio_X509_AUX(b, NULL, pem_pw_cb, vpninfo);
		BIO_free(b);
		if (!vpninfo->cert_x509) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to load X509 certificate from keystore\n"));
			openconnect_report_ssl_errors(vpninfo);
			return -EINVAL;
		}
		if (!SSL_CTX_use_certificate(vpninfo->https_ctx, vpninfo->cert_x509)) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to use X509 certificate from keystore\n"));
			openconnect_report_ssl_errors(vpninfo);
			X509_free(vpninfo->cert_x509);
			vpninfo->cert_x509 = NULL;
			return -EINVAL;
		}
	} else
#endif /* ANDROID_KEYSTORE */
	{
		if (!SSL_CTX_use_certificate_chain_file(vpninfo->https_ctx,
							vpninfo->cert)) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Loading certificate failed\n"));
			openconnect_report_ssl_errors(vpninfo);
			return -EINVAL;
		}

		/* Ew, we can't get it back from the OpenSSL CTX in any sane fashion */
		reload_pem_cert(vpninfo);
	}

#ifdef ANDROID_KEYSTORE
	if (!strncmp(vpninfo->sslkey, "keystore:", 9)) {
		EVP_PKEY *key;
		BIO *b;

	again_android:
		b = BIO_from_keystore(vpninfo, vpninfo->sslkey);
		if (!b)
			return -EINVAL;
		key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, vpninfo);
		BIO_free(b);
		if (!key) {
			if (is_pem_password_error(vpninfo))
				goto again_android;
			return -EINVAL;
		}
		if (!SSL_CTX_use_PrivateKey(vpninfo->https_ctx, key)) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to use private key from keystore\n"));
			EVP_PKEY_free(key);
			X509_free(vpninfo->cert_x509);
			vpninfo->cert_x509 = NULL;
			return -EINVAL;
		}
		return 0;
	}
#endif /* ANDROID_KEYSTORE */

	if (vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
		FILE *f = fopen(vpninfo->sslkey, "r");
		char buf[256];

		if (!f) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to open private key file %s: %s\n"),
				     vpninfo->cert, strerror(errno));
			return -ENOENT;
		}

		buf[255] = 0;
		while (fgets(buf, 255, f)) {
			if (!strcmp(buf, "-----BEGIN TSS KEY BLOB-----\n")) {
				vpninfo->cert_type = CERT_TYPE_TPM;
				break;
			} else if (!strcmp(buf, "-----BEGIN RSA PRIVATE KEY-----\n") ||
				   !strcmp(buf, "-----BEGIN DSA PRIVATE KEY-----\n") ||
				   !strcmp(buf, "-----BEGIN ENCRYPTED PRIVATE KEY-----\n")) {
				vpninfo->cert_type = CERT_TYPE_PEM;
				break;
			}
		}
		fclose(f);
		if (vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Failed to identify private key type in '%s'\n"),
				     vpninfo->sslkey);
			return -EINVAL;
		}
	}

	if (vpninfo->cert_type == CERT_TYPE_TPM)
		return load_tpm_certificate(vpninfo);

	/* Standard PEM certificate */
	SSL_CTX_set_default_passwd_cb(vpninfo->https_ctx, pem_pw_cb);
	SSL_CTX_set_default_passwd_cb_userdata(vpninfo->https_ctx, vpninfo);
 again:
	if (!SSL_CTX_use_RSAPrivateKey_file(vpninfo->https_ctx, vpninfo->sslkey,
					    SSL_FILETYPE_PEM)) {
		if (is_pem_password_error(vpninfo))
			goto again;
		return -EINVAL;
	}
	return 0;
}
Пример #18
0
static int start_dtls_handshake(struct openconnect_info *vpninfo, int dtls_fd)
{
	STACK_OF(SSL_CIPHER) *ciphers;
	method_const SSL_METHOD *dtls_method;
	SSL_CIPHER *dtls_cipher;
	SSL *dtls_ssl;
	BIO *dtls_bio;

	if (!vpninfo->dtls_ctx) {
		dtls_method = DTLSv1_client_method();
		vpninfo->dtls_ctx = SSL_CTX_new(dtls_method);
		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;
		}

		/* If we don't readahead, then we do short reads and throw
		   away the tail of data packets. */
		SSL_CTX_set_read_ahead(vpninfo->dtls_ctx, 1);

		if (!SSL_CTX_set_cipher_list(vpninfo->dtls_ctx, vpninfo->dtls_cipher)) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Set DTLS cipher list failed\n"));
			SSL_CTX_free(vpninfo->dtls_ctx);
			vpninfo->dtls_ctx = NULL;
			vpninfo->dtls_attempt_period = 0;
			return -EINVAL;
		}
	}

	if (!vpninfo->dtls_session) {
		/* We're going to "resume" a session which never existed. Fake it... */
		vpninfo->dtls_session = SSL_SESSION_new();
		if (!vpninfo->dtls_session) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("Initialise DTLSv1 session failed\n"));
			vpninfo->dtls_attempt_period = 0;
			return -EINVAL;
		}
		vpninfo->dtls_session->ssl_version = 0x0100; /* DTLS1_BAD_VER */
	}

	/* Do this every time; it may have changed due to a rekey */
	vpninfo->dtls_session->master_key_length = sizeof(vpninfo->dtls_secret);
	memcpy(vpninfo->dtls_session->master_key, vpninfo->dtls_secret,
	       sizeof(vpninfo->dtls_secret));

	vpninfo->dtls_session->session_id_length = sizeof(vpninfo->dtls_session_id);
	memcpy(vpninfo->dtls_session->session_id, vpninfo->dtls_session_id,
	       sizeof(vpninfo->dtls_session_id));

	dtls_ssl = SSL_new(vpninfo->dtls_ctx);
	SSL_set_connect_state(dtls_ssl);

	ciphers = SSL_get_ciphers(dtls_ssl);
	if (sk_SSL_CIPHER_num(ciphers) != 1) {
		vpn_progress(vpninfo, PRG_ERR, _("Not precisely one DTLS cipher\n"));
		SSL_CTX_free(vpninfo->dtls_ctx);
		SSL_free(dtls_ssl);
		SSL_SESSION_free(vpninfo->dtls_session);
		vpninfo->dtls_ctx = NULL;
		vpninfo->dtls_session = NULL;
		vpninfo->dtls_attempt_period = 0;
		return -EINVAL;
	}
	dtls_cipher = sk_SSL_CIPHER_value(ciphers, 0);

	/* Set the appropriate cipher on our session to be resumed */
	vpninfo->dtls_session->cipher = dtls_cipher;
	vpninfo->dtls_session->cipher_id = dtls_cipher->id;

	/* Add the generated session to the SSL */
	if (!SSL_set_session(dtls_ssl, vpninfo->dtls_session)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("SSL_set_session() failed with old protocol version 0x%x\n"
			       "Are you using a version of OpenSSL older than 0.9.8m?\n"
			       "See http://rt.openssl.org/Ticket/Display.html?id=1751\n"
			       "Use the --no-dtls command line option to avoid this message\n"),
			     vpninfo->dtls_session->ssl_version);
		vpninfo->dtls_attempt_period = 0;
		return -EINVAL;
	}

	dtls_bio = BIO_new_socket(dtls_fd, BIO_NOCLOSE);
	/* Set non-blocking */
	BIO_set_nbio(dtls_bio, 1);
	SSL_set_bio(dtls_ssl, dtls_bio, dtls_bio);

	SSL_set_options(dtls_ssl, SSL_OP_CISCO_ANYCONNECT);

	vpninfo->new_dtls_ssl = dtls_ssl;

	return 0;
}