Exemplo n.º 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;
}
Exemplo n.º 2
0
int openconnect_setup_tun_script(struct openconnect_info *vpninfo,
				 const char *tun_script)
{
	pid_t child;
	int fds[2];

	STRDUP(vpninfo->vpnc_script, tun_script);
	vpninfo->script_tun = 1;

	prepare_script_env(vpninfo);
	if (socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)) {
		vpn_progress(vpninfo, PRG_ERR, _("socketpair failed: %s\n"), strerror(errno));
		return -EIO;
	}
	child = fork();
	if (child < 0) {
		vpn_progress(vpninfo, PRG_ERR, _("fork failed: %s\n"), strerror(errno));
		return -EIO;
	} else if (!child) {
		if (setpgid(0, getpid()) < 0)
			perror(_("setpgid"));
		close(fds[0]);
		script_setenv_int(vpninfo, "VPNFD", fds[1]);
		apply_script_env(vpninfo->script_env);
		execl("/bin/sh", "/bin/sh", "-c", vpninfo->vpnc_script, NULL);
		perror(_("execl"));
		exit(1);
	}
	close(fds[1]);
	vpninfo->script_tun = child;
	vpninfo->ifname = strdup(_("(script)"));

	return openconnect_setup_tun_fd(vpninfo, fds[0]);
}
Exemplo n.º 3
0
static BIO *BIO_from_keystore(struct openconnect_info *vpninfo, const char *item)
{
	unsigned char *content;
	BIO *b;
	int len;
	const char *p = item + 9;

	/* Skip first two slashes if the user has given it as
	   keystore://foo ... */
	if (*p == '/')
		p++;
	if (*p == '/')
		p++;

	len = keystore_fetch(p, &content);
	if (len < 0) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to load item '%s' from keystore: %s\n"),
			     p, keystore_strerror(len));
		return NULL;
	}
	if (!(b = BIO_new(BIO_s_mem())) || BIO_write(b, content, len) != len) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to create BIO for keystore item '%s'\n"),
			       p);
		free(content);
		BIO_free(b);
		return NULL;
	}
	free(content);
	return b;
}
Exemplo n.º 4
0
int openconnect_check_peer_cert_hash(struct openconnect_info *vpninfo,
				     const char *old_hash)
{
	char sha1_text[41];
	const char *fingerprint;
	unsigned min_match_len;
	unsigned real_min_match_len = 4;
	unsigned old_len, fingerprint_len;

	if (strchr(old_hash, ':')) {
		if (strncmp(old_hash, "sha1:", 5) == 0) {
			fingerprint = vpninfo->peer_cert_sha1;
			min_match_len = real_min_match_len + sizeof("sha1:")-1;
		} else if (strncmp(old_hash, "sha256:", 7) == 0) {
			fingerprint = vpninfo->peer_cert_sha256;
			min_match_len = real_min_match_len + sizeof("sha256:")-1;
		} else {
			vpn_progress(vpninfo, PRG_ERR, _("Unknown certificate hash: %s.\n"), old_hash);
			return -EIO;
		}

		if (!fingerprint)
			return -EIO;
	} else {
		unsigned char *cert;
		int len, i;
		unsigned char sha1_bin[SHA1_SIZE];

		len = openconnect_get_peer_cert_DER(vpninfo, &cert);
		if (len < 0)
			return len;

		if (openconnect_sha1(sha1_bin, cert, len))
			return -EIO;

		for (i = 0; i < sizeof(sha1_bin); i++)
			sprintf(&sha1_text[i*2], "%02x", sha1_bin[i]);

		fingerprint = sha1_text;
		min_match_len = real_min_match_len;
	}

	old_len = strlen(old_hash);
	fingerprint_len = strlen(fingerprint);

	/* allow partial matches */
	if (old_len < fingerprint_len) {
		if (strncasecmp(old_hash, fingerprint, MAX(min_match_len, old_len))) {
			if (old_len < min_match_len) {
				vpn_progress(vpninfo, PRG_ERR, _("The size of the provided fingerprint is less than the minimum required (%u).\n"), real_min_match_len);
			}
			return 1;
		}
	} else {
		if (strcasecmp(old_hash, fingerprint))
			return 1;
	}

	return 0;
}
Exemplo n.º 5
0
int openconnect_parse_url(struct openconnect_info *vpninfo, const char *url)
{
	char *scheme = NULL;
	int ret;

	UTF8CHECK(url);

	openconnect_set_hostname(vpninfo, NULL);
	free(vpninfo->urlpath);
	vpninfo->urlpath = NULL;

	ret = internal_parse_url(url, &scheme, &vpninfo->hostname,
				 &vpninfo->port, &vpninfo->urlpath, 443);

	if (ret) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to parse server URL '%s'\n"),
			     url);
		return ret;
	}
	if (scheme && strcmp(scheme, "https")) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Only https:// permitted for server URL\n"));
		ret = -EINVAL;
	}
	free(scheme);
	return ret;
}
Exemplo n.º 6
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;
}
Exemplo n.º 7
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;
}
Exemplo n.º 8
0
static void print_gss_err(struct openconnect_info *vpninfo, const char *where,
			  gss_OID mech, OM_uint32 err_maj, OM_uint32 err_min)
{
	OM_uint32 major, minor, msg_ctx = 0;
	gss_buffer_desc status;

	do {
		major = gss_display_status(&minor, err_maj, GSS_C_GSS_CODE,
					   mech, &msg_ctx, &status);
		if (GSS_ERROR(major))
			break;
		vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", where, (char *)status.value);
		gss_release_buffer(&minor, &status);
	} while (msg_ctx);

	msg_ctx = 0;
	do {
		major = gss_display_status(&minor, err_min, GSS_C_MECH_CODE,
					   mech, &msg_ctx, &status);
		if (GSS_ERROR(major))
			break;
		vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", where, (char *)status.value);
		gss_release_buffer(&minor, &status);
	} while (msg_ctx);
}
Exemplo n.º 9
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;
}
Exemplo n.º 10
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;
}
Exemplo n.º 11
0
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;
	int cipher;

	for (cipher = 0; cipher < sizeof(gnutls_dtls_ciphers)/sizeof(gnutls_dtls_ciphers[0]); cipher++) {
		if (!strcmp(vpninfo->dtls_cipher, gnutls_dtls_ciphers[cipher].name))
			goto found_cipher;
	}
	vpn_progress(vpninfo, PRG_ERR, _("Unknown DTLS parameters for requested CipherSuite '%s'\n"),
		     vpninfo->dtls_cipher);
	vpninfo->dtls_attempt_period = 0;

	return -EINVAL;

 found_cipher:
	gnutls_init(&dtls_ssl, GNUTLS_CLIENT|GNUTLS_DATAGRAM|GNUTLS_NONBLOCK);
	err = gnutls_priority_set_direct(dtls_ssl,
					 gnutls_dtls_ciphers[cipher].prio,
					 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)(intptr_t)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_premaster(dtls_ssl, GNUTLS_CLIENT, gnutls_dtls_ciphers[cipher].version,
					   GNUTLS_KX_RSA, gnutls_dtls_ciphers[cipher].cipher,
					   gnutls_dtls_ciphers[cipher].mac, 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->dtls_ssl = dtls_ssl;
	return 0;
}
Exemplo n.º 12
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;
}
Exemplo n.º 13
0
int os_write_tun(struct openconnect_info *vpninfo, struct pkt *pkt)
{
	unsigned char *data = pkt->data;
	int len = pkt->len;

#ifdef TUN_HAS_AF_PREFIX
	if (!vpninfo->script_tun) {
		struct ip *iph = (void *)data;
		int type;

		if (iph->ip_v == 6)
			type = AF_INET6;
		else if (iph->ip_v == 4)
			type = AF_INET;
		else {
			static int complained = 0;
			if (!complained) {
				complained = 1;
				vpn_progress(vpninfo, PRG_ERR,
					     _("Unknown packet (len %d) received: %02x %02x %02x %02x...\n"),
					     len, data[0], data[1], data[2], data[3]);
			}
			return 0;
		}
		data -= sizeof(int);
		len += sizeof(int);
		*(int *)data = htonl(type);
	}
#endif
	if (write(vpninfo->tun_fd, data, len) < 0) {
		/* Handle death of "script" socket */
		if (vpninfo->script_tun && errno == ENOTCONN) {
			vpninfo->quit_reason = "Client connection terminated";
			return -1;
		}
		/* The tun device in the Linux kernel returns -ENOMEM when
		 * the queue is full, so theoretically we could check for
		 * that and retry too.  But it doesn't let us poll() for
		 * the no-longer-full situation, so let's not bother. */
		if (errno == ENOBUFS || errno == EAGAIN || errno == EWOULDBLOCK) {
			monitor_write_fd(vpninfo, tun);
			return -1;
		}
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to write incoming packet: %s\n"),
			     strerror(errno));
	}
	return 0;

}
Exemplo n.º 14
0
static int link_proto(struct openconnect_info *vpninfo, int unit_nr,
		      const char *devname, uint64_t flags)
{
	int ip_fd, mux_id, tun2_fd;
	struct lifreq ifr;

	tun2_fd = open("/dev/tun", O_RDWR);
	if (tun2_fd < 0) {
		vpn_perror(vpninfo, _("Could not open /dev/tun for plumbing"));
		return -EIO;
	}
	if (ioctl(tun2_fd, I_PUSH, "ip") < 0) {
		vpn_perror(vpninfo, _("Can't push IP"));
		close(tun2_fd);
		return -EIO;
	}

	sprintf(ifr.lifr_name, "tun%d", unit_nr);
	ifr.lifr_ppa = unit_nr;
	ifr.lifr_flags = flags;

	if (ioctl(tun2_fd, SIOCSLIFNAME, &ifr) < 0) {
		vpn_perror(vpninfo, _("Can't set ifname"));
		close(tun2_fd);
		return -1;
	}

	ip_fd = open(devname, O_RDWR);
	if (ip_fd < 0) {
		vpn_progress(vpninfo, PRG_ERR, _("Can't open %s: %s"),
			     devname, strerror(errno));
		close(tun2_fd);
		return -1;
	}

	mux_id = ioctl(ip_fd, I_LINK, tun2_fd);
	if (mux_id < 0) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Can't plumb %s for IPv%d: %s\n"),
			     ifr.lifr_name, (flags == IFF_IPV4) ? 4 : 6,
			     strerror(errno));
		close(tun2_fd);
		close(ip_fd);
		return -1;
	}

	close(tun2_fd);

	return ip_fd;
}
Exemplo n.º 15
0
/* MTU setting code for both Linux and BSD systems */
static void ifreq_set_ifname(struct openconnect_info *vpninfo, struct ifreq *ifr)
{
	char *ifname = openconnect_utf8_to_legacy(vpninfo, vpninfo->ifname);
	strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name) - 1);
	if (ifname != vpninfo->ifname)
		free(ifname);
}

