17
0

include clientos in prelogin.esp parameters (ping #6)

Apparently, it affects whether the prelogin.esp response contains SAML tags
in some cases.
(see https://github.com/dlenski/gp-saml-gui/issues/6#issuecomment-599743060)

This fits in with a long line of mystifying issues caused by GlobalProtect servers
silently handling different `clientos` values in stupidly different ways.
(see https://gitlab.com/openconnect/openconnect/-/merge_requests/17)
This commit is contained in:
Daniel Lenski 2020-03-17 15:54:48 -07:00
parent 3e09aecfec
commit 2cf05074cc
2 changed files with 26 additions and 9 deletions

View File

@ -11,9 +11,9 @@ import ssl
from operator import setitem from operator import setitem
from os import path from os import path
from shlex import quote from shlex import quote
from sys import stderr from sys import stderr, platform
from binascii import a2b_base64, b2a_base64 from binascii import a2b_base64, b2a_base64
from urllib.parse import urlparse from urllib.parse import urlparse, urlencode
gi.require_version('Gtk', '3.0') gi.require_version('Gtk', '3.0')
gi.require_version('WebKit2', '4.0') gi.require_version('WebKit2', '4.0')
@ -122,6 +122,9 @@ class SAMLLoginView:
Gtk.main_quit() Gtk.main_quit()
def parse_args(args = None): def parse_args(args = None):
clientos_map = dict(linux='Linux', darwin='Mac', win32='Windows', cygwin='Windows')
default_clientos = clientos_map.get(platform, 'Windows')
p = argparse.ArgumentParser() p = argparse.ArgumentParser()
p.add_argument('server', help='GlobalProtect server (portal or gateway)') p.add_argument('server', help='GlobalProtect server (portal or gateway)')
p.add_argument('--no-verify', dest='verify', action='store_false', default=True, help='Ignore invalid server certificate') p.add_argument('--no-verify', dest='verify', action='store_false', default=True, help='Ignore invalid server certificate')
@ -142,6 +145,7 @@ def parse_args(args = None):
g.add_argument('-v','--verbose', default=0, action='count') g.add_argument('-v','--verbose', default=0, action='count')
g.add_argument('-x','--external', action='store_true', help='Launch external browser (for debugging)') g.add_argument('-x','--external', action='store_true', help='Launch external browser (for debugging)')
g.add_argument('-u','--uri', action='store_true', help='Treat server as the complete URI of the SAML entry point, rather than GlobalProtect server') g.add_argument('-u','--uri', action='store_true', help='Treat server as the complete URI of the SAML entry point, rather than GlobalProtect server')
g.add_argument('--clientos', choices=set(clientos_map.values()), default=default_clientos, help="clientos value to send (default is %(default)s)")
p.add_argument('extra', nargs='*', help='Extra form field(s) to pass to include in the login query string (e.g. "magic-cookie-value=deadbeef01234567")') p.add_argument('extra', nargs='*', help='Extra form field(s) to pass to include in the login query string (e.g. "magic-cookie-value=deadbeef01234567")')
args = p.parse_args(args = None) args = p.parse_args(args = None)
@ -176,10 +180,11 @@ if __name__ == "__main__":
sam, uri, html = 'URI', args.server, None sam, uri, html = 'URI', args.server, None
else: else:
endpoint = 'https://{}/{}'.format(args.server, if2prelogin[args.interface]) endpoint = 'https://{}/{}'.format(args.server, if2prelogin[args.interface])
data = {'tmp':'tmp', 'kerberos-support':'yes', 'ipv6-support':'yes', 'clientVer':4100, 'clientos':args.clientos, **args.extra}
if args.verbose: if args.verbose:
print("Looking for SAML auth tags in response to %s..." % endpoint, file=stderr) print("Looking for SAML auth tags in response to %s..." % endpoint, file=stderr)
try: try:
res = s.post(endpoint, verify=args.verify, data=args.extra) res = s.post(endpoint, verify=args.verify, data=data)
except Exception as ex: except Exception as ex:
rootex = ex rootex = ex
while True: while True:
@ -196,12 +201,14 @@ if __name__ == "__main__":
raise raise
xml = ET.fromstring(res.content) xml = ET.fromstring(res.content)
if xml.tag != 'prelogin-response': if xml.tag != 'prelogin-response':
p.error("This does not appear to be a GlobalProtect prelogin response\nCheck in browser: {}".format(endpoint)) p.error("This does not appear to be a GlobalProtect prelogin response\nCheck in browser: {}?{}".format(endpoint, urlencode(data)))
sam = xml.find('saml-auth-method') sam = xml.find('saml-auth-method')
sr = xml.find('saml-request') sr = xml.find('saml-request')
if sam is None or sr is None: if sam is None or sr is None:
p.error("This does not appear to be a SAML prelogin response (<saml-auth-method> or <saml-request> tags missing)\n" p.error("{} prelogin response does not contain SAML tags (<saml-auth-method> or <saml-request> missing)\n\n"
"Check in browser: {}".format(endpoint)) "Things to try:\n"
"1) Spoof an officially supported OS (e.g. --clientos=Windows or --clientos=Mac)\n"
"2) Check in browser: {}?{}".format(args.interface.title(), endpoint, urlencode(data)))
sam = sam.text sam = sam.text
sr = a2b_base64(sr.text).decode() sr = a2b_base64(sr.text).decode()
if sam == 'POST': if sam == 'POST':

View File

@ -1,7 +1,7 @@
#!/usr/bin/python3 #!/usr/bin/python3
from __future__ import print_function from __future__ import print_function
from sys import stderr, version_info from sys import stderr, version_info, platform
if (version_info >= (3, 0)): if (version_info >= (3, 0)):
from urllib.parse import urlparse, urlencode from urllib.parse import urlparse, urlencode
raw_input = input raw_input = input
@ -18,15 +18,19 @@ from binascii import a2b_base64
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
from shlex import quote from shlex import quote
clientos_map = dict(linux='Linux', darwin='Mac', win32='Windows', cygwin='Windows')
default_clientos = clientos_map.get(platform, 'Windows')
p = argparse.ArgumentParser() p = argparse.ArgumentParser()
p.add_argument('-v','--verbose', default=0, action='count') p.add_argument('-v','--verbose', default=0, action='count')
p.add_argument('endpoint', help='GlobalProtect server; can append /ssl-vpn/login.esp (default) or /global-protect/getconfig.esp') p.add_argument('endpoint', help='GlobalProtect server; can append /ssl-vpn/login.esp (default) or /global-protect/getconfig.esp or /{ssl-vpn,global-protect}/prelogin.esp')
p.add_argument('extra', nargs='*', help='Extra field to pass to include in the login query string (e.g. "portal-userauthcookie=deadbeef01234567")') p.add_argument('extra', nargs='*', help='Extra field to pass to include in the login query string (e.g. "portal-userauthcookie=deadbeef01234567")')
g = p.add_argument_group('Login credentials') g = p.add_argument_group('Login credentials')
g.add_argument('-u','--user', help='Username (will prompt if unspecified)') g.add_argument('-u','--user', help='Username (will prompt if unspecified)')
g.add_argument('-p','--password', help='Password (will prompt if unspecified)') g.add_argument('-p','--password', help='Password (will prompt if unspecified)')
g.add_argument('-c','--cert', help='PEM file containing client certificate (and optionally private key)') g.add_argument('-c','--cert', help='PEM file containing client certificate (and optionally private key)')
g.add_argument('--computer', default=os.uname()[1], help="Computer name (default is `hostname`)") g.add_argument('--computer', default=os.uname()[1], help="Computer name (default is `hostname`)")
g.add_argument('--clientos', choices=set(clientos_map.values()), default=default_clientos, help="clientos value to send (default is %(default)s)")
g.add_argument('--key', help='PEM file containing client private key (if not included in same file as certificate)') g.add_argument('--key', help='PEM file containing client private key (if not included in same file as certificate)')
p.add_argument('-b','--browse', action='store_true', help='Automatically spawn browser for SAML') p.add_argument('-b','--browse', action='store_true', help='Automatically spawn browser for SAML')
p.add_argument('--no-verify', dest='verify', action='store_false', default=True, help='Ignore invalid server certificate') p.add_argument('--no-verify', dest='verify', action='store_false', default=True, help='Ignore invalid server certificate')
@ -53,7 +57,13 @@ s.headers['User-Agent'] = 'PAN GlobalProtect'
s.cert = cert s.cert = cert
if prelogin: if prelogin:
data = extra data={
# sent by many clients but not known to have any effect
'tmp': 'tmp', 'clientVer': 4100, 'kerberos-support': 'yes', 'ipv6-support': 'yes',
# affects some clients' behavior (https://github.com/dlenski/gp-saml-gui/issues/6#issuecomment-599743060)
'clientos': args.clientos,
**extra
}
else: else:
# same request params work for /global-protect/getconfig.esp as for /ssl-vpn/login.esp # same request params work for /global-protect/getconfig.esp as for /ssl-vpn/login.esp
if args.user == None: if args.user == None: