LCP works, no error handling yet.
Signed-off-by: Andreas Gnau <rondom@rondom.de>
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_nx = nx.c
lib_srcs_pulse = pulse.c
lib_srcs_f5 = f5.c
lib_srcs_ppp = ppp.c ppp.h
library_srcs += $(lib_srcs_juniper) $(lib_srcs_cisco) $(lib_srcs_oath) \
$(lib_srcs_globalprotect) $(lib_srcs_pulse) $(lib_srcs_f5) \
- $(lib_srcs_ppp) $(lib_srcs_fortinet) $(lib_srcs_oidc)
+ $(lib_srcs_ppp) $(lib_srcs_fortinet) $(lib_srcs_nx) \
+ $(lib_srcs_oidc)
+
lib_srcs_gnutls = gnutls.c gnutls_tpm.c gnutls_tpm2.c
lib_srcs_openssl = openssl.c openssl-pkcs11.c
.udp_catch_probe = gpst_esp_catch_probe,
#endif
}, {
+ .name = "nx",
+ .pretty_name = N_("SonicWall NetExtender"),
+ .description = N_("Compatible with SonicWall NetExtender SSL VPN"),
+ .flags = OC_PROTO_PROXY,
+ .vpn_close_session = nx_bye,
+ .tcp_connect = nx_connect,
+ .tcp_mainloop = ppp_mainloop,
+ .add_http_headers = nx_common_headers,
+ .obtain_cookie = nx_obtain_cookie,
+ }, {
.name = "pulse",
.pretty_name = N_("Pulse Connect Secure"),
.description = N_("Compatible with Pulse Connect Secure SSL VPN"),
--- /dev/null
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2020 Andreas Gnau
+ *
+ * Author: Andreas Gnau <rondom@rondom.de>
+ *
+ * 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.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+
+#include "openconnect-internal.h"
+
+int nx_obtain_cookie(struct openconnect_info *vpninfo)
+{
+ vpn_progress(
+ vpninfo, PRG_ERR,
+ _("Authentication for Net Extender not implemented yet.\n"));
+ return -EINVAL;
+}
+
+void nx_common_headers(struct openconnect_info *vpninfo,
+ struct oc_text_buf *buf)
+{
+ http_common_headers(vpninfo, buf);
+ dump_buf(vpninfo, PRG_ERR, buf->data); // TODO: XXX
+ // TODO: Is this the place to manipulate user agent (NX requires the UA to contain netextender)
+}
+
+int nx_connect(struct openconnect_info *vpninfo)
+{
+ int ret = -EINVAL;
+ struct oc_text_buf *reqbuf = NULL;
+ char *auth_token = NULL;
+ int auth_token_len = -1;
+ int ipv4 = 1; // TODO: get from info
+ int ipv6 = 0;
+
+ // TODO: check for correct swap-cookie
+ if (!vpninfo->cookie) {
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Malformed cookie or no cookie given\n"));
+ return -EINVAL;
+ }
+ // TODO: get auth_token and other info from /cgi-bin/sslvpnclient?launchplatform=mac&neProto=3&supportipv6=yes
+ auth_token = openconnect_base64_decode(&auth_token_len, vpninfo->cookie);
+ if (!auth_token)
+ return auth_token_len;
+ // TODO: get ECP (trojan) info from /cgi-bin/sslvpnclient?epcversionquery=nxx
+ ret = openconnect_open_https(vpninfo);
+ if (ret)
+ return ret;
+
+ reqbuf = buf_alloc();
+ if (!reqbuf)
+ return -errno;
+
+ buf_append(reqbuf, "CONNECT localhost:0 HTTP/1.0\r\n");
+ buf_append(reqbuf, "X-SSLVPN-PROTOCOL: 2.0\r\n");
+ buf_append(reqbuf, "X-SSLVPN-SERVICE: NETEXTENDER\r\n");
+ buf_append(reqbuf, "Connection-Medium: MacOS\r\n");
+ buf_append(reqbuf, "Frame-Encode: off\r\n");
+ buf_append(reqbuf, "X-NE-PROTOCOL: 2.0\r\n");
+ buf_append(reqbuf, "Proxy-Authorization: %.*s\r\n", auth_token_len,
+ auth_token);
+ // TODO: use set string for nx in openconnect_set_reported_os
+ buf_append(reqbuf, "X-NX-Client-Platform: Linux\r\n");
+ buf_append(reqbuf, "User-Agent: %s\r\n", vpninfo->useragent);
+ buf_append(reqbuf, "\r\n");
+ if ((ret = buf_error(reqbuf) != 0)) {
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Error creating HTTPS CONNECT request\n"));
+ goto out;
+ }
+ if (vpninfo->dump_http_traffic)
+ dump_buf(vpninfo, '>', reqbuf->data);
+ vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos);
+
+ // In case of success, there won't be a HTTP 200, data will start straight away
+ // TODO: refactor process_http_response to handle this, so we can use it and do proper error handling
+ // We expect either a HTTP response (failure) or a size (BE, 4b) (success).
+ // The size will be smaller than 0x01000000 for sure, so we can use the
+ // first byte as an indicator of success and don't need to check for "HTTP"
+ // TODO: actually handle errors as described above
+ vpn_progress(vpninfo, PRG_DEBUG, _("Connection established\n"));
+ vpninfo->ppp = openconnect_ppp_new(PPP_ENCAP_NX_HDLC, ipv4, ipv6);
+ if (!vpninfo->ppp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = 0;
+
+out:
+ if (ret < 0)
+ openconnect_close_https(vpninfo, 0);
+ else {
+ monitor_fd_new(vpninfo, ssl);
+ monitor_read_fd(vpninfo, ssl);
+ monitor_except_fd(vpninfo, ssl);
+ }
+
+ buf_free(reqbuf);
+ free(auth_token);
+ return ret;
+}
+
+int nx_bye(struct openconnect_info *vpninfo, const char *reason)
+{
+ //ppp_bye(vpninfo);
+ // TODO: implement
+ return -EINVAL;
+}
\ No newline at end of file
#define PPP_ENCAP_F5 1 /* F5 BigIP no HDLC */
#define PPP_ENCAP_F5_HDLC 2 /* F5 BigIP HDLC */
#define PPP_ENCAP_FORTINET_HDLC 3 /* Fortinet HDLC */
-#define PPP_ENCAP_MAX PPP_ENCAP_FORTINET_HDLC
+#define PPP_ENCAP_NX_HDLC 4 /* SonicWall NetExtender HDLC */
+#define PPP_ENCAP_MAX PPP_ENCAP_NX_HDLC
#define COMPR_DEFLATE (1<<0)
#define COMPR_LZS (1<<1)
int oncp_esp_send_probes(struct openconnect_info *vpninfo);
int oncp_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt);
+/* nx.c */
+int nx_obtain_cookie(struct openconnect_info *vpninfo);
+void nx_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
+int nx_connect(struct openconnect_info *vpninfo);
+int nx_bye(struct openconnect_info *vpninfo, const char *reason);
+
/* pulse.c */
int pulse_obtain_cookie(struct openconnect_info *vpninfo);
void pulse_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
static const char *ppps_names[] = { "DEAD", "ESTABLISH", "OPENED", "AUTHENTICATE", "NETWORK", "TERMINATE" };
static const char *encap_names[PPP_ENCAP_MAX+1] = { NULL,
- "F5", "F5 HDLC", "FORTINET HDLC" };
+ "F5", "F5 HDLC", "FORTINET HDLC", "NX HDLC" };
static const char *lcp_names[] = { NULL,
"Configure-Request", "Configure-Ack",
"Configure-Nak", "Configure-Reject",
ppp->hdlc = 1;
break;
+ case PPP_ENCAP_NX_HDLC:
+ ppp->encap_len = 4;
+ ppp->hdlc = 1;
+ break;
+
default:
free(ppp);
return NULL;
handle that */
unsigned char *ph, *pp;
int receive_mtu = MAX(16384, vpninfo->ip_info.mtu);
- int len, payload_len;
+ int len, payload_len, payload_len_hdr;
if (!vpninfo->cstp_pkt) {
vpninfo->cstp_pkt = malloc(sizeof(struct pkt) + receive_mtu);
continue; /* unhdlc_in_place already logged */
if (pp != ph + len)
vpn_progress(vpninfo, PRG_ERR,
- _("Packet contains %ld bytes after payload. Concatenated packets are not handled yet.\n"),
- len - (pp - ph));
- //if (vpninfo->dump_http_traffic)
- // dump_buf_hex(vpninfo, PRG_TRACE, '<', pp, payload_len);
+ _("Packet contains %ld bytes after payload. Concatenated packets are not handled yet.\n"),
+ len - (pp - ph));
+ if (vpninfo->dump_http_traffic)
+ dump_buf_hex(vpninfo, PRG_TRACE, '<', pp, payload_len);
+ break;
+
+ case PPP_ENCAP_NX_HDLC:
+ payload_len_hdr = load_be32(ph);
+ payload_len = unhdlc_in_place(vpninfo, ph + ppp->encap_len, len, &pp);
+ vpn_progress(vpninfo, PRG_INFO, "payload_len_hdr: %x, payload_len: %x, len: %x\n",
+ payload_len_hdr, payload_len, len);
+ if (payload_len < 0)
+ continue; /* unhdlc_in_place already logged */
+ if (pp != ph + len)
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Packet contains %ld bytes after payload. Concatenated packets are not handled yet.\n"),
+ len - (pp - ph));
+ if (vpninfo->dump_http_traffic)
+ dump_buf_hex(vpninfo, PRG_TRACE, '<', pp, payload_len);
break;
default:
free(vpninfo->current_ssl_pkt);
vpninfo->current_ssl_pkt = this;
break;
+ case PPP_ENCAP_NX_HDLC:
+ /* XX: use worst-case escaping for LCP */
+ this = hdlc_into_new_pkt(vpninfo, this->data + n, this->len - n,
+ proto == PPP_LCP ? ASYNCMAP_LCP : ppp->out_asyncmap);
+ if (!this)
+ return 1; /* XX */
+ store_be32(this->data + n - 4, this->len - n);
+ free(vpninfo->current_ssl_pkt);
+ this->ppp.hlen = -n + 4;
+ vpninfo->current_ssl_pkt = this;
+ break;
default:
/* XX: fail */
break;