static int set_tun_mtu(struct openconnect_info *vpninfo)
{
	struct ifreq ifr;
	int net_fd;

	net_fd = socket(PF_INET, SOCK_DGRAM, 0);
	if (net_fd < 0) {
		vpn_perror(vpninfo, _("open net"));
		return -EINVAL;
	}

	memset(&ifr, 0, sizeof(ifr));
	ifreq_set_ifname(vpninfo, &ifr);
	ifr.ifr_mtu = vpninfo->ip_info.mtu;

	if (ioctl(net_fd, SIOCSIFMTU, &ifr) < 0)
		vpn_perror(vpninfo, _("SIOCSIFMTU"));

	close(net_fd);
	return 0;
}

#ifdef IFF_TUN /* Linux */
intptr_t os_setup_tun(struct openconnect_info *vpninfo)
{
	int tun_fd = -1;
	struct ifreq ifr;
	int tunerr;

	tun_fd = open("/dev/net/tun", O_RDWR);
	if (tun_fd < 0) {
		/* Android has /dev/tun instead of /dev/net/tun
		   Since other systems might have too, just try it
		   as a fallback instead of using ifdef __ANDROID__ */
		tunerr = errno;
		tun_fd = open("/dev/tun", O_RDWR);
	}
	if (tun_fd < 0) {
		/* If the error on /dev/tun is ENOENT, that's boring.
		   Use the error we got on /dev/net/tun instead */
		if (errno != ENOENT)
			tunerr = errno;

		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to open tun device: %s\n"),
			     strerror(tunerr));
		return -EIO;
	}
	memset(&ifr, 0, sizeof(ifr));
	ifr.ifr_flags = IFF_TUN | IFF_NO_PI;
	if (vpninfo->ifname)
		ifreq_set_ifname(vpninfo, &ifr);
	if (ioctl(tun_fd, TUNSETIFF, (void *) &ifr) < 0) {
		int err = errno;
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to bind local tun device (TUNSETIFF): %s\n"),
			     strerror(err));
		if (err == EPERM) {
			vpn_progress(vpninfo, PRG_ERR,
				     _("To configure local networking, openconnect must be running as root\n"
				       "See http://www.infradead.org/openconnect/nonroot.html for more information\n"));
		}
		close(tun_fd);
		return -EIO;
	}
	if (!vpninfo->ifname)
		vpninfo->ifname = strdup(ifr.ifr_name);

	/* Ancient vpnc-scripts might not get this right */
	set_tun_mtu(vpninfo);

	return tun_fd;
}
Exemplo n.º 16
0
static int load_xml_conf_file(struct openconnect_info *vpninfo, char **ptr,
			      size_t *size)
{
	struct stat st;
	int fd;
	char *xmlfile = NULL;

	fd = open(vpninfo->xmlconfig, O_RDONLY);
	if (fd < 0) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to open XML config file: %s\n"),
			     strerror(errno));
		return 0;
	}

	if (fstat(fd, &st)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to fstat() XML config file: %s\n"),
			     strerror(errno));
		goto err;
	}

	xmlfile = malloc(st.st_size);
	if (!xmlfile) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to allocate %lu bytes for XML config file\n"),
			     (unsigned long)st.st_size);
		goto err;
	}

	if (read(fd, xmlfile, st.st_size) != st.st_size) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to read XML config file: %s\n"),
			     strerror(errno));
		goto err;
	}

	*ptr = xmlfile;
	*size = st.st_size;

	close(fd);

	return 1;

err:
	close(fd);
	free(xmlfile);
	return -1;
}
Exemplo n.º 17
0
int print_esp_keys(struct openconnect_info *vpninfo, const char *name, struct esp *esp)
{
	int i;
	const char *enctype, *mactype;
	char enckey[256], mackey[256];
	int enclen, maclen;

	switch(vpninfo->esp_enc) {
	case 0x02:
		enctype = "AES-128-CBC (RFC3602)";
		enclen = 16;
		break;
	case 0x05:
		enctype = "AES-256-CBC (RFC3602)";
		enclen = 32;
		break;
	default:
		return -EINVAL;
	}
	switch(vpninfo->esp_hmac) {
	case 0x01:
		mactype = "HMAC-MD5-96 (RFC2403)";
		maclen = 16;
		break;
	case 0x02:
		mactype = "HMAC-SHA-1-96 (RFC2404)";
		maclen = 20;
		break;
	default:
		return -EINVAL;
	}

	for (i = 0; i < enclen; i++)
		sprintf(enckey + (2 * i), "%02x", esp->secrets[i]);
	for (i = 0; i < maclen; i++)
		sprintf(mackey + (2 * i), "%02x", esp->secrets[enclen + i]);

	vpn_progress(vpninfo, PRG_TRACE,
		     _("Parameters for %s ESP: SPI 0x%08x\n"),
		     name, (unsigned)ntohl(esp->spi));
	vpn_progress(vpninfo, PRG_TRACE,
		     _("ESP encryption type %s key 0x%s\n"),
		     enctype, enckey);
	vpn_progress(vpninfo, PRG_TRACE,
		     _("ESP authentication type %s key 0x%s\n"),
		     mactype, mackey);
	return 0;
}
Exemplo n.º 18
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;
}
Exemplo n.º 19
0
static int pem_pw_cb(char *buf, int len, int w, void *v)
{
	struct openconnect_info *vpninfo = v;
	char *pass = NULL;
	int plen;

	if (vpninfo->cert_password) {
		pass = vpninfo->cert_password;
		vpninfo->cert_password = NULL;
	} else if (request_passphrase(vpninfo, "openconnect_pem",
				      &pass, _("Enter PEM pass phrase:")))
		return -1;

	plen = strlen(pass);

	if (len <= plen) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("PEM password too long (%d >= %d)\n"),
			     plen, len);
		free(pass);
		return -1;
	}

	memcpy(buf, pass, plen+1);
	free(pass);
	return plen;
}
Exemplo n.º 20
0
static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
                               const struct sockaddr *addr, socklen_t addrlen)
{
    struct sockaddr_storage peer;
    socklen_t peerlen = sizeof(peer);
    fd_set wr_set, rd_set;
    int maxfd = sockfd;

    set_sock_nonblock(sockfd);
    if (vpninfo->protect_socket)
        vpninfo->protect_socket(vpninfo->cbdata, sockfd);

    if (connect(sockfd, addr, addrlen) < 0 && !connect_pending())
        return -1;

    do {
        FD_ZERO(&wr_set);
        FD_ZERO(&rd_set);
        FD_SET(sockfd, &wr_set);
        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, _("Socket connect cancelled\n"));
            errno = EINTR;
            return -1;
        }
    } while (!FD_ISSET(sockfd, &wr_set) && !vpninfo->got_pause_cmd);

    /* Check whether connect() succeeded or failed by using
       getpeername(). See http://cr.yp.to/docs/connect.html */
    return getpeername(sockfd, (void *)&peer, &peerlen);
}
Exemplo n.º 21
0
/* In GnuTLS 2.12 since we don't have a normal privkey and hence can't just
   use gnutls_privkey_sign_data() with it, we have to jump through hoops to
   prepare the hash in exactly the right way and call our internal TPM
   signing function. */
