]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
GP: Pass 'preferred-ipv6' parameter among auth requests, just like 'preferred-ip'
authorDaniel Lenski <dlenski@gmail.com>
Mon, 3 May 2021 21:27:24 +0000 (14:27 -0700)
committerDaniel Lenski <dlenski@gmail.com>
Mon, 3 May 2021 21:51:11 +0000 (14:51 -0700)
Testing against a real GP server with IPv6 support shows that the
"preferred" IPv6 address can be passed in the /ssl-vpn/login.esp request
arguments, as well as returned by it. (Even if unprompted; some GP VPNs
seem to track a persistent mapping of user accounts to IP addresses on the
server side.)

We should save the 'preferred-ipv6' parameter from the login results in
vpninfo->cookie, just as we do with its Legacy IP counterpart,
'preferred-ip'.

Also adds Flask tests to verify the correct passing of
'preferred-ip'/'-ipv6'.

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
auth-globalprotect.c
gpst.c
tests/fake-gp-server.py
tests/gp-auth-and-config

index f32acd3423052108a9c81a007cc7e37d32337256..a698cb436680e46ebfc662a0aed27e56cbaf7484 100644 (file)
@@ -278,7 +278,7 @@ static const struct gp_login_arg gp_login_args[] = {
        { .opt="preferred-ip", .save=1 },
        { .opt="portal-userauthcookie", .show=1},
        { .opt="portal-prelogonuserauthcookie", .show=1},
-       { .unknown=1 },
+       { .opt="preferred-ipv6", .save=1 },
        { .opt="usually-equals-4", .show=1 },           /* newer servers send "4" here, meaning unknown */
        { .opt="usually-equals-unknown", .show=1 },     /* newer servers send "unknown" here */
 };
