Skip to content

Commit

Permalink
Add Pulse Connect Secure support
Browse files Browse the repository at this point in the history
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
  • Loading branch information
dwmw2 committed Jun 7, 2019
1 parent 669add7 commit b795ff3
Show file tree
Hide file tree
Showing 13 changed files with 2,297 additions and 12 deletions.
15 changes: 9 additions & 6 deletions Makefile.am
Expand Up @@ -27,30 +27,33 @@ openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIB
if OPENCONNECT_WIN32
openconnect_SOURCES += openconnect.rc
endif
library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c
library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c openconnect-internal.h
lib_srcs_cisco = auth.c cstp.c
lib_srcs_juniper = oncp.c lzo.c auth-juniper.c
lib_srcs_pulse = pulse.c
lib_srcs_globalprotect = gpst.c auth-globalprotect.c
lib_srcs_oath = oath.c

library_srcs += $(lib_srcs_juniper) $(lib_srcs_cisco) $(lib_srcs_oath) \
$(lib_srcs_globalprotect) $(lib_srcs_pulse)

lib_srcs_gnutls = gnutls.c gnutls_tpm.c gnutls_tpm2.c
lib_srcs_openssl = openssl.c openssl-pkcs11.c
lib_srcs_win32 = tun-win32.c sspi.c
lib_srcs_posix = tun.c
lib_srcs_gssapi = gssapi.c
lib_srcs_iconv = iconv.c
lib_srcs_oath = oath.c
lib_srcs_yubikey = yubikey.c
lib_srcs_stoken = stoken.c
lib_srcs_esp = esp.c esp-seqno.c
lib_srcs_dtls = dtls.c

POTFILES = $(openconnect_SOURCES) $(lib_srcs_cisco) $(lib_srcs_juniper) $(lib_srcs_globalprotect) \
gnutls-esp.c gnutls-dtls.c openssl-esp.c openssl-dtls.c \
POTFILES = $(openconnect_SOURCES) gnutls-esp.c gnutls-dtls.c openssl-esp.c openssl-dtls.c \
$(lib_srcs_esp) $(lib_srcs_dtls) gnutls_tpm2_esys.c gnutls_tpm2_ibm.c \
$(lib_srcs_openssl) $(lib_srcs_gnutls) $(library_srcs) \
$(lib_srcs_win32) $(lib_srcs_posix) $(lib_srcs_gssapi) $(lib_srcs_iconv) \
$(lib_srcs_oath) $(lib_srcs_yubikey) $(lib_srcs_stoken) openconnect-internal.h
$(lib_srcs_yubikey) $(lib_srcs_stoken)

library_srcs += $(lib_srcs_juniper) $(lib_srcs_cisco) $(lib_srcs_oath) $(lib_srcs_globalprotect)
if OPENCONNECT_LIBPCSCLITE
library_srcs += $(lib_srcs_yubikey)
endif
Expand Down
60 changes: 60 additions & 0 deletions gnutls.c
Expand Up @@ -2597,3 +2597,63 @@ int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge)
hpos = hash[hpos] & 15;
return load_be32(&hash[hpos]) & 0x7fffffff;
}


static int ttls_pull_timeout_func(gnutls_transport_ptr_t t, unsigned int ms)
{
struct openconnect_info *vpninfo = t;

vpn_progress(vpninfo, PRG_TRACE, _("ttls_pull_timeout_func %dms\n"), ms);
return 0;
}

static ssize_t ttls_pull_func(gnutls_transport_ptr_t t, void *buf, size_t len)
{
int ret = pulse_eap_ttls_recv(t, buf, len);
if (ret >= 0)
return ret;
else
return GNUTLS_E_PULL_ERROR;
}

static ssize_t ttls_push_func(gnutls_transport_ptr_t t, const void *buf, size_t len)
{
int ret = pulse_eap_ttls_send(t, buf, len);
if (ret >= 0)
return ret;
else
return GNUTLS_E_PUSH_ERROR;
}

void *establish_eap_ttls(struct openconnect_info *vpninfo)
{
gnutls_session_t ttls_sess = NULL;
int err;

gnutls_init(&ttls_sess, GNUTLS_CLIENT);
gnutls_session_set_ptr(ttls_sess, (void *) vpninfo);
gnutls_transport_set_ptr(ttls_sess, (void *) vpninfo);

gnutls_transport_set_push_function(ttls_sess, ttls_push_func);
gnutls_transport_set_pull_function(ttls_sess, ttls_pull_func);
gnutls_transport_set_pull_timeout_function(ttls_sess, ttls_pull_timeout_func);

gnutls_credentials_set(ttls_sess, GNUTLS_CRD_CERTIFICATE, vpninfo->https_cred);

err = gnutls_priority_set_direct(ttls_sess,
vpninfo->gnutls_prio, NULL);

err = gnutls_handshake(ttls_sess);
if (!err) {
vpn_progress(vpninfo, PRG_TRACE,
_("Established EAP-TTLS session\n"));
return ttls_sess;
}
gnutls_deinit(ttls_sess);
return NULL;
}