int gtls2_tpm_sign_dummy_data(struct openconnect_info *vpninfo,
			      const gnutls_datum_t *data,
			      gnutls_datum_t *sig)
{
	static const unsigned char ber_encode[15] = {
		0x30, 0x21, /* SEQUENCE, length 31 */
		0x30, 0x09,   /* SEQUENCE, length 9 */
		0x06, 0x05,      /* OBJECT_ID, length 5 */
		0x2b, 0x0e, 0x03, 0x02, 0x1a,  /* SHA1 OID: 1.3.14.3.2.26 */
		0x05, 0x00,      /* NULL (parameters) */
		0x04, 0x14,   /* OCTET_STRING, length 20 */
		/* followed by the 20-byte sha1 */
	};
	gnutls_datum_t hash;
	unsigned char digest[sizeof(ber_encode) + SHA1_SIZE];
	size_t shalen = SHA1_SIZE;
	int err;

	err = gnutls_fingerprint(GNUTLS_DIG_SHA1, data,
				 &digest[sizeof(ber_encode)], &shalen);
	if (err) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to SHA1 input data for signing: %s\n"),
			     gnutls_strerror(err));
		return err;
	}

	memcpy(digest, ber_encode, sizeof(ber_encode));

	hash.data = digest;
	hash.size = sizeof(digest);

	return tpm_sign_fn(NULL, vpninfo, &hash, sig);
}
Exemplo n.º 22
0
static int local_config_tun(struct openconnect_info *vpninfo, int mtu_only)
{
	if (!mtu_only)
		vpn_progress(vpninfo, PRG_ERR,
			     _("No vpnc-script configured. Need Solaris IP-setting code\n"));
	return 0;
}
Exemplo n.º 23
0
ssize_t read_file_into_string(struct openconnect_info *vpninfo, const char *fname,
			      char **ptr)
{
	int fd, len;
	struct stat st;
	char *buf;

	fd = openconnect_open_utf8(vpninfo, fname, O_RDONLY|O_BINARY);
	if (fd < 0) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to open %s: %s\n"),
			     fname, strerror(errno));
		return -ENOENT;
	}

	if (fstat(fd, &st)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to fstat() %s: %s\n"),
			     fname, strerror(errno));
		close(fd);
		return -EIO;
	}

	len = st.st_size;
	buf = malloc(len + 1);
	if (!buf) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to allocate %d bytes for %s\n"),
			     len + 1, fname);
		close(fd);
		return -ENOMEM;
	}

	if (read(fd, buf, len) != len) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to read %s: %s\n"),
			     fname, strerror(errno));
		free(buf);
		close(fd);
		return -EIO;
	}

	buf[len] = 0;
	close(fd);
	*ptr = buf;
	return len;
}
Exemplo n.º 24
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;
}
Exemplo n.º 25
0
int script_config_tun(struct openconnect_info *vpninfo, const char *reason)
{
	int ret;
	pid_t pid;

	if (!vpninfo->vpnc_script || vpninfo->script_tun)
		return 0;

	pid = fork();
	if (!pid) {
		/* Child */
		char *script = openconnect_utf8_to_legacy(vpninfo, vpninfo->vpnc_script);

		apply_script_env(vpninfo->script_env);

		setenv("reason", reason, 1);

		execl("/bin/sh", "/bin/sh", "-c", script, NULL);
		exit(127);
	}
	if (pid == -1 || waitpid(pid, &ret, 0) == -1) {
		int e = errno;
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to spawn script '%s' for %s: %s\n"),
			     vpninfo->vpnc_script, reason, strerror(e));
		return -e;
	}

	if (!WIFEXITED(ret)) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Script '%s' exited abnormally (%x)\n"),
			       vpninfo->vpnc_script, ret);
		return -EIO;
	}

	ret = WEXITSTATUS(ret);
	if (ret) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Script '%s' returned error %d\n"),
			     vpninfo->vpnc_script, ret);
		return -EIO;
	}
	return 0;
}
Exemplo n.º 26
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;
}
Exemplo n.º 27
0
static int check_certificate_expiry(struct openconnect_info *vpninfo)
{
	ASN1_TIME *notAfter;
	const char *reason = NULL;
	time_t t;
	int i;

	if (!vpninfo->cert_x509)
		return 0;

	t = time(NULL);
	notAfter = X509_get_notAfter(vpninfo->cert_x509);
	i = X509_cmp_time(notAfter, &t);
	if (!i) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Error in client cert notAfter field\n"));
		return -EINVAL;
	} else if (i < 0) {
		reason = _("Client certificate has expired at");
	} else {
		t += vpninfo->cert_expire_warning;
		i = X509_cmp_time(notAfter, &t);
		if (i < 0)
			reason = _("Client certificate expires soon at");
	}
	if (reason) {
		BIO *bp = BIO_new(BIO_s_mem());
		BUF_MEM *bm;
		const char *expiry = _("<error>");
		char zero = 0;

		if (bp) {
			ASN1_TIME_print(bp, notAfter);
			BIO_write(bp, &zero, 1);
			BIO_get_mem_ptr(bp, &bm);
			expiry = bm->data;
		}
		vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", reason, expiry);
		if (bp)
			BIO_free(bp);
	}
	return 0;
}
Exemplo n.º 28
0
int openconnect_setup_dtls(struct openconnect_info *vpninfo,
			   int attempt_period)