@@ -611,6 +611,8 @@ static int gpst_login(struct openconnect_info *vpninfo, int portal, struct login
                append_opt(request_body, "computer", vpninfo->localname);
                if (vpninfo->ip_info.addr)
                        append_opt(request_body, "preferred-ip", vpninfo->ip_info.addr);
+               if (vpninfo->ip_info.addr6)
+                       append_opt(request_body, "preferred-ipv6", vpninfo->ip_info.addr);
                if (ctx->form->action)
                        append_opt(request_body, "inputStr", ctx->form->action);
                append_form_opts(vpninfo, ctx->form, request_body);
diff --git a/gpst.c b/gpst.c
index 9f424c670cc56f6a9ad2f3995ab39247450193e9..e3d8cc800a76c2f2f0e1fade03e025364ea4aae4 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -823,9 +823,9 @@ static int build_csd_token(struct openconnect_info *vpninfo)
        if (!vpninfo->csd_token)
                return -ENOMEM;
 
-       /* use cookie (excluding volatile authcookie and preferred-ip) to build md5sum */
+       /* use cookie (excluding volatile authcookie and preferred-ip/ipv6) to build md5sum */
        buf = buf_alloc();
-       filter_opts(buf, vpninfo->cookie, "authcookie,preferred-ip", 0);
+       filter_opts(buf, vpninfo->cookie, "authcookie,preferred-ip,preferred-ipv6", 0);
        if (buf_error(buf))
                goto out;
 
@@ -848,7 +848,7 @@ static int check_or_submit_hip_report(struct openconnect_info *vpninfo, const ch
        const char *method = "POST";
        char *xml_buf=NULL, *orig_path;
 
-       /* cookie gives us these fields: authcookie, portal, user, domain, computer, and (maybe the unnecessary) preferred-ip */
+       /* cookie gives us these fields: authcookie, portal, user, domain, computer, and (maybe the unnecessary) preferred-ip/ipv6 */
        buf_append(request_body, "client-role=global-protect-full&%s", vpninfo->cookie);
        if (vpninfo->ip_info.addr)
                append_opt(request_body, "client-ip", vpninfo->ip_info.addr);
index 11e905c7a983184321dc6f75c8e2e87f20ad50b1..585c5ac4f839ea4cb558a4b5ef8e980d3629a80c 100755 (executable)
@@ -47,12 +47,13 @@ def check_form_against_session(*fields, use_query=False, on_failure=None):
             source = request.args if use_query else request.form
             source_name = 'args' if use_query else 'form'
             for f in fields:
+                fs = f.replace('_', '-')
                 if on_failure:
-                    if session.get(f) != source.get(f) or f not in source:
+                    if session.get(f) != source.get(fs):
                         return on_failure
                 else:
-                    assert session.get(f) == source.get(f), \
-                        f'at step {session.get("step")}: {source_name} {f!r} {source.get(f)!r} != session {f!r} {session.get(f)!r}'
+                    assert session.get(f) == source.get(fs), \
+                        f'at step {session.get("step")}: {source_name} {f!r} {source.get(fs)!r} != session {f!r} {session.get(f)!r}'
             return fn(*args, **kwargs)
         return wrapped
     return inner
@@ -147,7 +148,12 @@ def gateway_login():
     auth = 'Auth%d' % randint(1, 10)
     domain = 'Domain%d' % randint(1, 10)
     preferred_ip = request.form.get('preferred-ip') or '192.168.%d.%d' % (randint(2, 254), randint(2, 254))
-    session.update(preferred_ip=preferred_ip, portal=portal, auth=auth, domain=domain, computer=request.form.get('computer'))
+    if request.form.get('ipv6-support') == 'yes':
+        preferred_ipv6 = request.form.get('preferred-ipv6') or 'fd00::%x' % randint(0x1000, 0xffff)
+    else:
+        preferred_ipv6 = None
+    session.update(preferred_ip=preferred_ip, portal=portal, auth=auth, domain=domain, computer=request.form.get('computer'),
+                   ipv6_support=request.form.get('ipv6-support'), preferred_ipv6=preferred_ipv6)
     session['authcookie'] = cookify(dict(session)).decode()
 
     return '''<?xml version="1.0" encoding="utf-8"?> <jnlp> <application-desc>
@@ -167,17 +173,21 @@ def gateway_login():
             <argument>-1</argument>
             <argument>4100</argument>
             <argument>{preferred_ip}</argument>
-            </application-desc></jnlp>'''.format(**session)
+            <argument/>
+            <argument/>
+            <argument>{ipv6}</argument>
+            </application-desc></jnlp>'''.format(ipv6=preferred_ipv6 or '', **session)
 
 
 # Respond to gateway getconfig request
 @app.route('/ssl-vpn/getconfig.esp', methods=('POST',))
-@check_form_against_session('user', 'portal', 'domain', 'authcookie', on_failure="errors getting SSL/VPN config")
+@check_form_against_session('user', 'portal', 'domain', 'authcookie', 'preferred_ip', 'preferred_ipv6', 'ipv6_support', on_failure="errors getting SSL/VPN config")
 def getconfig():
     session.update(step='gateway-config')
-    return '''<response><ip-address>{preferred_ip}</ip-address>
-        <ssl-tunnel-url>/ssl-tunnel-connect.sslvpn</ssl-tunnel-url>
-        </response>'''.format(**session)
+    addrs = '<ip-address>{}</ip-address>'.format(session['preferred_ip'])
+    if session['ipv6_support'] == 'yes':
+        addrs += '<ip-address-v6>{}</ip-address-v6>'.format(session['preferred_ipv6'])
+    return '''<response>{}<ssl-tunnel-url>/ssl-tunnel-connect.sslvpn</ssl-tunnel-url></response>'''.format(addrs)
 
 
 # Respond to gateway getconfig request
index a66681c821c880855bdf4de3baac03e1fd1a95d9..ace70f176355c74d735965140d9fbb9afb7b5f18 100755 (executable)
@@ -72,6 +72,13 @@ test $? = 2 || # what OpenConnect returns when server rejects cookie upon tunnel
 
 echo ok
 
+echo -n "Authenticating with username/password via portal, then proceeding to tunnel stage (with IPv6 disabled)... "
+echo "test" | LD_PRELOAD=libsocket_wrapper.so $OPENCONNECT --protocol=gp --disable-ipv6 -q $ADDRESS:443/portal -u test $FINGERPRINT >/dev/null 2>&1
+test $? = 2 || # what OpenConnect returns when server rejects cookie upon tunnel connection, as the fake server does
+    fail $PID "Something went wrong in fake GlobalProtect server (other than the expected rejection of cookie)"
+
+echo ok
+
 cleanup
 
 exit 0