1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285 |
- /*
- * OpenConnect (SSL + DTLS) VPN client
- *
- * Copyright © 2008-2015 Intel Corporation.
- *
- * Author: David Woodhouse <dwmw2@infradead.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- */
- /*
- * Grateful thanks to Tiebing Zhang, who did much of the hard work
- * of analysing and decoding the protocol.
- */
- #include <config.h>
- #include "openconnect-internal.h"
- #include <unistd.h>
- #include <fcntl.h>
- #include <sys/types.h>
- #include <time.h>
- #include <string.h>
- #include <ctype.h>
- #include <errno.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <stdarg.h>
- static void buf_append_tlv(struct oc_text_buf *buf, uint16_t val, uint32_t len, void *data)
- {
- unsigned char b[6];
- store_be16(b, val);
- store_be32(b + 2, len);
- buf_append_bytes(buf, b, 6);
- if (len)
- buf_append_bytes(buf, data, len);
- }
- static void buf_append_tlv_be32(struct oc_text_buf *buf, uint16_t val, uint32_t data)
- {
- unsigned char d[4];
- store_be32(d, data);
- buf_append_tlv(buf, val, 4, d);
- }
- static const char authpkt_head[] = { 0x00, 0x04, 0x00, 0x00, 0x00 };
- static const char authpkt_tail[] = { 0xbb, 0x01, 0x00, 0x00, 0x00, 0x00 };
- #define GRP_ATTR(g, a) (((g) << 16) | (a))
- static int process_attr(struct openconnect_info *vpninfo,
- struct oc_vpn_option **new_opts, struct oc_ip_info *new_ip_info,
- int group, int attr, unsigned char *data, int attrlen)
- {
- char buf[80];
- int i;
- switch(GRP_ATTR(group, attr)) {
- case GRP_ATTR(6, 2):
- if (attrlen != 4) {
- badlen:
- vpn_progress(vpninfo, PRG_ERR,
- _("Unexpected length %d for TLV %d/%d\n"),
- attrlen, group, attr);
- return -EINVAL;
- }
- new_ip_info->mtu = load_be32(data);
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Received MTU %d from server\n"),
- new_ip_info->mtu);
- break;
- case GRP_ATTR(2, 1):
- if (attrlen != 4)
- goto badlen;
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
- vpn_progress(vpninfo, PRG_DEBUG, _("Received DNS server %s\n"), buf);
- for (i = 0; i < 3; i++) {
- if (!new_ip_info->dns[i]) {
- new_ip_info->dns[i] = add_option_dup(new_opts, "DNS", buf, -1);
- break;
- }
- }
- break;
- case GRP_ATTR(2, 2):
- vpn_progress(vpninfo, PRG_DEBUG, _("Received DNS search domain %.*s\n"),
- attrlen, (char *)data);
- new_ip_info->domain = add_option_dup(new_opts, "search", (char *)data, attrlen);
- if (new_ip_info->domain) {
- char *p = (char *)new_ip_info->domain;
- while ((p = strchr(p, ',')))
- *p = ' ';
- }
- break;
- case GRP_ATTR(1, 1):
- if (attrlen != 4)
- goto badlen;
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
- vpn_progress(vpninfo, PRG_DEBUG, _("Received internal IP address %s\n"), buf);
- new_ip_info->addr = add_option_dup(new_opts, "ipaddr", buf, -1);
- break;
- case GRP_ATTR(1, 2):
- if (attrlen != 4)
- goto badlen;
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
- vpn_progress(vpninfo, PRG_DEBUG, _("Received netmask %s\n"), buf);
- new_ip_info->netmask = add_option_dup(new_opts, "netmask", buf, -1);
- break;
- case GRP_ATTR(1, 3):
- if (attrlen != 4)
- goto badlen;
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
- vpn_progress(vpninfo, PRG_DEBUG, _("Received internal gateway address %s\n"), buf);
- /* Hm, what are we supposed to do with this? It's a tunnel;
- having a gateway is meaningless. */
- add_option_dup(new_opts, "ipaddr", buf, -1);
- break;
- case GRP_ATTR(3, 3): {
- struct oc_split_include *inc;
- if (attrlen != 8)
- goto badlen;
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d/%d.%d.%d.%d",
- data[0], data[1], data[2], data[3],
- data[4], data[5], data[6], data[7]);
- vpn_progress(vpninfo, PRG_DEBUG, _("Received split include route %s\n"), buf);
- inc = malloc(sizeof(*inc));
- if (inc) {
- inc->route = add_option_dup(new_opts, "split-include", buf, -1);
- if (inc->route) {
- inc->next = new_ip_info->split_includes;
- new_ip_info->split_includes = inc;
- } else
- free(inc);
- }
- break;
- }
- case GRP_ATTR(3, 4): {
- struct oc_split_include *exc;
- if (attrlen != 8)
- goto badlen;
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d/%d.%d.%d.%d",
- data[0], data[1], data[2], data[3],
- data[4], data[5], data[6], data[7]);
- vpn_progress(vpninfo, PRG_DEBUG, _("Received split exclude route %s\n"), buf);
- exc = malloc(sizeof(*exc));
- if (exc) {
- exc->route = add_option_dup(new_opts, "split-exclude", buf, -1);
- if (exc->route) {
- exc->next = new_ip_info->split_excludes;
- new_ip_info->split_excludes = exc;
- } else
- free(exc);
- }
- break;
- }
- case GRP_ATTR(4, 1):
- if (attrlen != 4)
- goto badlen;
- snprintf(buf, sizeof(buf), "%d.%d.%d.%d", data[0], data[1], data[2], data[3]);
- vpn_progress(vpninfo, PRG_DEBUG, _("Received WINS server %s\n"), buf);
- for (i = 0; i < 3; i++) {
- if (!new_ip_info->nbns[i]) {
- new_ip_info->nbns[i] = add_option_dup(new_opts, "WINS", buf, -1);
- break;
- }
- }
- break;
- case GRP_ATTR(8, 1): {
- const char *enctype;
- if (attrlen != 1)
- goto badlen;
- if (data[0] == ENC_AES_128_CBC) {
- enctype = "AES-128";
- vpninfo->enc_key_len = 16;
- } else if (data[0] == ENC_AES_256_CBC) {
- enctype = "AES-256";
- vpninfo->enc_key_len = 32;
- } else
- enctype = "unknown";
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP encryption: 0x%02x (%s)\n"),
- data[0], enctype);
- vpninfo->esp_enc = data[0];
- break;
- }
- case GRP_ATTR(8, 2): {
- const char *mactype;
- if (attrlen != 1)
- goto badlen;
- if (data[0] == HMAC_MD5) {
- mactype = "MD5";
- vpninfo->hmac_key_len = 16;
- } else if (data[0] == HMAC_SHA1) {
- mactype = "SHA1";
- vpninfo->hmac_key_len = 20;
- } else
- mactype = "unknown";
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP HMAC: 0x%02x (%s)\n"),
- data[0], mactype);
- vpninfo->esp_hmac = data[0];
- break;
- }
- case GRP_ATTR(8, 3):
- if (attrlen != 1)
- goto badlen;
- vpninfo->esp_compr = data[0];
- vpninfo->dtls_compr = data[0] ? COMPR_LZO : 0;
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP compression: %d\n"), data[0]);
- break;
- case GRP_ATTR(8, 4):
- if (attrlen != 2)
- goto badlen;
- i = load_be16(data);
- udp_sockaddr(vpninfo, i);
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP port: %d\n"), i);
- break;
- case GRP_ATTR(8, 5):
- if (attrlen != 4)
- goto badlen;
- vpninfo->esp_lifetime_bytes = load_be32(data);
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP key lifetime: %u bytes\n"),
- vpninfo->esp_lifetime_bytes);
- break;
- case GRP_ATTR(8, 6):
- if (attrlen != 4)
- goto badlen;
- vpninfo->esp_lifetime_seconds = load_be32(data);
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP key lifetime: %u seconds\n"),
- vpninfo->esp_lifetime_seconds);
- break;
- case GRP_ATTR(8, 9):
- if (attrlen != 4)
- goto badlen;
- vpninfo->esp_ssl_fallback = load_be32(data);
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP to SSL fallback: %u seconds\n"),
- vpninfo->esp_ssl_fallback);
- break;
- case GRP_ATTR(8, 10):
- if (attrlen != 4)
- goto badlen;
- vpninfo->esp_replay_protect = load_be32(data);
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP replay protection: %d\n"),
- load_be32(data));
- break;
- case GRP_ATTR(7, 1):
- if (attrlen != 4)
- goto badlen;
- memcpy(&vpninfo->esp_out.spi, data, 4);
- vpn_progress(vpninfo, PRG_DEBUG, _("ESP SPI (outbound): %x\n"),
- load_be32(data));
- break;
- case GRP_ATTR(7, 2):
- if (attrlen != 0x40)
- goto badlen;
- /* data contains enc_key and hmac_key concatenated */
- memcpy(vpninfo->esp_out.enc_key, data, 0x40);
- vpn_progress(vpninfo, PRG_DEBUG, _("%d bytes of ESP secrets\n"),
- attrlen);
- break;
- default:
- buf[0] = 0;
- for (i=0; i < 16 && i < attrlen; i++)
- sprintf(buf + strlen(buf), " %02x", data[i]);
- if (attrlen > 16)
- sprintf(buf + strlen(buf), "...");
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Unknown TLV group %d attr %d len %d:%s\n"),
- group, attr, attrlen, buf);
- }
- return 0;
- }
- static void put_len16(struct oc_text_buf *buf, int where)
- {
- int len = buf->pos - where;
- store_be16(buf->data + where - 2, len);
- }
- static void put_len32(struct oc_text_buf *buf, int where)
- {
- int len = buf->pos - where;
- store_be32(buf->data + where - 4, len);
- }
- /* We don't know what these are so just hope they never change */
- static const unsigned char kmp_head[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
- static const unsigned char kmp_tail[] = { 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00 };
- static const unsigned char kmp_tail_out[] = { 0x01, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0x00, 0x00, 0x00 };
- static const unsigned char data_hdr[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x2c, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 };
- #ifdef HAVE_ESP
- static const unsigned char esp_kmp_hdr[] = {
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2e,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, /* KMP header */
- 0x00, 0x56, /* KMP length */
- 0x00, 0x07, 0x00, 0x00, 0x00, 0x50, /* TLV group 7 */
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, /* Attr 1 (SPI) */
- };
- /* Followed by 4 bytes of SPI */
- static const unsigned char esp_kmp_part2[] = {
- 0x00, 0x02, 0x00, 0x00, 0x00, 0x40, /* Attr 2 (secrets) */
- };
- /* And now 0x40 bytes of random secret for encryption and HMAC key */
- #endif
- static const struct pkt esp_enable_pkt = {
- .next = NULL,
- { .oncp = { .rec = { 0x21, 0x00 },
- .kmp = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x2f,
- 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x0d } }
- },
- .data = {
- 0x00, 0x06, 0x00, 0x00, 0x00, 0x07, /* Group 6, len 7 */
- 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, /* Attr 1, len 1 */
- 0x01
- },
- .len = 13
- };
- static int queue_esp_control(struct openconnect_info *vpninfo, int enable)
- {
- struct pkt *new = alloc_pkt(vpninfo, esp_enable_pkt.len);
- if (!new)
- return -ENOMEM;
- new->oncp = esp_enable_pkt.oncp;
- new->len = esp_enable_pkt.len;
- memcpy(new->data, esp_enable_pkt.data, esp_enable_pkt.len);
- new->data[12] = enable;
- queue_packet(&vpninfo->tcp_control_queue, new);
- return 0;
- }
- static int check_kmp_header(struct openconnect_info *vpninfo, unsigned char *bytes, int pktlen)
- {
- if (pktlen < 20 || memcmp(bytes, kmp_head, sizeof(kmp_head)) ||
- memcmp(bytes + 8, kmp_tail, sizeof(kmp_tail))) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to parse KMP header\n"));
- return -EINVAL;
- }
- return load_be16(bytes + 6);
- }
- static int parse_conf_pkt(struct openconnect_info *vpninfo, unsigned char *bytes, int pktlen, int kmp)
- {
- int kmplen, kmpend, grouplen, groupend, group, attr, attrlen;
- int ofs = 0;
- int split_enc_hmac_keys = 0;
- struct oc_vpn_option *new_opts = NULL;
- struct oc_ip_info new_ip_info = {};
- kmplen = load_be16(bytes + ofs + 18);
- kmpend = ofs + kmplen;
- if (kmpend > pktlen) {
- eparse:
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to parse KMP message\n"));
- dump_buf_hex(vpninfo, PRG_ERR, '<', bytes, pktlen);
- einval:
- free_optlist(new_opts);
- free_split_routes(&new_ip_info);
- return -EINVAL;
- }
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Got KMP message %d of size %d\n"),
- kmp, kmplen);
- ofs += 0x14;
- while (ofs < kmpend) {
- if (ofs + 6 > kmpend)
- goto eparse;
- group = load_be16(bytes + ofs);
- grouplen = load_be32(bytes + ofs + 2);
- ofs += 6;
- groupend = ofs + grouplen;
- if (groupend > pktlen)
- goto eparse;
- if (kmp == 302 && group != 7 && group != 8) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Received non-ESP TLVs (group %d) in ESP negotiation KMP\n"),
- group);
- goto einval;
- }
- while (ofs < groupend) {
- if (ofs + 6 > groupend)
- goto eparse;
- attr = load_be16(bytes + ofs);
- attrlen = load_be32(bytes + ofs + 2);
- ofs += 6;
- if (attrlen + ofs > groupend)
- goto eparse;
- if (process_attr(vpninfo, &new_opts, &new_ip_info,
- group, attr, bytes + ofs, attrlen))
- goto eparse;
- if (GRP_ATTR(group, attr)==GRP_ATTR(7, 2))
- split_enc_hmac_keys = 1;
- ofs += attrlen;
- }
- }
- /* The encryption and HMAC keys are sent concatenated together in a block of 0x40 bytes;
- we can't split them apart until we know how long the encryption key is. */
- if (split_enc_hmac_keys)
- memcpy(vpninfo->esp_out.hmac_key, vpninfo->esp_out.enc_key + vpninfo->enc_key_len, vpninfo->hmac_key_len);
- int ret = install_vpn_opts(vpninfo, new_opts, &new_ip_info);
- if (ret) {
- free_optlist(new_opts);
- free_split_routes(&new_ip_info);
- return ret;
- }
- return 0;
- }
- int oncp_connect(struct openconnect_info *vpninfo)
- {
- int ret, len, kmp, kmplen, group, check_len;
- struct oc_text_buf *reqbuf;
- unsigned char bytes[65536];
- if (!vpninfo->cookies) {
- /* XX: This will happen if authentication was separate/external */
- ret = internal_split_cookies(vpninfo, 0, "DSID");
- if (ret)
- return ret;
- }
- ret = openconnect_open_https(vpninfo);
- if (ret)
- return ret;
- reqbuf = buf_alloc();
- buf_append(reqbuf, "POST /dana/js?prot=1&svc=4 HTTP/1.1\r\n");
- /* The TLS socket actually remains open for use by the oNCP
- tunnel, but the "Connection: close" header is nevertheless
- required here. It appears to signal to the server to stop
- treating this as an HTTP connection and to start treating
- it as an oNCP connection.
- */
- buf_append(reqbuf, "Connection: close\r\n");
- oncp_common_headers(vpninfo, reqbuf);
- buf_append(reqbuf, "Content-Length: 256\r\n");
- buf_append(reqbuf, "\r\n");
- if (buf_error(reqbuf)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error creating oNCP negotiation request\n"));
- ret = buf_error(reqbuf);
- goto out;
- }
- if (vpninfo->dump_http_traffic)
- dump_buf(vpninfo, '>', reqbuf->data);
- ret = vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos);
- if (ret < 0)
- goto out;
- ret = process_http_response(vpninfo, 1, NULL, reqbuf);
- if (ret < 0)
- goto out;
- if (ret != 200) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Unexpected %d result from server\n"),
- ret);
- ret = ((ret >= 400 && ret <= 499) || ret == 302) ? -EPERM : -EINVAL;
- goto out;
- }
- /* This is probably some kind of vestigial authentication packet, although
- * it's mostly obsolete now that the authentication is really done over
- * HTTP. We only send the hostname. */
- buf_truncate(reqbuf);
- buf_append_le16(reqbuf, sizeof(authpkt_head) + 2 +
- strlen(vpninfo->localname) + sizeof(authpkt_tail));
- buf_append_bytes(reqbuf, authpkt_head, sizeof(authpkt_head));
- buf_append_le16(reqbuf, strlen(vpninfo->localname));
- buf_append(reqbuf, "%s", vpninfo->localname);
- buf_append_bytes(reqbuf, authpkt_tail, sizeof(authpkt_tail));
- if (buf_error(reqbuf)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error creating oNCP negotiation request\n"));
- ret = buf_error(reqbuf);
- goto out;
- }
- dump_buf_hex(vpninfo, PRG_DEBUG, '>', (void *)reqbuf->data, reqbuf->pos);
- ret = vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos);
- if (ret != reqbuf->pos) {
- if (ret >= 0) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Short write in oNCP negotiation\n"));
- ret = -EIO;
- }
- goto out;
- }
- /* Now we expect a three-byte response with what's presumably an
- error code */
- ret = vpninfo->ssl_read(vpninfo, (void *)bytes, 3);
- check_len = load_le16(bytes);
- if (ret < 0)
- goto out;
- vpn_progress(vpninfo, PRG_TRACE,
- _("Read %d bytes of SSL record\n"), ret);
- dump_buf_hex(vpninfo, PRG_TRACE, '<', (void *)bytes, ret);
- if (ret != 3 || check_len < 1) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Unexpected response of size %d after hostname packet\n"),
- ret);
- ret = -EINVAL;
- goto out;
- }
- if (bytes[2]) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Server response to hostname packet is error 0x%02x\n"),
- bytes[2]);
- if (bytes[2] == 0x08)
- vpn_progress(vpninfo, PRG_ERR,
- _("This seems to indicate that the server has disabled support for\n"
- "Juniper's older oNCP protocol, and only allows connections using\n"
- "the newer Junos Pulse protocol. This version of OpenConnect has\n"
- "EXPERIMENTAL support for Pulse using --prot=pulse\n"));
- ret = -EINVAL;
- goto out;
- }
- /* And then a KMP message 301 with the IP configuration.
- * Sometimes this arrives as a separate SSL record (with its own
- * 2-byte length prefix), and sometimes concatenated with the
- * previous 3-byte response).
- */
- if (check_len == 1) {
- len = vpninfo->ssl_read(vpninfo, (void *)bytes, sizeof(bytes));
- check_len = load_le16(bytes);
- } else {
- len = vpninfo->ssl_read(vpninfo, (void *)(bytes+2), sizeof(bytes)-2) + 2;
- check_len--;
- }
- if (len < 0) {
- ret = len;
- goto out;
- }
- vpn_progress(vpninfo, PRG_TRACE,
- _("Read %d bytes of SSL record\n"), len);
- if (len < 0x16 || check_len + 2 != len) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Invalid packet waiting for KMP 301\n"));
- dump_buf_hex(vpninfo, PRG_ERR, '<', bytes, len);
- ret = -EINVAL;
- goto out;
- }
- ret = check_kmp_header(vpninfo, bytes + 2, len);
- if (ret < 0)
- goto out;
- /* We expect KMP message 301 here */
- if (ret != 301) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Expected KMP message 301 from server but got %d\n"),
- ret);
- ret = -EINVAL;
- goto out;
- }
- kmplen = load_be16(bytes + 20);
- if (kmplen + 2 >= sizeof(bytes)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("KMP message 301 from server too large (%d bytes)\n"),
- kmplen);
- ret = -EINVAL;
- goto out;
- }
- vpn_progress(vpninfo, PRG_TRACE,
- _("Got KMP message 301 of length %d\n"), kmplen);
- while (kmplen + 22 > len) {
- char l[2];
- int thislen;
- if (vpninfo->ssl_read(vpninfo, (void *)l, 2) != 2) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to read continuation record length\n"));
- ret = -EINVAL;
- goto out;
- }
- if (load_le16(l) + len > kmplen + 22) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Record of additional %d bytes too large; would make %d\n"),
- load_le16(l), len + load_le16(l));
- ret = -EINVAL;
- goto out;
- }
- thislen = vpninfo->ssl_read(vpninfo, (void *)(bytes + len), load_le16(l));
- if (thislen != load_le16(l)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Failed to read continuation record of length %d\n"),
- load_le16(l));
- ret = -EINVAL;
- goto out;
- }
- vpn_progress(vpninfo, PRG_TRACE,
- _("Read additional %d bytes of KMP 301 message\n"),
- thislen);
- len += thislen;
- }
- ret = parse_conf_pkt(vpninfo, bytes + 2, len - 2, ret);
- if (ret)
- goto out;
- buf_truncate(reqbuf);
- buf_append_le16(reqbuf, 0); /* Length. We'll fix it later. */
- buf_append_bytes(reqbuf, kmp_head, sizeof(kmp_head));
- buf_append_be16(reqbuf, 303); /* KMP message 303 */
- buf_append_bytes(reqbuf, kmp_tail_out, sizeof(kmp_tail_out));
- buf_append_be16(reqbuf, 0); /* KMP message length */
- kmp = reqbuf->pos;
- buf_append_tlv(reqbuf, 6, 0, NULL); /* TLV group 6 */
- group = reqbuf->pos;
- buf_append_tlv_be32(reqbuf, 2, vpninfo->ip_info.mtu);
- if (buf_error(reqbuf)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error creating oNCP negotiation request\n"));
- ret = buf_error(reqbuf);
- goto out;
- }
- put_len32(reqbuf, group);
- put_len16(reqbuf, kmp);
- #ifdef HAVE_ESP
- if (!openconnect_setup_esp_keys(vpninfo, 1)) {
- struct esp *esp = &vpninfo->esp_in[vpninfo->current_esp_in];
- /* Since we'll want to do this in the oncp_mainloop too, where it's easier
- * *not* to have an oc_text_buf and build it up manually, and since it's
- * all fixed size and fairly simple anyway, just hard-code the packet */
- buf_append_bytes(reqbuf, esp_kmp_hdr, sizeof(esp_kmp_hdr));
- buf_append_bytes(reqbuf, &esp->spi, sizeof(esp->spi));
- buf_append_bytes(reqbuf, esp_kmp_part2, sizeof(esp_kmp_part2));
- buf_append_bytes(reqbuf, &esp->enc_key, vpninfo->enc_key_len);
- buf_append_bytes(reqbuf, &esp->hmac_key, 0x40 - vpninfo->enc_key_len);
- if (buf_error(reqbuf)) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Error negotiating ESP keys\n"));
- ret = buf_error(reqbuf);
- goto out;
- }
- }
- #endif
- /* Length at the start of the packet is little-endian */
- store_le16(reqbuf->data, reqbuf->pos - 2);
- vpn_progress(vpninfo, PRG_DEBUG, _("oNCP negotiation request outgoing:\n"));
- dump_buf_hex(vpninfo, PRG_DEBUG, '>', (void *)reqbuf->data, reqbuf->pos);
- ret = vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos);
- if (ret != reqbuf->pos) {
- if (ret >= 0) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Short write in oNCP negotiation\n"));
- ret = -EIO;
- }
- goto out;
- }
- ret = 0;
- out:
- if (ret)
- openconnect_close_https(vpninfo, 0);
- else {
- monitor_fd_new(vpninfo, ssl);
- monitor_read_fd(vpninfo, ssl);
- monitor_except_fd(vpninfo, ssl);
- }
- buf_free(reqbuf);
- vpninfo->partial_rec_size = 0;
- free_pkt(vpninfo, vpninfo->cstp_pkt);
- vpninfo->cstp_pkt = NULL;
- return ret;
- }
- static int oncp_receive_espkeys(struct openconnect_info *vpninfo, int len)
- {
- #ifdef HAVE_ESP
- int ret;
- ret = parse_conf_pkt(vpninfo, vpninfo->cstp_pkt->oncp.kmp, len + 20, 301);
- if (!ret)
- ret = openconnect_setup_esp_keys(vpninfo, 1);
- if (!ret) {
- struct esp *esp = &vpninfo->esp_in[vpninfo->current_esp_in];
- unsigned char *p = vpninfo->cstp_pkt->oncp.kmp;
- memcpy(p, esp_kmp_hdr, sizeof(esp_kmp_hdr));
- p += sizeof(esp_kmp_hdr);
- memcpy(p, &esp->spi, sizeof(esp->spi));
- p += sizeof(esp->spi);
- memcpy(p, esp_kmp_part2, sizeof(esp_kmp_part2));
- p += sizeof(esp_kmp_part2);
- memcpy(p, esp->enc_key, vpninfo->enc_key_len);
- memcpy(p+vpninfo->enc_key_len, esp->hmac_key, 0x40 - vpninfo->enc_key_len);
- p += 0x40;
- vpninfo->cstp_pkt->len = p - vpninfo->cstp_pkt->data;
- store_le16(vpninfo->cstp_pkt->oncp.rec,
- (p - vpninfo->cstp_pkt->oncp.kmp));
- queue_packet(&vpninfo->tcp_control_queue, vpninfo->cstp_pkt);
- vpninfo->cstp_pkt = NULL;
- print_esp_keys(vpninfo, _("new incoming"), esp);
- print_esp_keys(vpninfo, _("new outgoing"), &vpninfo->esp_out);
- }
- return ret;
- #else
- vpn_progress(vpninfo, PRG_DEBUG,
- _("Ignoring ESP keys since ESP support not available in this build\n"));
- return 0;
- #endif
- }
- static int oncp_record_read(struct openconnect_info *vpninfo, void *buf, int len)
- {
- int ret;
- if (!vpninfo->partial_rec_size) {
- unsigned char lenbuf[2];
- ret = ssl_nonblock_read(vpninfo, 0, lenbuf, 2);
- if (ret <= 0)
- return ret;
- if (ret == 1) {
- /* Surely at least *this* never happens? The two length bytes
- * of the oNCP record being split across multiple SSL records */
- vpn_progress(vpninfo, PRG_ERR,
- _("Read only 1 byte of oNCP length field\n"));
- return -EIO;
- }
- vpninfo->partial_rec_size = load_le16(lenbuf);
- if (!vpninfo->partial_rec_size) {
- ret = ssl_nonblock_read(vpninfo, 0, lenbuf, 1);
- if (ret == 1) {
- if (lenbuf[0] == 1) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Server terminated connection (session expired)\n"));
- vpninfo->quit_reason = "VPN session expired";
- } else if (lenbuf[0] == 8) {
- vpn_progress(vpninfo, PRG_ERR,
- _("Server terminated connection (idle timeout)\n"));
- vpninfo->quit_reason = "Idle timeout";
- } else {
- vpn_progress(vpninfo, PRG_ERR,
- _("Server terminated connection (reason: %d)\n"),
- lenbuf[0]);
- vpninfo->quit_reason = "Server terminated connection";
- }
- return -EPIPE;
- } else {
- vpn_progress(vpninfo, PRG_ERR,
- _("Server sent zero-length oNCP record\n"));
- vpninfo->quit_reason = "Zero-length oNCP record";
- return -EIO;
- }
- }
- }
- if (len > vpninfo->partial_rec_size)
- len = vpninfo->partial_rec_size;
- ret = ssl_nonblock_read(vpninfo, 0, buf, len);
- if (ret > 0)
- vpninfo->partial_rec_size -= ret;
- return ret;
- }
- int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
- {
- int ret;
- int work_done = 0;
- /* Periodic TNCC */
- if (trojan_check_deadline(vpninfo, timeout)) {
- oncp_send_tncc_command(vpninfo, 0);
- return 1;
- }
- if (vpninfo->ssl_fd == -1)
- goto do_reconnect;
- /* FIXME: The poll() handling here is fairly simplistic. Actually,
- if the SSL connection stalls it could return a WANT_WRITE error
- on _either_ of the SSL_read() or SSL_write() calls. In that case,
- we should probably remove POLLIN from the events we're looking for,
- and add POLLOUT. As it is, though, it'll just chew CPU time in that
- fairly unlikely situation, until the write backlog clears. */
- while (readable) {
- int len, kmp, kmplen, iplen;
- /* Some servers send us packets that are larger than
- negotiated MTU. We reserve some extra space to
- handle that */
- int receive_mtu = MAX(16384, vpninfo->ip_info.mtu);
- len = receive_mtu + vpninfo->pkt_trailer;
- if (!vpninfo->cstp_pkt) {
- vpninfo->cstp_pkt = alloc_pkt(vpninfo, len);
- if (!vpninfo->cstp_pkt) {
- vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n"));
- break;
- }
- vpninfo->cstp_pkt->len = 0;
- }
- /*
- * This protocol is horrid. There are encapsulations within
- * encapsulations within encapsulations. Some of them entirely
- * gratuitous.
- *
- * First there's the SSL records which are a natural part of
- * using TLS as a transport. They appear to make no use of the
- * packetisation which these provide.
- *
- * Then within the TLS data stream there are "records" preceded
- * by a 16-bit little-endian length. It's not clear what these
- * records represent; they appear to be entirely gratuitous and
- * just need to be discarded. A record boundary sometimes falls
- * right in the middle of a data packet; there's no apparent
- * logic to it.
- *
- * Then there are the KMP packets themselves, each of which has
- * a length field of its own. There can be multiple KMP packets
- * in each of the above-mention "records", and as noted there
- * even be *partial* KMP packets in each record.
- *
- * Finally, a KMP data packet may actually contain multiple IP
- * packets, which need to be split apart by using the length
- * field in the IP header. This is Legacy IP only, never IPv6
- * for the Network Connect protocol.
- */
- /* Until we pass it up the stack, we use cstp_pkt->len to show
- * the amount of data received *including* the KMP header. */
- len = oncp_record_read(vpninfo,
- vpninfo->cstp_pkt->oncp.kmp + vpninfo->cstp_pkt->len,
- receive_mtu + 20 - vpninfo->cstp_pkt->len);
- if (!len)
- break;
- else if (len < 0) {
- if (vpninfo->quit_reason)
- return len;
- goto do_reconnect;
- }
- vpninfo->cstp_pkt->len += len;
- vpninfo->ssl_times.last_rx = time(NULL);
- if (vpninfo->cstp_pkt->len < 20)
- continue;
- next_kmp:
- /* Now we have a KMP header. It might already have been there */
- kmp = load_be16(vpninfo->cstp_pkt->oncp.kmp + 6);
- kmplen = load_be16(vpninfo->cstp_pkt->oncp.kmp + 18);
- if (len == vpninfo->cstp_pkt->len)
- vpn_progress(vpninfo, PRG_DEBUG, _("Incoming KMP message %d of size %d (got %d)\n"),
- kmp, kmplen, vpninfo->cstp_pkt->len - 20);
- else
- vpn_progress(vpninfo, PRG_DEBUG, _("Continuing to process KMP message %d now size %d (got %d)\n"),
- kmp, kmplen, vpninfo->cstp_pkt->len - 20);
- switch (kmp) {
- case 300:
- next_ip:
- /* Need at least 6 bytes of payload to check the IP packet length */
- if (vpninfo->cstp_pkt->len < 26)
- continue;
- switch(vpninfo->cstp_pkt->data[0] >> 4) {
- case 4:
- iplen = load_be16(vpninfo->cstp_pkt->data + 2);
- break;
- case 6:
- iplen = load_be16(vpninfo->cstp_pkt->data + 4) + 40;
- break;
- default:
- badiplen:
- vpn_progress(vpninfo, PRG_ERR,
- _("Unrecognised data packet\n"));
- goto unknown_pkt;
- }
- if (!iplen || iplen > receive_mtu || iplen > kmplen)
- goto badiplen;
- if (iplen > vpninfo->cstp_pkt->len - 20)
- continue;
- work_done = 1;
- vpn_progress(vpninfo, PRG_TRACE,
- _("Received uncompressed data packet of %d bytes\n"),
- iplen);
- /* If there's nothing after the IP packet, and it's the last (or
- * only) packet in this KMP300 so we don't need to keep the KMP
- * header either, then just queue it. */
- if (iplen == kmplen && iplen == vpninfo->cstp_pkt->len - 20) {
- vpninfo->cstp_pkt->len = iplen;
- queue_packet(&vpninfo->incoming_queue, vpninfo->cstp_pkt);
- vpninfo->cstp_pkt = NULL;
- continue;
- }
- /* OK, we have a whole packet, and we have stuff after it */
- queue_new_packet(vpninfo, &vpninfo->incoming_queue,
- vpninfo->cstp_pkt->data, iplen);
- kmplen -= iplen;
- if (kmplen) {
- /* Still data packets to come in this KMP300 */
- store_be16(vpninfo->cstp_pkt->oncp.kmp + 18, kmplen);
- vpninfo->cstp_pkt->len -= iplen;
- if (vpninfo->cstp_pkt->len > 20)
- memmove(vpninfo->cstp_pkt->data,
- vpninfo->cstp_pkt->data + iplen,
- vpninfo->cstp_pkt->len - 20);
- goto next_ip;
- }
- /* We have depleted the KMP300, and there are more bytes from
- * the next KMP message in the buffer. Move it up and process it */
- memmove(vpninfo->cstp_pkt->oncp.kmp,
- vpninfo->cstp_pkt->data + iplen,
- vpninfo->cstp_pkt->len - iplen - 20);
- vpninfo->cstp_pkt->len -= (iplen + 20);
- goto next_kmp;
- case 302:
- /* Should never happen; if it does we'll have to cope */
- if (kmplen > receive_mtu)
- goto unknown_pkt;
- /* Probably never happens. We need it in its own record.
- * If I fix oncp_receive_espkeys() not to reuse cstp_pkt
- * we can stop doing this. */
- if (vpninfo->cstp_pkt->len != kmplen + 20)
- goto unknown_pkt;
- ret = oncp_receive_espkeys(vpninfo, kmplen);
- if (ret) {
- vpn_progress(vpninfo, PRG_ERR, _("Failed to set up ESP: %s\n"),
- strerror(-ret));
- oncp_esp_close(vpninfo);
- }
- work_done = 1;
- break;
- default:
- unknown_pkt:
- vpn_progress(vpninfo, PRG_ERR,
- _("Unknown KMP message %d of size %d:\n"), kmp, kmplen);
- dump_buf_hex(vpninfo, PRG_ERR, '<', vpninfo->cstp_pkt->oncp.kmp,
- vpninfo->cstp_pkt->len);
- if (kmplen + 20 != vpninfo->cstp_pkt->len)
- vpn_progress(vpninfo, PRG_DEBUG,
- _(".... + %d more bytes unreceived\n"),
- kmplen + 20 - vpninfo->cstp_pkt->len);
- vpninfo->quit_reason = "Unknown packet received";
- return 1;
- }
- }
- /* If SSL_write() fails we are expected to try again. With exactly
- the same data, at exactly the same location. So we keep the
- packet we had before.... */
- if (vpninfo->current_ssl_pkt) {
- handle_outgoing:
- vpninfo->ssl_times.last_tx = time(NULL);
- unmonitor_write_fd(vpninfo, ssl);
- vpn_progress(vpninfo, PRG_TRACE, _("Packet outgoing:\n"));
- dump_buf_hex(vpninfo, PRG_TRACE, '>',
- vpninfo->current_ssl_pkt->oncp.rec,
- vpninfo->current_ssl_pkt->len + 22);
- ret = ssl_nonblock_write(vpninfo, 0,
- vpninfo->current_ssl_pkt->oncp.rec,
- vpninfo->current_ssl_pkt->len + 22);
- if (ret < 0) {
- do_reconnect:
- /* XXX: Do we have to do this or can we leave it open?
- * Perhaps we could even reconnect asynchronously while
- * the ESP is still running? */
- #ifdef HAVE_ESP
- esp_shutdown(vpninfo);
- #endif
- ret = ssl_reconnect(vpninfo);
- if (ret) {
- vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n"));
- vpninfo->quit_reason = "oNCP reconnect failed";
- return ret;
- }
- vpninfo->dtls_need_reconnect = 1;
- return 1;
- } else if (!ret) {
- #if 0 /* Not for Juniper yet */
- /* -EAGAIN: ssl_nonblock_write() will have added the SSL
- fd to ->select_wfds if appropriate, so we can just
- return and wait. Unless it's been stalled for so long
- that DPD kicks in and we kill the connection. */
- switch (ka_stalled_action(&vpninfo->ssl_times, timeout)) {
- case KA_DPD_DEAD:
- goto peer_dead;
- case KA_REKEY:
- goto do_rekey;
- case KA_NONE:
- return work_done;
- default:
- /* This should never happen */
- break;
- }
- #else
- return work_done;
- #endif
- }
- if (ret != vpninfo->current_ssl_pkt->len + 22) {
- vpn_progress(vpninfo, PRG_ERR,
- _("SSL wrote too few bytes! Asked for %d, sent %d\n"),
- vpninfo->current_ssl_pkt->len + 22, ret);
- vpninfo->quit_reason = "Internal error";
- return 1;
- }
- /* Don't free the 'special' packets */
- if (vpninfo->current_ssl_pkt == vpninfo->deflate_pkt) {
- free_pkt(vpninfo, 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
- *sent* over the TCP channel. */
- vpn_progress(vpninfo, PRG_TRACE,
- _("Sent ESP enable control packet\n"));
- vpninfo->dtls_state = DTLS_ESTABLISHED;
- work_done = 1;
- } else {
- free_pkt(vpninfo, vpninfo->current_ssl_pkt);
- }
- vpninfo->current_ssl_pkt = NULL;
- }
- #if 0 /* Not understood for Juniper yet */
- if (vpninfo->owe_ssl_dpd_response) {
- vpninfo->owe_ssl_dpd_response = 0;
- vpninfo->current_ssl_pkt = (struct pkt *)&dpd_resp_pkt;
- goto handle_outgoing;
- }
- switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
- case KA_REKEY:
- do_rekey:
- /* Not that this will ever happen; we don't even process
- the setting when we're asked for it. */
- vpn_progress(vpninfo, PRG_INFO, _("CSTP rekey due\n"));
- if (vpninfo->ssl_times.rekey_method == REKEY_TUNNEL)
- goto do_reconnect;
- else if (vpninfo->ssl_times.rekey_method == REKEY_SSL) {
- ret = cstp_handshake(vpninfo, 0);
- if (ret) {
- /* if we failed rehandshake try establishing a new-tunnel instead of failing */
- vpn_progress(vpninfo, PRG_ERR, _("Rehandshake failed; attempting new-tunnel\n"));
- goto do_reconnect;
- }
- goto do_dtls_reconnect;
- }
- break;
- case KA_DPD_DEAD:
- peer_dead:
- vpn_progress(vpninfo, PRG_ERR,
- _("CSTP Dead Peer Detection detected dead peer!\n"));
- do_reconnect:
- ret = cstp_reconnect(vpninfo);
- if (ret) {
- vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n"));
- vpninfo->quit_reason = "CSTP reconnect failed";
- return ret;
- }
- do_dtls_reconnect:
- /* succeeded, let's rekey DTLS, if it is not rekeying
- * itself. */
- if (vpninfo->dtls_state > DTLS_SLEEPING &&
- vpninfo->dtls_times.rekey_method == REKEY_NONE) {
- vpninfo->dtls_need_reconnect = 1;
- }
- return 1;
- case KA_DPD:
- vpn_progress(vpninfo, PRG_DEBUG, _("Send CSTP DPD\n"));
- vpninfo->current_ssl_pkt = (struct pkt *)&dpd_pkt;
- goto handle_outgoing;
- case KA_KEEPALIVE:
- /* No need to send an explicit keepalive
- if we have real data to send */
- if (vpninfo->dtls_state != DTLS_ESTABLISHED && vpninfo->outgoing_queue)
- break;
- vpn_progress(vpninfo, PRG_DEBUG, _("Send CSTP Keepalive\n"));
- vpninfo->current_ssl_pkt = (struct pkt *)&keepalive_pkt;
- goto handle_outgoing;
- case KA_NONE:
- ;
- }
- #endif
- /* Queue the ESP enable message. We will start sending packets
- * via ESP once the enable message has been *sent* over the
- * TCP channel. Assign it directly to current_ssl_pkt so that
- * we can use it in-place and match against it above. */
- if (vpninfo->dtls_state == DTLS_CONNECTED) {
- vpninfo->current_ssl_pkt = (struct pkt *)&esp_enable_pkt;
- goto handle_outgoing;
- }
- vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->tcp_control_queue);
- if (vpninfo->current_ssl_pkt)
- goto handle_outgoing;
- /* Service outgoing packet queue, if no DTLS */
- while (vpninfo->dtls_state != DTLS_ESTABLISHED &&
- (vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) {
- struct pkt *this = vpninfo->current_ssl_pkt;
- /* Little-endian overall record length */
- store_le16(this->oncp.rec, (this->len + 20));
- memcpy(this->oncp.kmp, data_hdr, 18);
- /* Big-endian length in KMP message header */
- store_be16(this->oncp.kmp + 18, this->len);
- vpn_progress(vpninfo, PRG_TRACE,
- _("Sending uncompressed data packet of %d bytes\n"),
- this->len);
- goto handle_outgoing;
- }
- /* Work is not done if we just got rid of packets off the queue */
- return work_done;
- }
- int oncp_bye(struct openconnect_info *vpninfo, const char *reason)
- {
- char *orig_path;
- char *res_buf=NULL;
- int ret;
- /* We need to close and reopen the HTTPS connection (to kill
- * the oncp tunnel) and submit a new HTTPS request to logout.
- */
- openconnect_close_https(vpninfo, 0);
- orig_path = vpninfo->urlpath;
- vpninfo->urlpath = strdup("dana-na/auth/logout.cgi"); /* redirect segfaults without strdup */
- ret = do_https_request(vpninfo, "GET", NULL, NULL, &res_buf, NULL, HTTP_NO_FLAGS);
- free(vpninfo->urlpath);
- vpninfo->urlpath = orig_path;
- if (ret < 0)
- vpn_progress(vpninfo, PRG_ERR, _("Logout failed.\n"));
- else
- vpn_progress(vpninfo, PRG_INFO, _("Logout successful.\n"));
- free(res_buf);
- return ret;
- }
- #ifdef HAVE_ESP
- void oncp_esp_close(struct openconnect_info *vpninfo)
- {
- /* Tell server to stop sending on ESP channel */
- if (vpninfo->dtls_state >= DTLS_CONNECTED)
- queue_esp_control(vpninfo, 0);
- esp_close(vpninfo);
- }
- int oncp_esp_send_probes(struct openconnect_info *vpninfo)
- {
- struct pkt *pkt;
- int pktlen, seq;
- if (vpninfo->dtls_fd == -1) {
- int fd = udp_connect(vpninfo);
- if (fd < 0)
- return fd;
- /* We are not connected until we get an ESP packet back */
- vpninfo->dtls_state = DTLS_SLEEPING;
- vpninfo->dtls_fd = fd;
- monitor_fd_new(vpninfo, dtls);
- monitor_read_fd(vpninfo, dtls);
- monitor_except_fd(vpninfo, dtls);
- }
- pkt = alloc_pkt(vpninfo, 1 + vpninfo->pkt_trailer);
- if (!pkt)
- return -ENOMEM;
- for (seq=1; seq <= (vpninfo->dtls_state==DTLS_ESTABLISHED ? 1 : 2); seq++) {
- pkt->len = 1;
- pkt->data[0] = 0;
- pktlen = construct_esp_packet(vpninfo, pkt,
- vpninfo->dtls_addr->sa_family == AF_INET6 ? IPPROTO_IPV6 : IPPROTO_IPIP);
- if (pktlen < 0 ||
- send(vpninfo->dtls_fd, (void *)&pkt->esp, pktlen, 0) < 0)
- vpn_progress(vpninfo, PRG_DEBUG, _("Failed to send ESP probe\n"));
- }
- free_pkt(vpninfo, pkt);
- vpninfo->dtls_times.last_tx = time(&vpninfo->new_dtls_started);
- return 0;
- };
- int oncp_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt)
- {
- return (pkt->len == 1 && pkt->data[0] == 0);
- }
- #endif /* HAVE_ESP */
|