{
	if (vpninfo->proto->udp_setup)
		return vpninfo->proto->udp_setup(vpninfo, attempt_period);

	vpn_progress(vpninfo, PRG_ERR,
		     _("Built against SSL library with no Cisco DTLS support\n"));
	return -EINVAL;
}
Exemplo n.º 29
0
static int tpm_sign_fn(gnutls_privkey_t key, void *_vpninfo,
		       const gnutls_datum_t *data, gnutls_datum_t *sig)
{
	struct openconnect_info *vpninfo = _vpninfo;
	TSS_HHASH hash;
	int err;

	vpn_progress(vpninfo, PRG_TRACE,
		     _("TPM sign function called for %d bytes.\n"),
		     data->size);

	err = Tspi_Context_CreateObject(vpninfo->tpm_context, TSS_OBJECT_TYPE_HASH,
					TSS_HASH_OTHER, &hash);
	if (err) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to create TPM hash object: %s\n"),
			     Trspi_Error_String(err));
		return GNUTLS_E_PK_SIGN_FAILED;
	}
	err = Tspi_Hash_SetHashValue(hash, data->size, data->data);
	if (err) {
		vpn_progress(vpninfo, PRG_ERR,
			     _("Failed to set value in TPM hash object: %s\n"),
			     Trspi_Error_String(err));
		Tspi_Context_CloseObject(vpninfo->tpm_context, hash);
		return GNUTLS_E_PK_SIGN_FAILED;
	}
	err = Tspi_Hash_Sign(hash, vpninfo->tpm_key, &sig->size, &sig->data);
	Tspi_Context_CloseObject(vpninfo->tpm_context, hash);
	if (err) {
		if (vpninfo->tpm_key_policy || err != TPM_E_AUTHFAIL)
			vpn_progress(vpninfo, PRG_ERR,
				     _("TPM hash signature failed: %s\n"),
				     Trspi_Error_String(err));
		if (err == TPM_E_AUTHFAIL)
			return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
		else
			return GNUTLS_E_PK_SIGN_FAILED;
	}
	return 0;
}
Exemplo n.º 30
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;
}