Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
GP: Pass 'preferred-ipv6' parameter among auth requests, just like 'p…
…referred-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 <dlenski@gmail.com>
  • Loading branch information
dlenski committed May 3, 2021
1 parent 9b5652e commit ad9107a
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 13 deletions.
4 changes: 3 additions & 1 deletion auth-globalprotect.c
Expand Up @@ -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 */
};
Expand Down Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions gpst.c
Expand Up @@ -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;

Expand All @@ -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);
Expand Down
28 changes: 19 additions & 9 deletions tests/fake-gp-server.py
Expand Up @@ -47,12 +47,13 @@ def wrapped(*args, **kwargs):
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
Expand Down Expand Up @@ -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>
Expand All @@ -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
Expand Down
7 changes: 7 additions & 0 deletions tests/gp-auth-and-config
Expand Up @@ -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

0 comments on commit ad9107a

Please sign in to comment.