]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Incomplete, speculative IPv6 for GlobalProtect
authorDaniel Lenski <dlenski@gmail.com>
Fri, 12 Jan 2018 09:44:17 +0000 (01:44 -0800)
committerDavid Woodhouse <dwmw2@infradead.org>
Sun, 9 Jun 2019 23:51:05 +0000 (00:51 +0100)
Client-side IPv6 support was added in v4.0:
https://live.paloaltonetworks.com/t5/Colossal-Event-Blog/New-GlobalProtect-4-0-announced-with-IPv6-support/ba-p/141593

Server-side IPv6 support was added in PanOS 8.0:
https://www.paloaltonetworks.com/documentation/80/pan-os/newfeaturesguide/globalprotect-features

I've been wanting to get IPv6 working for a while, but don't have access to
a GP VPN that supports IPv6, and haven't found anyone else who does.  I'm
adding incomplete, speculative IPv6 support here in the hopes that someone
will use it and report back on partial success/failure:

* Known from Windows client: `ipv6-support=yes` in `/ssl-vpn/login.esp`
  request, `preferred-ipv6` in `/ssl-vpn/getconfig.esp` request,
  `client-ipv6` in `/ssl-vpn/hipreport{,check}.esp` requests,
  `app-version=4.0.5-8`,
* Educated guess: 0x0800 in GPST packet header represents IPv4 ethertype,
  and will be replaced with 0x86DD for IPv6 packets.
* Unknown: IPv6 routing configuration tags to expect in
  `/ssl-vpn/getconfig.esp` response. This build prints a prominent
  error message if it encounters any unknown configuration tags
  containing the character '6', and requests feedback to the mailing
  list.

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
auth-globalprotect.c
gpst.c
trojans/hipreport.sh
www/globalprotect.xml
www/hip.xml

index 8cc543b57bdaf94efe737d1ec8c26b9bde42a54c..ca9331f92059e2d6e40d58c112faf2a85d309bd9 100644 (file)
@@ -489,6 +489,7 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal, struct login
                /* submit gateway login (ssl-vpn/login.esp) or portal config (global-protect/getconfig.esp) request */
                buf_truncate(request_body);
                buf_append(request_body, "jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:");
+               append_opt(request_body, "ipv6-support", vpninfo->disable_ipv6 ? "no" : "yes");
                if (!strcmp(vpninfo->platname, "mac-intel") || !strcmp(vpninfo->platname, "apple-ios"))
                        append_opt(request_body, "clientos", "Mac");
                else if (!strcmp(vpninfo->platname, "linux-64") || !strcmp(vpninfo->platname, "android"))
diff --git a/gpst.c b/gpst.c
index 390411d0a579363482bab97bed98ffff736106c1..7d123d75280c35c60be53da37f0510698f935a5e 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -561,13 +561,31 @@ static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_
 #else
                        vpn_progress(vpninfo, PRG_DEBUG, _("Ignoring ESP keys since ESP support not available in this build\n"));
 #endif
+               } else if (xmlnode_is_named(xml_node, "need-tunnel")
+                          || xmlnode_is_named(xml_node, "bw-c2s")
+                          || xmlnode_is_named(xml_node, "bw-s2c")
+                          || xmlnode_is_named(xml_node, "default-gateway")
+                          || xmlnode_is_named(xml_node, "no-direct-access-to-local-network")
+                          || xmlnode_is_named(xml_node, "ip-address-preferred")
+                          || xmlnode_is_named(xml_node, "portal")
+                          || xmlnode_is_named(xml_node, "user")) {
+                       /* XX: Do these have any potential value at all for routing configuration or diagnostics? */
+               } else if (xml_node->type == XML_ELEMENT_NODE) {
+                       /* XX: Don't know what tags are used for IPv6 addresses and networks, since
+                        * we haven't yet seen a real GlobalProtect VPN with IPv6 internal addresses.
+                        */
+                       free(s);
+                       s = (char *)xmlNodeGetContent(xml_node);
+                       if (strchr((char *)xml_node->name, '6'))
+                               vpn_progress(vpninfo, PRG_ERR, _("Potential IPv6-related GlobalProtect config tag <%s>: %s\n"
+                                                                "This build does not support GlobalProtect IPv6 due to a lack of\n"
+                                                                "of information on how it is configured. Please report this\n"
+                                                                "to <openconnect-devel@lists.infradead.org>.\n"), xml_node->name, s);
+                       else
+                               vpn_progress(vpninfo, PRG_DEBUG, _("Unknown GlobalProtect config tag <%s>: %s\n"), xml_node->name, s);
                }
        }
 
