OpenConnect on OS X and the Latest AnyConnect SSL VPN Rev

Andrew Kutz sakutz at gmail.com
Wed May 15 16:45:51 EDT 2013


David,

Thank you for this wonderful software. It's so nice to be able to create a VPN connection to my office without them totally raping my routing tables. That said, it took 48 hours of straight packet sniffing and mucking about to make it work. Here's how I got OpenConnect  4.08-4.99 to work against my company's AnyConnect SSL VPN solution from my OS X 10.8.2 client.

	• The 4.08 version available via Homebrew has an issue where the sfinst file is not downloaded, but rather the XML that points to it is. For example:

<?xml version="1.0" encoding="UTF-8"?>
<auth id="main" type="sdi">
    <title>SSL VPN Service</title>
    <ca href="/+CSCOCA+/login.html" status="disabled"/>
    <csd ticket="5E1380B54A1C94EC7CFF0B9F" token="24A9313E7D39FDF9276D6CC0"/>
    <csd preloginname="CSDPreLogin" preloginurl="/CACHE/sdesktop/install/binaries/cache.jar" starturl="/CACHE/sdesktop/install/result.htm" stuburl="/CACHE/sdesktop/install/binaries/inst.exe" waiturl="/+CSCOE+/sdesktop/wait.html"/>
    <csdMac starturl="/CACHE/sdesktop/install/result.htm" stuburl="/CACHE/sdesktop/install/binaries/sfinst" waiturl="/+CSCOE+/sdesktop/wait.html"/>
    <csdLinux starturl="/CACHE/sdesktop/install/result.htm" stuburl="/CACHE/sdesktop/install/binaries/sfinst" waiturl="/+CSCOE+/sdesktop/wait.html"/>
    <banner>This Virtual Private Network is intended for
authorized *** employees and contractors.
Unauthorized use is prohibited by law and may be
subject to civil and/or criminal penalties. This system
may be logged or monitored without further notice
and resulting logs may be used as evidence in court.</banner>
    <message>Please enter your username and password.</message>
    <form action="/+webvpn+/index.html" method="post">
        <input label="Username:" name="username" type="text"/>
        <input label="PASSCODE:" name="password" type="password"/>
        <input name="Login" type="submit" value="Login"/>
        <input name="Clear" type="reset" value="Clear"/>
    </form>
</auth>

	• Even when upgrading the Homebrew formula to 4.99, the issue persisted. It wasn't until I downloaded the 4.99 sources and compiled it outside of Homebrew that this issue went away. I have no idea why it did, but it did.

	• Which did not end up meaning a whole lot, because it turns out the current solution for CSD no longer works. This is the part that took 24 hours to figure out. It turns out that my company hasn't completely replaced the old method, but the above file is no longer used at all (although it is still retrieved and parsed by OpenConnect and the sfinst script is downloaded (once I compiled 4.99 from source) and executed. However, *every* time this happened a Java window would appear telling me that SwordFish.jar could not be found on the server. I thought it had to be because I was missing an auth header or something that the server wasn't letting me access the file. It worked fine in the browser…

	• After a day of playing around with various methods to figure out what was going on, I broke out the packet sniffers.

	• Even with stunnel providing a MITM proxy so I could decrypt the SSL, Wireshark was unable to reassemble the packets. Thus I loaded Fiddler on my Windows VM. I took three packet dumps using Fiddler's SSL decryption capabilities (it seamlessly sets up the same MITM configuration):
		• I connected to the VPN via the web inside the VM
		• I connected to the VPN via the web from my Mac (and used Fiddler's ability to advertise itself as a proxy to proxy my Mac's connections through that so I can dump those too)
		• I dumped OpenConnect

	• Now, I don't feel comfortable sharing the packet logs with the entire list, but I will send them directly to you. They include my company's IPs, and although they're discoverable, I'd rather not have them on a mailing list archive.

	• What I learned was that at least the version of AnyConnect to which I'm connecting no longer uses sfinst. It uses  /CACHE/sdesktop/install/binaries/instjava.java. 

	• I downloaded this file manually and using JD-GUI I tracked down how it's executed. It's very similar to the options used by sfinst:

(from InstallerJava.class)

   if (!OsIsWindows)
          {
            try
            {
              String str4 = "/bin/chmod u+x " + str2;
              localObject3 = ((Runtime)localObject1).exec(str4);
              int k = ((Process)localObject3).waitFor();
              if (k != 0) { log("chmod returned: " + k);
                setStatus(STATUS_STUB_FAILURE);
                setErrorCode(STUB_CHANGE_PERMISSION_FAILED);
                return;
              } } catch (Exception localException4) { log("Exception while giving executable permissions to stub");
              setStatus(STATUS_STUB_FAILURE);
              setErrorCode(STUB_CHANGE_PERMISSION_FAILED);
              log("Exception occured: " + localException4.getMessage());
              localException4.printStackTrace();
              return;
            }
          }
          ((Vector)localObject2).addElement(str2);
          if ((this.ticket.length() > 0) && (this.stub.length() > 0))
          {
            ((Vector)localObject2).addElement("-ticket");
            ((Vector)localObject2).addElement(this.ticket);
            ((Vector)localObject2).addElement("-stub");
            ((Vector)localObject2).addElement(this.stub);
          }
          if (this.certhash.length() > 0)
          {
            ((Vector)localObject2).addElement("-certhash");
            ((Vector)localObject2).addElement(this.certhash);
          }
          if (this.group.length() > 0)
          {

	• So it's executing some file. What file? Well, based on the packet dumps it's executing the file "/CACHE/sdesktop/hostscan/windows_i386/cstub.exe" on Windows and "/CACHE/sdesktop/hostscan/darwin_i386/cstub" on a Mac. I didn't test it on Linux.

	• At this point I could either patch the source of OpenConnect to make this work, or I could just use a CSD wrapper. I did the latter.

	• Also, FWIW, I did make one change to the sources for the sake of dumping the vpninfo easily to the logs:

diff --git a/auth.c b/auth.c
index c3bc4fb..7c06456 100644
--- a/auth.c
+++ b/auth.c
@@ -462,6 +462,15 @@ static int parse_auth_node(struct openconnect_info *vpninfo, xmlNode *xml_node,
                }
        }
 
+       /*vpn_progress(vpninfo, PRG_INFO,
+               _("vpninfo: tok=%s, tic=%s, stub=%s, start=%s, wait=%s"),
+               vpninfo->csd_token,
+               vpninfo->csd_ticket,
+               vpninfo->csd_stuburl,
+               vpninfo->csd_starturl,
+               vpninfo->csd_waiturl);*/
+       vpn_progress_all(vpninfo);
+
 out:
        return ret;
 }
diff --git a/openconnect-internal.h b/openconnect-internal.h
index 51172af..9f621b7 100644
--- a/openconnect-internal.h
+++ b/openconnect-internal.h
@@ -333,6 +333,13 @@ struct openconnect_info {
 #define AC_PKT_TERM_SERVER     9       /* Server kick */
 
 #define vpn_progress(vpninfo, ...) (vpninfo)->progress((vpninfo)->cbdata, __VA_ARGS__)
+#define vpninfo_string(vpninfo) _("vpninfo: tok='%s', tic='%s', stub='%s', start='%s', wait='%s'\n"),\
+    (vpninfo)->csd_token,\
+    (vpninfo)->csd_ticket,\
+    (vpninfo)->csd_stuburl,\
+    (vpninfo)->csd_starturl,\
+    (vpninfo)->csd_waiturl
+#define vpn_progress_all(vpninfo) vpn_progress((vpninfo), 1, vpninfo_string((vpninfo)))
 
 /****************************************************************************/
 /* Oh Solaris how we hate thee! */

	• So anyway, I fetched the cstub binary for Darwin and put it in $HOME/.vpn. I then wrote the following wrapper for it:

#!/bin/sh

echo "$0 $*"

shift

URL=
TICKET=
STUB=
GROUP=
CERTHASH=
LANGSELEN=

while [ "$1" ]; do
    if [ "$1" == "-ticket" ];   then shift; TICKET=$1; fi
    if [ "$1" == "-stub" ];     then shift; STUB=$1; fi
    if [ "$1" == "-group" ];    then shift; GROUPR=$1; fi
    if [ "$1" == "-certhash" ]; then shift; CERTHASH=$1; fi
    if [ "$1" == "-url" ];      then shift; URL=$1; fi
    if [ "$1" == "-langselen" ];then shift; LANGSELEN=$1; fi
    shift
done

echo "URL:          $URL"
echo "TICKET:       $TICKET"
echo "STUB:         $STUB"
echo "GROUP:        $GROUP"
echo "CERTHASH:     $CERTHASH"
echo "LANGSELEN:    $LANGSELEN"

ARGS="-url $URL -ticket $TICKET -stub $STUB"
if [ "$GROUP" ]; then ARGS="$ARGS -group $GROUP"; fi
ARGS="$ARGS -certhash $CERTHASH"
if [ "$LANGSELEN" ]; then ARGS="$ARGS -langsel $LANGSELEN"; fi

echo "Executing $HOME/.vpn/cstub $ARGS"
$HOME/.vpn/cstub $ARGS

	• Once that was in place OpenConnect was finally able to connect! Almost. There is a bug in your vpnc-script that you link to on your site. In the do_connect and do_disconnect methods an incorrect Bash comparison is used. The $NETWORK variable needs to be quoted:
			
			if [ "$NETWORK" != "0.0.0.0" ]; then
				set_network_route "$NETWORK" "$NETMASK" "$NETMASKLEN"
			else
				set_default_route
			fi

After I did all that I was finally able to connect to my VPN as well as split-tunnel my connection using the CISCO_SPLIT_INC_%d_\w+ variables. Thank you so much for this awesome piece of software!

The packet dumps are here - https://docs.google.com/file/d/0BwA1o-dOr0AVbE1Wd0VYdF9zNjQ/edit?usp=sharing. I will be sending you the password for the 7z in a separate, private e-mail.

-- 
-a

"I wonder if procrastinators realize that they're not putting off work, just putting it off onto other people?"


More information about the openconnect-devel mailing list