From ad9107acfe6ef12e6651ff22500f636f6d76545c Mon Sep 17 00:00:00 2001 From: Daniel Lenski Date: Mon, 3 May 2021 14:27:24 -0700 Subject: [PATCH] GP: Pass 'preferred-ipv6' parameter among auth requests, just like 'preferred-ip' 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 --- auth-globalprotect.c | 4 +++- gpst.c | 6 +++--- tests/fake-gp-server.py | 28 +++++++++++++++++++--------- tests/gp-auth-and-config | 7 +++++++ 4 files changed, 32 insertions(+), 13 deletions(-) diff --git a/auth-globalprotect.c b/auth-globalprotect.c index f32acd34..a698cb43 100644 --- a/auth-globalprotect.c +++ b/auth-globalprotect.c @@ -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 9f424c67..e3d8cc80 100644 --- 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); diff --git a/tests/fake-gp-server.py b/tests/fake-gp-server.py index 11e905c7..585c5ac4 100755 --- a/tests/fake-gp-server.py +++ b/tests/fake-gp-server.py @@ -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 ''' @@ -167,17 +173,21 @@ def gateway_login(): -1 4100 {preferred_ip} - '''.format(**session) + + + {ipv6} + '''.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 '''{preferred_ip} - /ssl-tunnel-connect.sslvpn - '''.format(**session) + addrs = '{}'.format(session['preferred_ip']) + if session['ipv6_support'] == 'yes': + addrs += '{}'.format(session['preferred_ipv6']) + return '''{}/ssl-tunnel-connect.sslvpn'''.format(addrs) # Respond to gateway getconfig request diff --git a/tests/gp-auth-and-config b/tests/gp-auth-and-config index a66681c8..ace70f17 100755 --- a/tests/gp-auth-and-config +++ b/tests/gp-auth-and-config @@ -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 -- 2.50.1