-       /* No IPv6 support for SSL VPN:
-        * https://live.paloaltonetworks.com/t5/Learning-Articles/IPv6-Support-on-the-Palo-Alto-Networks-Firewall/ta-p/52994 */
-       openconnect_disable_ipv6(vpninfo);
-
        /* Set 10-second DPD/keepalive (same as Windows client) unless
         * overridden with --force-dpd */
        if (!vpninfo->ssl_times.dpd)
@@ -585,12 +603,13 @@ static int gpst_get_config(struct openconnect_info *vpninfo)
        struct oc_text_buf *request_body = buf_alloc();
        struct oc_vpn_option *old_cstp_opts = vpninfo->cstp_options;
        const char *old_addr = vpninfo->ip_info.addr, *old_netmask = vpninfo->ip_info.netmask;
+       const char *old_addr6 = vpninfo->ip_info.addr6, *old_netmask6 = vpninfo->ip_info.netmask6;
        const char *request_body_type = "application/x-www-form-urlencoded";
        const char *method = "POST";
        char *xml_buf=NULL;
 
        /* submit getconfig request */
-       buf_append(request_body, "client-type=1&protocol-version=p1&app-version=3.0.1-10");
+       buf_append(request_body, "client-type=1&protocol-version=p1&app-version=4.0.5-8");
        if (!strcmp(vpninfo->platname, "mac-intel") || !strcmp(vpninfo->platname, "apple-ios"))
                append_opt(request_body, "clientos", "Mac");
        else if (!strcmp(vpninfo->platname, "linux-64") || !strcmp(vpninfo->platname, "android"))
@@ -600,9 +619,10 @@ static int gpst_get_config(struct openconnect_info *vpninfo)
        append_opt(request_body, "os-version", vpninfo->platname);
        append_opt(request_body, "hmac-algo", "sha1,md5");
        append_opt(request_body, "enc-algo", "aes-128-cbc,aes-256-cbc");
-       if (old_addr) {
+       if (old_addr || old_addr6) {
                append_opt(request_body, "preferred-ip", old_addr);
-               filter_opts(request_body, vpninfo->cookie, "preferred-ip", 0);
+               append_opt(request_body, "preferred-ipv6", old_addr6);
+               filter_opts(request_body, vpninfo->cookie, "preferred-ip,preferred-ipv6", 0);
        } else
                buf_append(request_body, "&%s", vpninfo->cookie);
        if ((result = buf_error(request_body)))
@@ -638,7 +658,8 @@ static int gpst_get_config(struct openconnect_info *vpninfo)
                             no_esp_reason ? "SSL tunnel. " : "ESP tunnel", no_esp_reason ? : "");
                /* return -EINVAL; */
        }
-       if (!vpninfo->ip_info.addr) {
+       if (!vpninfo->ip_info.addr && !vpninfo->ip_info.addr6 &&
+           !vpninfo->ip_info.netmask6) {
                vpn_progress(vpninfo, PRG_ERR,
                             _("No IP address received. Aborting\n"));
                result = -EINVAL;
@@ -662,6 +683,22 @@ static int gpst_get_config(struct openconnect_info *vpninfo)
                        goto out;
                }
        }
+       if (old_addr6) {
+               if (strcmp(old_addr6, vpninfo->ip_info.addr6)) {
+                       vpn_progress(vpninfo, PRG_ERR,
+                                    _("Reconnect gave different IPv6 address (%s != %s)\n"),
+                                    vpninfo->ip_info.addr6, old_addr6);
+                       return -EINVAL;
+               }
+       }
+       if (old_netmask6) {
+               if (strcmp(old_netmask6, vpninfo->ip_info.netmask6)) {
+                       vpn_progress(vpninfo, PRG_ERR,
+                                    _("Reconnect gave different IPv6 netmask (%s != %s)\n"),
+                                    vpninfo->ip_info.netmask6, old_netmask6);
+                       return -EINVAL;
+               }
+       }
 
 out:
        free_optlist(old_cstp_opts);