void destroy_eap_ttls(struct openconnect_info *vpninfo, void *sess)
{
gnutls_deinit(sess);
}
4 changes: 2 additions & 2 deletions http.c
Expand Up @@ -507,8 +507,8 @@ int process_http_response(struct openconnect_info *vpninfo, int connect,
if (result == 100)
goto cont;

/* On successful CONNECT, there is no body. Return success */
if (connect && result == 200)
/* On successful CONNECT or upgrade, there is no body. Return success */
if (connect && (result == 200 || result == 101))
return result;

/* Now the body, if there is one */
Expand Down
26 changes: 24 additions & 2 deletions library.c
Expand Up @@ -127,7 +127,7 @@ const struct vpn_proto openconnect_protos[] = {
}, {
.name = "nc",
.pretty_name = N_("Juniper Network Connect"),
.description = N_("Compatible with Juniper Network Connect / Pulse Secure SSL VPN"),
.description = N_("Compatible with Juniper Network Connect"),
.flags = OC_PROTO_PROXY | OC_PROTO_CSD | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP,
.vpn_close_session = oncp_bye,
.tcp_connect = oncp_connect,
Expand Down Expand Up @@ -161,6 +161,25 @@ const struct vpn_proto openconnect_protos[] = {
.udp_shutdown = esp_shutdown,
.udp_send_probes = gpst_esp_send_probes,
.udp_catch_probe = gpst_esp_catch_probe,
#endif
}, {
.name = "pulse",
.pretty_name = N_("Pulse Connect Secure"),
.description = N_("Compatible with Pulse Connect Secure SSL VPN"),
.flags = OC_PROTO_PROXY,
.vpn_close_session = pulse_bye,
.tcp_connect = pulse_connect,
.tcp_mainloop = pulse_mainloop,
.add_http_headers = http_common_headers,
.obtain_cookie = pulse_obtain_cookie,
.udp_protocol = "ESP",
#ifdef HAVE_ESPx
.udp_setup = esp_setup,
.udp_mainloop = esp_mainloop,
.udp_close = esp_close,
.udp_shutdown = esp_shutdown,
.udp_send_probes = pulse_esp_send_probes,
.udp_catch_probe = pulse_esp_catch_probe,
#endif
},
{ /* NULL */ }
Expand Down Expand Up @@ -363,7 +382,10 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
free(vpninfo->ifname);
free(vpninfo->dtls_cipher);
free(vpninfo->peer_cert_hash);
#ifdef OPENCONNECT_GNUTLS
#if defined(OPENCONNECT_OPENSSL)
if (vpninfo->ttls_bio_meth)
BIO_meth_free(vpninfo->ttls_bio_meth);
#elif defined(OPENCONNECT_GNUTLS)
gnutls_free(vpninfo->cstp_cipher); /* In OpenSSL this is const */
#ifdef HAVE_DTLS
gnutls_free(vpninfo->gnutls_dtls_cipher);
Expand Down
1 change: 1 addition & 0 deletions oncp.c
Expand Up @@ -1126,6 +1126,7 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
/* Don't free the 'special' packets */
if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt) {
free(vpninfo->pending_deflated_pkt);
vpninfo->pending_deflated_pkt = NULL;
} else if (vpninfo->current_ssl_pkt == &esp_enable_pkt) {
/* Only set the ESP state to connected and actually start
sending packets on it once the enable message has been
Expand Down
29 changes: 29 additions & 0 deletions openconnect-internal.h
Expand Up @@ -137,6 +137,13 @@ struct pkt {
unsigned char pad[8];
unsigned char hdr[16];
} gpst;
struct {
unsigned char pad[8];
uint32_t vendor;
uint32_t type;
uint32_t len;
uint32_t ident;
} pulse;
};
unsigned char data[];
};
Expand Down Expand Up @@ -500,8 +507,10 @@ struct openconnect_info {
X509 *cert_x509;
SSL_CTX *https_ctx;
SSL *https_ssl;
BIO_METHOD *ttls_bio_meth;
#elif defined(OPENCONNECT_GNUTLS)
gnutls_session_t https_sess;
gnutls_session_t eap_ttls_sess;
gnutls_certificate_credentials_t https_cred;
gnutls_psk_client_credentials_t psk_cred;
char local_cert_md5[MD5_SIZE * 2 + 1]; /* For CSD */
Expand All @@ -513,6 +522,12 @@ struct openconnect_info {
struct oc_tpm2_ctx *tpm2;
#endif
#endif /* OPENCONNECT_GNUTLS */
struct oc_text_buf *ttls_pushbuf;
uint8_t ttls_eap_ident;
unsigned char *ttls_recvbuf;
int ttls_recvpos;
int ttls_recvlen;

struct pin_cache *pin_cache;
struct keepalive_info ssl_times;
int owe_ssl_dpd_response;
Expand Down Expand Up @@ -560,6 +575,8 @@ struct openconnect_info {
unsigned char dtls_app_id[32];
unsigned dtls_app_id_size;

uint32_t ift_seq;

int cisco_dtls12;
char *dtls_cipher;
char *vpnc_script;
Expand Down Expand Up @@ -830,6 +847,9 @@ int dtls_try_handshake(struct openconnect_info *vpninfo);
unsigned dtls_set_mtu(struct openconnect_info *vpninfo, unsigned mtu);
void dtls_ssl_free(struct openconnect_info *vpninfo);

void *establish_eap_ttls(struct openconnect_info *vpninfo);
void destroy_eap_ttls(struct openconnect_info *vpninfo, void *sess);

/* dtls.c */
int dtls_setup(struct openconnect_info *vpninfo, int dtls_attempt_period);
int dtls_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable);
Expand Down Expand Up @@ -863,6 +883,15 @@ void oncp_esp_close(struct openconnect_info *vpninfo);
int oncp_esp_send_probes(struct openconnect_info *vpninfo);
int oncp_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt);

/* pulse.c */
int pulse_obtain_cookie(struct openconnect_info *vpninfo);
void pulse_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
int pulse_connect(struct openconnect_info *vpninfo);
int pulse_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable);
int pulse_bye(struct openconnect_info *vpninfo, const char *reason);
int pulse_eap_ttls_send(struct openconnect_info *vpninfo, const void *data, int len);
int pulse_eap_ttls_recv(struct openconnect_info *vpninfo, void *data, int len);

/* auth-globalprotect.c */
int gpst_obtain_cookie(struct openconnect_info *vpninfo);
void gpst_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
Expand Down
2 changes: 1 addition & 1 deletion openssl-dtls.c
Expand Up @@ -524,7 +524,7 @@ int dtls_try_handshake(struct openconnect_info *vpninfo)
/* 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)
if (vpninfo->peer_addr->sa_family == AF_INET6)
data_mtu -= 40; /* IPv6 header */
else
data_mtu -= 20; /* Legacy IP header */
Expand Down
78 changes: 78 additions & 0 deletions openssl.c
Expand Up @@ -1978,3 +1978,81 @@ int hotp_hmac(struct openconnect_info *vpninfo, const void *challenge)
hashlen = hash[hashlen - 1] & 15;
return load_be32(&hash[hashlen]) & 0x7fffffff;
}

