From 0e4fd86e593c4d7ead2e408282c8c4b24935101b Mon Sep 17 00:00:00 2001 From: Daniel Lenski Date: Tue, 7 Apr 2020 23:10:14 -0700 Subject: [PATCH] periodic TNCC - Factor out oncp_send_tncc_command function - Reuse csd_token and csd_starturl for TNCC state (just like GP already does) - Teach `tncc-emulate.py` to send the TNCC interval *back* to OpenConnect - Apply `--force-trojan` (vpninfo->trojan_interval) to TNCC as well Signed-off-by: Daniel Lenski --- auth-juniper.c | 124 +++++++++++++++++++++++++--------------- oncp.c | 5 ++ openconnect-internal.h | 1 + trojans/tncc-emulate.py | 25 ++++++-- 4 files changed, 102 insertions(+), 53 deletions(-) diff --git a/auth-juniper.c b/auth-juniper.c index 69a87081..bafbae58 100644 --- a/auth-juniper.c +++ b/auth-juniper.c @@ -286,9 +286,42 @@ static xmlNodePtr find_form_node(xmlDocPtr doc) return NULL; } +int oncp_send_tncc_command(struct openconnect_info *vpninfo, int start) +{ + const char *dspreauth = vpninfo->csd_token, *dsurl = vpninfo->csd_starturl ? : "null"; + struct oc_text_buf *buf; + buf = buf_alloc(); + + if (start) { + buf_append(buf, "start\n"); + buf_append(buf, "IC=%s\n", vpninfo->hostname); + buf_append(buf, "Cookie=%s\n", dspreauth); + buf_append(buf, "DSSIGNIN=%s\n", dsurl); + } else { + buf_append(buf, "setcookie\n"); + buf_append(buf, "Cookie=%s\n", dspreauth); + } + + if (buf_error(buf)) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to allocate memory for communication with TNCC\n")); + return buf_free(buf); + } + if (cancellable_send(vpninfo, vpninfo->tncc_fd, buf->data, buf->pos) != buf->pos) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to send command to TNCC\n")); + buf_free(buf); + return -EIO; + } + + /* Mainloop timers need to know the last Trojan was invoked */ + vpninfo->last_trojan = time(NULL); + return buf_free(buf); +} + static int check_cookie_success(struct openconnect_info *vpninfo) { - const char *dslast = NULL, *dsfirst = NULL, *dsurl = NULL, *dsid = NULL, *dspreauth = NULL; + const char *dslast = NULL, *dsfirst = NULL, *dsurl = NULL, *dsid = NULL; struct oc_vpn_option *cookie; struct oc_text_buf *buf; @@ -301,27 +334,24 @@ static int check_cookie_success(struct openconnect_info *vpninfo) dsid = cookie->value; else if (!strcmp(cookie->option, "DSSignInUrl")) dsurl = cookie->value; - else if (!strcmp(cookie->option, "DSPREAUTH")) - dspreauth = cookie->value; + else if (!strcmp(cookie->option, "DSSIGNIN")) { + free(vpninfo->csd_starturl); + vpninfo->csd_starturl = strdup(cookie->value); + } else if (!strcmp(cookie->option, "DSPREAUTH")) { + free(vpninfo->csd_token); + vpninfo->csd_token = strdup(cookie->value); + } } if (!dsid) return -ENOENT; - buf = buf_alloc(); if (vpninfo->tncc_fd != -1) { - buf_append(buf, "setcookie\n"); - buf_append(buf, "Cookie=%s\n", dspreauth); - if (buf_error(buf)) - return buf_free(buf); - if (send(vpninfo->tncc_fd, buf->data, buf->pos, 0) < 0) { - vpn_progress(vpninfo, PRG_ERR, - _("Failed to send cookie to TNCC\n")); - /* Continue anyway */ - } - buf_truncate(buf); + /* update TNCC once we get a DSID cookie */ + oncp_send_tncc_command(vpninfo, 0); } /* XXX: Do these need escaping? Could they theoreetically have semicolons in? */ + buf = buf_alloc(); buf_append(buf, "DSID=%s", dsid); if (dsfirst) buf_append(buf, "; DSFirst=%s", dsfirst); @@ -349,18 +379,10 @@ static int tncc_preauth(struct openconnect_info *vpninfo) { int sockfd[2]; pid_t pid; - struct oc_text_buf *buf; - struct oc_vpn_option *cookie; - const char *dspreauth = NULL, *dssignin = "null"; + const char *dspreauth = vpninfo->csd_token; char recvbuf[1024]; - int len, count; + int len, count, ret; - for (cookie = vpninfo->cookies; cookie; cookie = cookie->next) { - if (!strcmp(cookie->option, "DSPREAUTH")) - dspreauth = cookie->value; - else if (!strcmp(cookie->option, "DSSIGNIN")) - dssignin = cookie->value; - } if (!dspreauth) { vpn_progress(vpninfo, PRG_ERR, _("No DSPREAUTH cookie; not attempting TNCC\n")); @@ -404,6 +426,12 @@ static int tncc_preauth(struct openconnect_info *vpninfo) goto out; if (setenv("TNCC_HOSTNAME", vpninfo->localname, 1)) goto out; + if (!vpninfo->trojan_interval) { + char is[32]; + snprintf(is, 32, "%d", vpninfo->trojan_interval); + if (setenv("TNCC_INTERVAL", is, 1)) + goto out; + } execl(vpninfo->csd_wrapper, vpninfo->csd_wrapper, vpninfo->hostname, NULL); out: @@ -413,27 +441,16 @@ static int tncc_preauth(struct openconnect_info *vpninfo) } waitpid(pid, NULL, 0); close(sockfd[0]); + vpninfo->tncc_fd = sockfd[1]; - buf = buf_alloc(); - buf_append(buf, "start\n"); - buf_append(buf, "IC=%s\n", vpninfo->hostname); - buf_append(buf, "Cookie=%s\n", dspreauth); - buf_append(buf, "DSSIGNIN=%s\n", dssignin); - if (buf_error(buf)) { - vpn_progress(vpninfo, PRG_ERR, - _("Failed to allocate memory for communication with TNCC\n")); - close(sockfd[1]); - return buf_free(buf); + ret = oncp_send_tncc_command(vpninfo, 1); + if (ret < 0) { + err: + close(vpninfo->tncc_fd); + vpninfo->tncc_fd = -1; + return ret; } - if (cancellable_send(vpninfo, sockfd[1], buf->data, buf->pos) != buf->pos) { - vpn_progress(vpninfo, PRG_ERR, - _("Failed to send start command to TNCC\n")); - buf_free(buf); - close(sockfd[1]); - return -EIO; - } - buf_free(buf); vpn_progress(vpninfo, PRG_DEBUG, _("Sent start; waiting for response from TNCC\n")); @@ -443,16 +460,16 @@ static int tncc_preauth(struct openconnect_info *vpninfo) respfail: vpn_progress(vpninfo, PRG_ERR, _("Failed to read response from TNCC\n")); - close(sockfd[1]); - return -EIO; + ret = -EIO; + goto err; } if (strcmp(recvbuf, "200")) { vpn_progress(vpninfo, PRG_ERR, _("Received unsuccessful %s response from TNCC\n"), recvbuf); - close(sockfd[1]); - return -EINVAL; + ret = -EINVAL; + goto err; } vpn_progress(vpninfo, PRG_TRACE, _("TNCC response 200 OK\n")); @@ -474,7 +491,20 @@ static int tncc_preauth(struct openconnect_info *vpninfo) _("Got new DSPREAUTH cookie from TNCC: %s\n"), recvbuf); http_add_cookie(vpninfo, "DSPREAUTH", recvbuf, 1); - vpninfo->tncc_fd = sockfd[1]; + + /* Fourth line, if present, is the interval to rerun TNCC */ + len = cancellable_gets(vpninfo, sockfd[1], recvbuf, sizeof(recvbuf)); + if (len < 0) + goto respfail; + if (len > 0) { + int interval = atoi(recvbuf); + if (interval != vpninfo->trojan_interval) { + vpninfo->trojan_interval = interval; + vpn_progress(vpninfo, PRG_DEBUG, + _("Got reauth interval from TNCC: %d seconds\n"), + interval); + } + } count = 0; do { diff --git a/oncp.c b/oncp.c index 71571a49..72b5ed77 100644 --- a/oncp.c +++ b/oncp.c @@ -915,6 +915,11 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable) handle that */ int receive_mtu = MAX(16384, vpninfo->ip_info.mtu); + if (trojan_check_deadline(vpninfo, timeout)) { + /* Periodic TNCC */ + oncp_send_tncc_command(vpninfo, 0); + } + len = receive_mtu + vpninfo->pkt_trailer; if (!vpninfo->cstp_pkt) { vpninfo->cstp_pkt = malloc(sizeof(struct pkt) + len); diff --git a/openconnect-internal.h b/openconnect-internal.h index 9d4f62b2..09b747d5 100644 --- a/openconnect-internal.h +++ b/openconnect-internal.h @@ -901,6 +901,7 @@ int compress_packet(struct openconnect_info *vpninfo, int compr_type, struct pkt /* auth-juniper.c */ int oncp_obtain_cookie(struct openconnect_info *vpninfo); +int oncp_send_tncc_command(struct openconnect_info *vpninfo, int first); void oncp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf); /* oncp.c */ diff --git a/trojans/tncc-emulate.py b/trojans/tncc-emulate.py index 8040693e..140a1b08 100755 --- a/trojans/tncc-emulate.py +++ b/trojans/tncc-emulate.py @@ -270,7 +270,7 @@ class x509cert(object): self.subject = self.decode_names(tbs['subject']) class tncc(object): - def __init__(self, vpn_host, device_id=None, funk=None, platform=None, hostname=None, mac_addrs=[], certs=[]): + def __init__(self, vpn_host, device_id=None, funk=None, platform=None, hostname=None, mac_addrs=[], certs=[], interval=None): self.vpn_host = vpn_host self.path = '/dana-na/' @@ -279,6 +279,7 @@ class tncc(object): self.hostname = hostname self.mac_addrs = mac_addrs self.avail_certs = certs + self.interval = interval self.deviceid = device_id @@ -508,6 +509,12 @@ class tncc(object): # Parse the data returned into a key/value dict response = self.parse_response() + if 'interval' in response: + m = int(response['interval']) + logging.debug('Got interval of %d minutes' % m) + if self.interval is None or self.interval > m*60: + self.interval = m*60 + # msg has the stuff we want, it's base64 encoded logging.debug('Receiving packet -') msg_raw = base64.b64decode(response['msg']) @@ -597,11 +604,15 @@ class tncc_server(object): args[key] = val if cmd == 'start': cookie = self.tncc.get_cookie(args['Cookie'], args['DSSIGNIN']) - resp = '200\n3\n%s\n\n' % cookie.value - sock.send(resp.encode('ascii')) + resp = ['200', '3', cookie.value] + if self.tncc.interval is not None: + resp.append(str(self.tncc.interval)) + sock.send(('\n'.join(resp) + '\n\n').encode('ascii')) elif cmd == 'setcookie': - # FIXME: Support for periodic updates - dsid_value = args['Cookie'] + cookie = self.tncc.get_cookie(args['Cookie'], + self.tncc.find_cookie('DSSIGNIN')) + else: + logging.warn('Unknown command %r' % cmd) def fingerprint_checking_SSLSocket(_fingerprint): class SSLSocket(ssl.SSLSocket): @@ -621,6 +632,8 @@ if __name__ == "__main__": funk = 'TNCC_FUNK' in os.environ and os.environ['TNCC_FUNK'] != '0' + interval = int(os.environ.get('TNCC_INTERVAL', 0)) or None + platform = os.environ.get('TNCC_PLATFORM', platform.system() + ' ' + platform.release()) if 'TNCC_HWADDR' in os.environ: @@ -668,7 +681,7 @@ if __name__ == "__main__": # \HKEY_CURRENT_USER\Software\Juniper Networks\Device Id device_id = os.environ.get('TNCC_DEVICE_ID') - t = tncc(vpn_host, device_id, funk, platform, hostname, mac_addrs, certs) + t = tncc(vpn_host, device_id, funk, platform, hostname, mac_addrs, certs, interval) sock = socket.fromfd(0, socket.AF_UNIX, socket.SOCK_SEQPACKET) server = tncc_server(sock, t) while True: -- 2.50.1