@@ -820,7 +857,10 @@ static int check_or_submit_hip_report(struct openconnect_info *vpninfo, const ch
 
        /* cookie gives us these fields: authcookie, portal, user, domain, computer, and (maybe the unnecessary) preferred-ip */
        buf_append(request_body, "client-role=global-protect-full&%s", vpninfo->cookie);
-       append_opt(request_body, "client-ip", vpninfo->ip_info.addr);
+       if (vpninfo->ip_info.addr)
+               append_opt(request_body, "client-ip", vpninfo->ip_info.addr);
+       if (vpninfo->ip_info.addr6)
+               append_opt(request_body, "client-ipv6", vpninfo->ip_info.addr6);
        if (report) {
                /* XML report contains many characters requiring URL-encoding (%xx) */
                buf_ensure_space(request_body, strlen(report)*3);
@@ -932,8 +972,14 @@ static int run_hip_script(struct openconnect_info *vpninfo)
                hip_argv[i++] = openconnect_utf8_to_legacy(vpninfo, vpninfo->csd_wrapper);
                hip_argv[i++] = (char *)"--cookie";
                hip_argv[i++] = vpninfo->cookie;
-               hip_argv[i++] = (char *)"--client-ip";
-               hip_argv[i++] = (char *)vpninfo->ip_info.addr;
+               if (vpninfo->ip_info.addr) {
+                       hip_argv[i++] = (char *)"--client-ip";
+                       hip_argv[i++] = (char *)vpninfo->ip_info.addr;
+               }
+               if (vpninfo->ip_info.addr6) {
+                       hip_argv[i++] = (char *)"--client-ipv6";
+                       hip_argv[i++] = (char *)vpninfo->ip_info.addr6;
+               }
                hip_argv[i++] = (char *)"--md5";
                hip_argv[i++] = vpninfo->csd_token;
                hip_argv[i++] = NULL;
@@ -1090,9 +1136,10 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
                        }
                        continue;
                case 0x0800:
+               case 0x86DD:
                        vpn_progress(vpninfo, PRG_TRACE,
-                                    _("Received data packet of %d bytes\n"),
-                                    payload_len);
+                                    _("Received IPv%d data packet of %d bytes\n"),
+                                    ethertype == 0x86DD ? 6 : 4, payload_len);
                        vpninfo->cstp_pkt->len = payload_len;
                        queue_packet(&vpninfo->incoming_queue, vpninfo->cstp_pkt);
                        vpninfo->cstp_pkt = NULL;
@@ -1193,16 +1240,19 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
               (vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) {
                struct pkt *this = vpninfo->current_ssl_pkt;
 
+               /* IPv4 or IPv6 EtherType */
+               int ethertype = this->len && (this->data[0] & 0xF0) == 0x60 ? 0x86DD : 0x0800;
+
                /* store header */
                store_be32(this->gpst.hdr, 0x1a2b3c4d);
-               store_be16(this->gpst.hdr + 4, 0x0800); /* IPv4 EtherType */
+               store_be16(this->gpst.hdr + 4, ethertype);
                store_be16(this->gpst.hdr + 6, this->len);
                store_le32(this->gpst.hdr + 8, 1);
                store_le32(this->gpst.hdr + 12, 0);
 
                vpn_progress(vpninfo, PRG_TRACE,
-                            _("Sending data packet of %d bytes\n"),
-                            this->len);
+                            _("Sending IPv%d data packet of %d bytes\n"),
+                            (ethertype == 0x86DD ? 6 : 4), this->len);
 
                goto handle_outgoing;
        }