static int ttls_push_func(BIO *b, const char *buf, int len)
{
struct openconnect_info *vpninfo = BIO_get_data(b);
int ret = pulse_eap_ttls_send(vpninfo, buf, len);
if (ret >= 0)
return ret;

return 0;
}

static int ttls_pull_func(BIO *b, char *buf, int len)
{
struct openconnect_info *vpninfo = BIO_get_data(b);
int ret = pulse_eap_ttls_recv(vpninfo, buf, len);
if (ret >= 0)
return ret;

return 0;
}

static long ttls_ctrl_func(BIO *b, int cmd, long larg, void *iarg)
{
switch(cmd) {
case BIO_CTRL_FLUSH:
return 1;
default:
return 0;
}
}

void *establish_eap_ttls(struct openconnect_info *vpninfo)
{
SSL *ttls_ssl = NULL;
BIO *bio;
int err;


if (!vpninfo->ttls_bio_meth) {
vpninfo->ttls_bio_meth = BIO_meth_new(BIO_get_new_index(), "EAP-TTLS");
BIO_meth_set_write(vpninfo->ttls_bio_meth, ttls_push_func);
BIO_meth_set_read(vpninfo->ttls_bio_meth, ttls_pull_func);
BIO_meth_set_ctrl(vpninfo->ttls_bio_meth, ttls_ctrl_func);
}

bio = BIO_new(vpninfo->ttls_bio_meth);
BIO_set_data(bio, vpninfo);
BIO_set_init(bio, 1);
ttls_ssl = SSL_new(vpninfo->https_ctx);
workaround_openssl_certchain_bug(vpninfo, ttls_ssl);

SSL_set_bio(ttls_ssl, bio, bio);

SSL_set_verify(ttls_ssl, SSL_VERIFY_PEER, NULL);

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

err = SSL_connect(ttls_ssl);
if (err == 1) {
vpn_progress(vpninfo, PRG_TRACE,
_("Established EAP-TTLS session\n"));
return ttls_ssl;
}

err = SSL_get_error(ttls_ssl, err);
vpn_progress(vpninfo, PRG_ERR, _("EAP-TTLS connection failure %d\n"), err);
openconnect_report_ssl_errors(vpninfo);
SSL_free(ttls_ssl);
return NULL;
}

void destroy_eap_ttls(struct openconnect_info *vpninfo, void *ttls)
{
SSL_free(ttls);
/* Leave the BIO_METH for now. It may get reused and we don't want to
* have to call BIO_get_new_index() more times than is necessary */
}

0 comments on commit b795ff3

Please sign in to comment.