@@ -1304,7 +1354,7 @@ int gpst_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt)
 {
        struct ip *iph = (void *)(pkt->data);
 
-       return ( pkt->len >= 21
+       return ( pkt->len >= 21 && iph->ip_v==4 /* IPv4 header */
                 && iph->ip_p==1 /* IPv4 protocol field == ICMP */
                 && iph->ip_src.s_addr == vpninfo->esp_magic /* source == magic address */
                 && pkt->len >= (iph->ip_hl<<2) + ICMP_MINLEN + sizeof(magic_ping_payload) /* No short-packet segfaults */
index e3e9deb039336aba6f529a5e9a2fe077b58bdad5..9ac04b5627b019c606cf1c3dcb8b80e36c335130 100755 (executable)
@@ -8,9 +8,9 @@
 #             --authenticate --protocol=gp, which includes parameters
 #             from the /ssl-vpn/login.esp response
 #
-#   --client-ip: IPv4 address allocated by the GlobalProtect VPN for
-#                this client (included in /ssl-vpn/getconfig.esp
-#                response)
+#   --client-ip{,v6}: IPv4/6 addresses allocated by the GlobalProtect
+#                     VPN for this client (included in
+#                     /ssl-vpn/getconfig.esp response)
 #
 #   --md5: The md5 digest to encode into this HIP report. I'm not sure
 #          exactly what this is the md5 digest *of*, but all that
 # Read command line arguments into variables
 COOKIE=
 IP=
+IPv6=
 MD5=
 
 while [ "$1" ]; do
-    if [ "$1" = "--cookie" ];    then shift; COOKIE="$1"; fi
-    if [ "$1" = "--client-ip" ]; then shift; IP="$1"; fi
-    if [ "$1" = "--md5" ];       then shift; MD5="$1"; fi
+    if [ "$1" = "--cookie" ];      then shift; COOKIE="$1"; fi
+    if [ "$1" = "--client-ip" ];   then shift; IP="$1"; fi
+    if [ "$1" = "--client-ipv6" ]; then shift; IPV6="$1"; fi
+    if [ "$1" = "--md5" ];         then shift; MD5="$1"; fi
     shift
 done
 
-if [ -z "$COOKIE" -o -z "$IP" -o -z "$MD5" ]; then
-    echo "Parameters --cookie, --client-ip, and --md5 are required" >&2
+if [ -z "$COOKIE" -o -z "$MD5" -o -z "$IP$IPV6" ]; then
+    echo "Parameters --cookie, --md5, and --client-ip and/or --client-ipv6 are required" >&2
     exit 1;
 fi
 
@@ -58,7 +60,7 @@ cat <<EOF
        <host-name>$COMPUTER</host-name>
        <host-id>$HOSTID</host-id>
        <ip-address>$IP</ip-address>
-       <ipv6-address></ipv6-address>
+       <ipv6-address>$IPV6</ipv6-address>
        <generate-time>$NOW</generate-time>
        <categories>
                <entry name="host-info">
@@ -76,7 +78,7 @@ cat <<EOF
                                                <entry name="$IP"/>
                                        </ip-address>
                                        <ipv6-address>
-                                               <entry name="dead::beef:dead:beef:dead"/>
+                                               <entry name="$IPV6"/>
                                        </ipv6-address>
                                </entry>
                        </network-interface>
index a9de423a99a1633a6af025329bf69ba79fe23159..3984b3c8c31573dc108a05f24923e90cf1565725 100644 (file)
@@ -64,10 +64,12 @@ always shows <tt><![CDATA[&lt;mtu&gt;0&lt;/mtu&gt;]]></tt>.  OpenConnect attempt
 calculate the MTU by starting from the base MTU with the overhead of
 encapsulating each packets within ESP, UDP, and IP.</p>
 
-<p>There is currently no IPv6 support.  <a
-href="https://live.paloaltonetworks.com/t5/Learning-Articles/IPv6-Support-on-the-Palo-Alto-Networks-Firewall/ta-p/52994">PAN's
-documentation</a> suggests that recent versions of GlobalProtect may support
-IPv6 over the ESP tunnel, though not the SSL tunnel.</p>
+<p>IPv6 support was added in <a
+href="https://live.paloaltonetworks.com/t5/Colossal-Event-Blog/New-GlobalProtect-4-0-announced-with-IPv6-support/ba-p/141593">GlobalProtect
+4.0 in 2017</a> but <b>OpenConnect support for GlobalProtect IPv6 is incomplete</b>
+due to developers' lack of access to a GlobalProtect VPN server that supports it.
+If you have access to a GlobalProtect VPN that supports IPv6, please send information
+to <a href="mail.html">the mailing list</a> so we can add complete support.</p>
 
 <p>The ESP and HTTPS tunnels cannot be connected simultaneously.  The ESP
 tunnel becomes unresponsive as soon as the HTTPS tunnel is started, and
@@ -77,7 +79,7 @@ re-fetched.</p>
 <p>Compared to the AnyConnect or Juniper protocols, the GlobalProtect
 protocol appears to have very little in the way of <a
 href="https://en.wikipedia.org/wiki/In-band_signaling">in-band
-signaling</a>.  The HTTPS tunnel can only send or receive IPv4 packets and a
+signaling</a>.  The HTTPS tunnel can only send or receive IP packets and a
 simple DPD/keepalive packet (always sent by the client and echoed by the
 server).  The ESP tunnel does not have any special DPD/keepalive packet, but
 uses an <a
index 57b4e2ef017fedbbd9c9878166a718c8f327bfbb..f17bae1549debaf30836c1679aa4c314038e64ca 100644 (file)
@@ -58,9 +58,9 @@ server. This shell script must output the HIP report to standard output and exit
              --authenticate --protocol=gp, which includes parameters
              --from the /ssl-vpn/login.esp response
 
-   --client-ip: IPv4 address allocated by the GlobalProtect VPN for
-                this client (included in /ssl-vpn/getconfig.esp
-                response)
+   --client-ip{,v6}: IPv4/6 addresses allocated by the GlobalProtect
+                     VPN for this client (included in
+                     /ssl-vpn/getconfig.esp response)
 
    --md5: The md5 digest to encode into this HIP report. All that
           really matters is that the value in the HIP report