From argel.ramirez at gmail.com  Mon Apr  2 21:40:20 2018
From: argel.ramirez at gmail.com (=?UTF-8?Q?Argel_Ram=C3=ADrez_Reyes?=)
Date: Mon, 2 Apr 2018 21:40:20 -0700
Subject: [Help] Openconnect working just fine on terminal but not on
 network-manager (Failed to find or parse web form in login page) Debian Buster
Message-ID: <CAFG-dfyu9dtN6DTgaAp2a_ww0-vapk4JJ2wrJiErwgZMYj6vcw@mail.gmail.com>

Hi, dear developers:
I'm using openconnect 7.08-3, network-manager-openconnect 1.2.4-1 on
Debian Linux (Buster) installed from Debian repositories.
I connect succesfully to  Pulse VPN using the command line:

>>>>openconnect -v  https://vpn.library.ucdavis.edu --juniper

This command asks for user and password and connects allright.

When trying to use the graphical interface, however, I get an error:

GET https://vpn.library.ucdavis.edu/
Intentando conectar al servidor 169.237.240.10:443
Conectado a 169.237.240.10:443
Negociaci?n SSL con ?vpn.library.ucdavis.edu?
Conect? a HTTPS en vpn.library.ucdavis.edu
Se obtuvo la respuesta HTTP: HTTP/1.1 302 Found
Location: https://vpn.library.ucdavis.edu/dana-na/auth/url_3/welcome.cgi
Content-Type: text/html; charset=utf-8
Set-Cookie: DSSIGNIN=url_3; path=/dana-na/; expires=Thu, 31-Dec-2037
00:00:00 GMT; secure
Set-Cookie: DSIVS=; path=/; expires=Thu, 01 Jan 1970 22:00:00 GMT; secure
Set-Cookie: DSSignInURL=/; path=/; secure
Connection: close
Content-Length: 0
Cuerpo HTTP length:  (0)
GET https://vpn.library.ucdavis.edu/dana-na/auth/url_3/welcome.cgi
Negociaci?n SSL con ?vpn.library.ucdavis.edu?
Conect? a HTTPS en vpn.library.ucdavis.edu
Se obtuvo la respuesta HTTP: HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Date: Tue, 03 Apr 2018 04:36:06 GMT
Connection: close
Pragma: no-cache
Cache-Control: no-store
Expires: -1
Strict-Transport-Security: max-age=31536000
X-Frame-Options: SAMEORIGIN
Cuerpo HTTP http 1.0 (-1)
Socket SSL cerrado no limpiamente
Fall? al encontrar o analizar el formulario web en la p?gina de inicio de sesi?n

(error message translates as (sorry for the spanish): Failed to find
or parse web form in login page)
I have seen some similar errors on the web but somehow for the users
it disappears on its own.
Could anyone here help me make this work or at least understanding
what is going on?

Thanks!



-- 
Argel Ram?rez R.


From maxcrees at me.com  Mon Apr  2 23:03:18 2018
From: maxcrees at me.com (Max Rees)
Date: Tue, 03 Apr 2018 02:03:18 -0400
Subject: [PATCH] Allow specifying server in configuration file
Message-ID: <20180403060317.GA31272@bebop>

This allows the configuration file to have an entry of the form:
server https://server[:port][/group]
similar to the CLI invocation.

Signed-off-by: Max Rees <maxcrees at me.com>
---
 main.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/main.c b/main.c
index d09efd5..23b0efc 100644
--- a/main.c
+++ b/main.c
@@ -187,6 +187,7 @@ enum {
 	OPT_HTTP_AUTH,
 	OPT_LOCAL_HOSTNAME,
 	OPT_PROTOCOL,
+	OPT_SERVER,
 	OPT_PASSTOS,
 };
 
@@ -269,6 +270,7 @@ static const struct option long_options[] = {
 	OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP),
 	OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST),
 	OPTION("protocol", 1, OPT_PROTOCOL),
+	OPTION("server", 1, OPT_SERVER),
 #ifdef OPENCONNECT_GNUTLS
 	OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG),
 #endif
@@ -1164,6 +1166,10 @@ int main(int argc, char **argv)
 			if (openconnect_set_protocol(vpninfo, config_arg))
 				exit(1);
 			break;
+		case OPT_SERVER:
+			if (openconnect_parse_url(vpninfo, config_arg))
+				exit(1);
+			break;
 		case OPT_JUNIPER:
 			fprintf(stderr, "WARNING: Juniper Network Connect support is experimental.\n");
 			fprintf(stderr, "It will probably be superseded by Junos Pulse support.\n");
@@ -1457,7 +1463,7 @@ int main(int argc, char **argv)
 	if (optind < argc - 1) {
 		fprintf(stderr, _("Too many arguments on command line\n"));
 		usage();
-	} else if (optind > argc - 1) {
+	} else if (optind > argc - 1 && !vpninfo->hostname) {
 		fprintf(stderr, _("No server specified\n"));
 		usage();
 	}
-- 
2.15.0



From dlenski at gmail.com  Tue Apr  3 11:29:00 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Tue, 3 Apr 2018 13:29:00 -0500
Subject: [PATCH] Allow specifying server in configuration file
In-Reply-To: <20180403060317.GA31272@bebop>
References: <20180403060317.GA31272@bebop>
Message-ID: <CAOw_LSExN2064EU3VJEVHODyjVPznxwbDz8K+ZHe-Z6=2MrFHw@mail.gmail.com>

On Tue, Apr 3, 2018 at 1:03 AM, Max Rees <maxcrees at me.com> wrote:
>
> This allows the configuration file to have an entry of the form:
> server https://server[:port][/group]
> similar to the CLI invocation.
>
> Signed-off-by: Max Rees <maxcrees at me.com>
> ---
>  main.c | 8 +++++++-
>  1 file changed, 7 insertions(+), 1 deletion(-)
>
> diff --git a/main.c b/main.c
> index d09efd5..23b0efc 100644
> --- a/main.c
> +++ b/main.c
> @@ -187,6 +187,7 @@ enum {
>         OPT_HTTP_AUTH,
>         OPT_LOCAL_HOSTNAME,
>         OPT_PROTOCOL,
> +       OPT_SERVER,
>         OPT_PASSTOS,
>  };
>
> @@ -269,6 +270,7 @@ static const struct option long_options[] = {
>         OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP),
>         OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST),
>         OPTION("protocol", 1, OPT_PROTOCOL),
> +       OPTION("server", 1, OPT_SERVER),
>  #ifdef OPENCONNECT_GNUTLS
>         OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG),
>  #endif
> @@ -1164,6 +1166,10 @@ int main(int argc, char **argv)
>                         if (openconnect_set_protocol(vpninfo, config_arg))
>                                 exit(1);
>                         break;
> +               case OPT_SERVER:
> +                       if (openconnect_parse_url(vpninfo, config_arg))
> +                               exit(1);
> +                       break;
>                 case OPT_JUNIPER:
>                         fprintf(stderr, "WARNING: Juniper Network Connect support is experimental.\n");
>                         fprintf(stderr, "It will probably be superseded by Junos Pulse support.\n");
> @@ -1457,7 +1463,7 @@ int main(int argc, char **argv)
>         if (optind < argc - 1) {

I think you should also check for the converse case of a
*double-specified* server (where the user specifies --server and an
argument with no option).
It'd be something like (optind < argc && vpninfo->hostname).

>                 fprintf(stderr, _("Too many arguments on command line\n"));
>                 usage();
> -       } else if (optind > argc - 1) {
> +       } else if (optind > argc - 1 && !vpninfo->hostname) {
>                 fprintf(stderr, _("No server specified\n"));
>                 usage();
>         }
> --
> 2.15.0
>
>
> _______________________________________________
> openconnect-devel mailing list
> openconnect-devel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/openconnect-devel


From maxcrees at me.com  Tue Apr  3 13:37:25 2018
From: maxcrees at me.com (Max Rees)
Date: Tue, 03 Apr 2018 16:37:25 -0400
Subject: [PATCH] Allow specifying server in configuration file
In-Reply-To: <CAOw_LSExN2064EU3VJEVHODyjVPznxwbDz8K+ZHe-Z6=2MrFHw@mail.gmail.com>
References: <20180403060317.GA31272@bebop>
 <CAOw_LSExN2064EU3VJEVHODyjVPznxwbDz8K+ZHe-Z6=2MrFHw@mail.gmail.com>
Message-ID: <20180403203724.GA19931@bebop>

On Apr 03 01:29 PM, Daniel Lenski wrote:
> I think you should also check for the converse case of a
> *double-specified* server (where the user specifies --server and an
> argument with no option).
> It'd be something like (optind < argc && vpninfo->hostname).

What should be the behavior in this situation? An error message or
should it accept one or the other as the server to use?

Max


From dlenski at gmail.com  Tue Apr  3 15:24:59 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Tue, 3 Apr 2018 17:24:59 -0500
Subject: [PATCH] Allow specifying server in configuration file
In-Reply-To: <20180403203724.GA19931@bebop>
References: <20180403060317.GA31272@bebop>
 <CAOw_LSExN2064EU3VJEVHODyjVPznxwbDz8K+ZHe-Z6=2MrFHw@mail.gmail.com>
 <20180403203724.GA19931@bebop>
Message-ID: <CAOw_LSEMK7hc3gsh9PzfpVj6bpaA=OzFEvuBwYFnjmqbuxaGdg@mail.gmail.com>

On Tue, Apr 3, 2018 at 3:37 PM, Max Rees <maxcrees at me.com> wrote:
> On Apr 03 01:29 PM, Daniel Lenski wrote:
>> I think you should also check for the converse case of a
>> *double-specified* server (where the user specifies --server and an
>> argument with no option).
>> It'd be something like (optind < argc && vpninfo->hostname).
>
> What should be the behavior in this situation? An error message or
> should it accept one or the other as the server to use?

I'd say it should be an error ("explicit is better than implicit"),
but BDFL David Woodhouse should tell me if I'm wrong. :-D

Dan


From dwmw2 at infradead.org  Tue Apr  3 16:10:52 2018
From: dwmw2 at infradead.org (David Woodhouse)
Date: Wed, 04 Apr 2018 00:10:52 +0100
Subject: [PATCH] Allow specifying server in configuration file
In-Reply-To: <CAOw_LSEMK7hc3gsh9PzfpVj6bpaA=OzFEvuBwYFnjmqbuxaGdg@mail.gmail.com>
References: <20180403060317.GA31272@bebop>
 <CAOw_LSExN2064EU3VJEVHODyjVPznxwbDz8K+ZHe-Z6=2MrFHw@mail.gmail.com>
 <20180403203724.GA19931@bebop>
 <CAOw_LSEMK7hc3gsh9PzfpVj6bpaA=OzFEvuBwYFnjmqbuxaGdg@mail.gmail.com>
Message-ID: <1522797052.3773.51.camel@infradead.org>

On Tue, 2018-04-03 at 17:24 -0500, Daniel Lenski wrote:
> On Tue, Apr 3, 2018 at 3:37 PM, Max Rees <maxcrees at me.com> wrote:
> > 
> > On Apr 03 01:29 PM, Daniel Lenski wrote:
> > > 
> > > I think you should also check for the converse case of a
> > > *double-specified* server (where the user specifies --server and
> > > an
> > > argument with no option).
> > > It'd be something like (optind < argc && vpninfo->hostname).
>
> > What should be the behavior in this situation? An error message or
> > should it accept one or the other as the server to use?
>
> I'd say it should be an error ("explicit is better than implicit"),
> but BDFL David Woodhouse should tell me if I'm wrong. :-D

I'd probably be inclined to suggest we should accept whatever comes
last. We don't reject multiple '--server xxx' options, do we? It allows
the value in the config file to be overridden on the command line.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 5213 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/openconnect-devel/attachments/20180404/e8ed5705/attachment.bin>

From maxcrees at me.com  Tue Apr  3 17:03:51 2018
From: maxcrees at me.com (Max Rees)
Date: Tue, 03 Apr 2018 20:03:51 -0400
Subject: [PATCH v2] Allow specifying server in configuration file
In-Reply-To: <1522797052.3773.51.camel@infradead.org>
References: <20180403060317.GA31272@bebop>
 <CAOw_LSExN2064EU3VJEVHODyjVPznxwbDz8K+ZHe-Z6=2MrFHw@mail.gmail.com>
 <20180403203724.GA19931@bebop>
 <CAOw_LSEMK7hc3gsh9PzfpVj6bpaA=OzFEvuBwYFnjmqbuxaGdg@mail.gmail.com>
 <1522797052.3773.51.camel@infradead.org>
Message-ID: <20180404000349.GB19931@bebop>

On Apr 04 12:10 AM, David Woodhouse wrote:
> I'd probably be inclined to suggest we should accept whatever comes
> last. We don't reject multiple '--server xxx' options, do we? It allows
> the value in the config file to be overridden on the command line.

Here is an updated version of the patch that always takes the last
argument without a corresponding --option (if there is such an argument
present) as the server URL regardless of any previous options.

Max

>From fa9c058ed259e2d76fe35c993b155e6bd6c49f50 Mon Sep 17 00:00:00 2001
From: Max Rees <maxcrees at me.com>
Date: Mon, 2 Apr 2018 01:28:16 -0400
Subject: [PATCH] Allow specifying server in configuration file

This allows the configuration file to have an entry of the form:
server https://server[:port][/group]
similar to the CLI invocation.

Signed-off-by: Max Rees <maxcrees at me.com>
---
 main.c | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/main.c b/main.c
index d09efd5..7d41811 100644
--- a/main.c
+++ b/main.c
@@ -187,6 +187,7 @@ enum {
 	OPT_HTTP_AUTH,
 	OPT_LOCAL_HOSTNAME,
 	OPT_PROTOCOL,
+	OPT_SERVER,
 	OPT_PASSTOS,
 };
 
@@ -269,6 +270,7 @@ static const struct option long_options[] = {
 	OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP),
 	OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST),
 	OPTION("protocol", 1, OPT_PROTOCOL),
+	OPTION("server", 1, OPT_SERVER),
 #ifdef OPENCONNECT_GNUTLS
 	OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG),
 #endif
@@ -1164,6 +1166,10 @@ int main(int argc, char **argv)
 			if (openconnect_set_protocol(vpninfo, config_arg))
 				exit(1);
 			break;
+		case OPT_SERVER:
+			if (openconnect_parse_url(vpninfo, config_arg))
+				exit(1);
+			break;
 		case OPT_JUNIPER:
 			fprintf(stderr, "WARNING: Juniper Network Connect support is experimental.\n");
 			fprintf(stderr, "It will probably be superseded by Junos Pulse support.\n");
@@ -1457,7 +1463,7 @@ int main(int argc, char **argv)
 	if (optind < argc - 1) {
 		fprintf(stderr, _("Too many arguments on command line\n"));
 		usage();
-	} else if (optind > argc - 1) {
+	} else if (optind > argc - 1 && !vpninfo->hostname) {
 		fprintf(stderr, _("No server specified\n"));
 		usage();
 	}
@@ -1513,7 +1519,10 @@ int main(int argc, char **argv)
 	if (config_lookup_host(vpninfo, argv[optind]))
 		exit(1);
 
-	if (!vpninfo->hostname) {
+	/* The last argument without a corresponding --option is taken
+	 * to be the server URL and overrides any --server option on the
+	 * command line or from a --config */
+	if (!vpninfo->hostname || optind < argc) {
 		char *url = strdup(argv[optind]);
 
 		if (openconnect_parse_url(vpninfo, url))
-- 
2.15.0



From tyson at ninjut.su  Wed Apr  4 09:46:14 2018
From: tyson at ninjut.su (Tyson Vinson)
Date: Wed, 4 Apr 2018 10:46:14 -0600
Subject: [HELP] Option for token-field
Message-ID: <CAGRZ_M2Ew6xzer1x+gsxPg+NQrU5Wq1TyQ97y8fJnLPTx665PA@mail.gmail.com>

I'm connecting to a VPN endpoint that uses a non-standard field name
for the oath/totp challenge. There was a set of patches to openconnect
here http://lists.infradead.org/pipermail/openconnect-devel/2015-December/003330.html
that add a token-field option. These still apply cleanly and the patch
resolves my issue replying with a token. Can this patchset be applied
to HEAD? If there was some reason this wasn't merged I'd be happy to
help get it in to a state where it can be.

Here's the challenge, dumped with --dump-http-traffic, I'm seeing:

Got HTTP response: HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Transfer-Encoding: chunked
Cache-Control: no-cache
Pragma: no-cache
Connection: Keep-Alive
Date: **redacted**
X-Frame-Options: SAMEORIGIN
X-Aggregate-Auth: 1
HTTP body chunked (-2)
< <?xml version="1.0" encoding="UTF-8"?>
< <config-auth client="vpn" type="auth-request" aggregate-auth-version="2">
< <opaque is-for="sg">
< <tunnel-group>**redacted**</tunnel-group>
< <auth-handle>**redacted**</auth-handle>
< <group-alias>**redacted**</group-alias>
< <config-hash>**redacted**</config-hash>
< </opaque>
< <auth id="challenge">
< <title>Login</title>
< <message id="2" param1="Enter your security code:" param2="">%s</message>
< <form>
< <input type="password" name="answer" label="Response:"></input>
< <input type="submit" name="Continue" label="continue"></input>
< </form>
< </auth>
< </config-auth>

- Tyson


From dlenski at gmail.com  Thu Apr  5 07:10:05 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 5 Apr 2018 09:10:05 -0500
Subject: [HELP] Option for token-field
In-Reply-To: <CAGRZ_M2Ew6xzer1x+gsxPg+NQrU5Wq1TyQ97y8fJnLPTx665PA@mail.gmail.com>
References: <CAGRZ_M2Ew6xzer1x+gsxPg+NQrU5Wq1TyQ97y8fJnLPTx665PA@mail.gmail.com>
Message-ID: <CAOw_LSH=qXesHb4d=zZswu0_rdHtc6=W-24-yOG652u7BrKVTA@mail.gmail.com>

On Wed, Apr 4, 2018 at 11:46 AM, Tyson Vinson <tyson at ninjut.su> wrote:
> I'm connecting to a VPN endpoint that uses a non-standard field name
> for the oath/totp challenge. There was a set of patches to openconnect
> here http://lists.infradead.org/pipermail/openconnect-devel/2015-December/003330.html
> that add a token-field option. These still apply cleanly and the patch
> resolves my issue replying with a token. Can this patchset be applied
> to HEAD? If there was some reason this wasn't merged I'd be happy to
> help get it in to a state where it can be.

I'm glad that patch is useful.

If I remember right, there was some follow-up discussion much later in
which David Woodhouse pointed out that my approach would be hard to
integrate correctly with GUI front-ends, or with multiple sequential
login forms (as Juniper VPNs often use?)

Dan


From chelapa at hotmail.com  Fri Apr  6 09:27:34 2018
From: chelapa at hotmail.com (Luis l)
Date: Fri, 6 Apr 2018 16:27:34 +0000
Subject: Openconnect - Palo Alto - Okta SSO / MFA
Message-ID: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>

Hi Guys, I am using the latests version of OC w/ Palo Alto VPN and Okta as the IDP / MFA. Using NON mfa/okta the process works and connects but when using okta it does not prompt me for the MFA key. Gives a error of


HTTP body length:  (128)
Unexpected 512 result from server
Invalid username or password.


protocol used is gp and i saw this post but no results

https://github.com/dlenski/openconnect/issues/57

Ubuntu LTS 14.01

openconnect v7.08-274-gabb4ef3




From dlenski at gmail.com  Fri Apr  6 09:54:35 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Fri, 6 Apr 2018 11:54:35 -0500
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
Message-ID: <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>

On Fri, Apr 6, 2018 at 11:27 AM, Luis l <chelapa at hotmail.com> wrote:
> Hi Guys, I am using the latests version of OC w/ Palo Alto VPN ?

As explained on the page for the fork with PAN GlobalProtect support
(https://github.com/dlenski/openconnect#feedback-and-troubleshooting),
you should report problems which are specific to PAN-GP as a new issue
on Github, rather than on this mailing list. GlobalProtect support is
not yet part of the official OpenConnect.

> ? and Okta as the IDP / MFA. Using NON mfa/okta the process works and connects but when using okta it does not prompt me for the MFA key.

I have absolutely zero idea what Okta or IDP are. I think you're
saying that with single-factor authentication (username and password)
it works fine, but with multi-factor authentication it doesn't. Is
that correct?

> Gives a error of
>
>
> HTTP body length: (128)
> Unexpected 512 result from server
> Invalid username or password.
>
>
> protocol used is gp and i saw this post but no results
>
> https://github.com/dlenski/openconnect/issues/57
>
> Ubuntu LTS 14.01
>
> openconnect v7.08-274-gabb4ef3

You're using a recent build which does include challenge-based
multi-factor authentication support. Good.

Without more information, it's impossible to diagnose this. You should
file a new issue in Github, and be sure to include the debug logs as I
request in the template.

Dan


From dwmw2 at infradead.org  Fri Apr  6 11:23:51 2018
From: dwmw2 at infradead.org (David Woodhouse)
Date: Fri, 06 Apr 2018 19:23:51 +0100
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
Message-ID: <1523039030.7862.254.camel@infradead.org>



On Fri, 2018-04-06 at 11:54 -0500, Daniel Lenski wrote:
> On Fri, Apr 6, 2018 at 11:27 AM, Luis l <chelapa at hotmail.com> wrote:
> > Hi Guys, I am using the latests version of OC w/ Palo Alto VPN ?
> 
> As explained on the page for the fork with PAN GlobalProtect support
> (https://github.com/dlenski/openconnect#feedback-and-troubleshooting),
> you should report problems which are specific to PAN-GP as a new issue
> on Github, rather than on this mailing list. GlobalProtect support is
> not yet part of the official OpenConnect.

FWIW I have no objection to using the mailing list for it even when it
isn't merged yet. Where *are* we with merging it? I did some heckling
at the last round of patches as there was some string allocation
confusion, and it looked like it hadn't been run in valgrind. Did you
give me another set after that?
-------------- next part --------------
A non-text attachment was scrubbed...
Name: smime.p7s
Type: application/x-pkcs7-signature
Size: 5213 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/openconnect-devel/attachments/20180406/b4774eb2/attachment.bin>

From keekychen at gmail.com  Mon Apr  9 01:37:17 2018
From: keekychen at gmail.com (Kee K Y CHEN)
Date: Mon, 9 Apr 2018 16:37:17 +0800
Subject: no-route a.k.a X-CSTP-Split-Exclude configure on ocserv not working
 with openconnect cli/gui
Message-ID: <CA+pj79UhjGSoto-7opnNE45FD5T0Gpab5RY48+ideJUQp0c83A@mail.gmail.com>

Dear Author:

recently I met one problem: no-route a.k.a X-CSTP-Split-Exclude
configure on ocserv not working with openconnect in both gui and cli

My Enviroment:
Client:
Windows 7 64-bit, OpenConnect-GUI version is 1.5.{1,2,3},  with
OpenConnect version v7.08
Server:
ocserv 0.10.11/ocserv 0.11.8
    with Cisco compatible mode enabled

In ocserv's configuration, the ?no-route? command defined which IP
segment will go from local gateway instead of VPN gateway(split
tunnels client side).
the ocserv configure file may looks like:

...
"no-route = 1.1.0.0/255.255.0.0"
"no-route = 2.2.0.0/255.255.0.0"
...


Normally, after user dialed vpn, the vpn client will trigger to
rewrite local host route(s) point to local gateway based on ?no-route?
configuration as split tunnels.

However this is feature not working on OpenConnect, but works on Cisco
anyconnect clients.

In Log, I can see info from server do pushed to local

2018-03-27 12:59:05 | c74 | X-CSTP-Split-Exclude: 1.1.0.0/255.255.0.0
2018-03-27 12:59:05 | c74 | X-CSTP-Split-Exclude: 2.2.0.0/255.255.0.0

But no local host "routing set command such as route add/delete" been
found in OpenConnect log for above two subnet.

?cmd -> route print? also confirm only 0.0.0.0 with smaller metric in
local routing table but no detailed split routing entries.

Network Destination Netmask Gateway Interface Metric
0.0.0.0 0.0.0.0 192.168.1.1 192.168.1.28     21      <- local Lan
0.0.0.0 0.0.0.0 172.16.2.1 172.16.2.66          2      <- VPN net with
lower metric

Do you mind to check why X-CSTP-Split-Exclude not triggering local
routing commands rewrite local routing table?

Thank you.


From dlenski at gmail.com  Mon Apr  9 20:37:10 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Mon, 9 Apr 2018 20:37:10 -0700
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <1523039030.7862.254.camel@infradead.org>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
Message-ID: <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>

On Apr 6, 2018 2:23 PM, "David Woodhouse" <dwmw2 at infradead.org> wrote:
>On Fri, 2018-04-06 at 11:54 -0500, Daniel Lenski wrote:
>> On Fri, Apr 6, 2018 at 11:27 AM, Luis l <chelapa at hotmail.com> wrote:
>> > Hi Guys, I am using the latests version of OC w/ Palo Alto VPN ?
>>
>> As explained on the page for the fork with PAN GlobalProtect support
>> (https://github.com/dlenski/openconnect#feedback-and-troubleshooting),
>> you should report problems which are specific to PAN-GP as a new issue
>> on Github, rather than on this mailing list. GlobalProtect support is
>> not yet part of the official OpenConnect.
>
> FWIW I have no objection to using the mailing list for it even when it
> isn't merged yet.

Great, okay! I think I added that admonition on the Github project
README when it was at a much less functional state.

> Where *are* we with merging it?

I gave you another round of cleaned-up-and-rebased patches on March 4,
and one more patch on top on March 27 (for tolerance of oversize ESP
packets, in the same vein as previous patches for tolerating oversize
ONCP and GPST packets).

> I did some heckling
> at the last round of patches as there was some string allocation
> confusion, and it looked like it hadn't been run in valgrind. Did you
> give me another set after that?

valgrind credibly accuses me of a lengthy of memory-allocation crimes.
I haven't fixed them all yet. :-(

Do you have a preferred invocation for valgrind'ing openconnect, by
the way? To test the GP protocol, I've been using variants of this:

    valgrind --tool=memcheck --leak-check=full
--log-file=/tmp/valgrind.log -v $OPENCONNECT_BIN
--protocol=globalprotect -u $USERNAME --csd-wrapper ./hipreport.sh
$SERVER

Thanks,
Dan


From n.mavrogiannopoulos at gmail.com  Tue Apr 10 06:46:34 2018
From: n.mavrogiannopoulos at gmail.com (Nikos Mavrogiannopoulos)
Date: Tue, 10 Apr 2018 15:46:34 +0200
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
 <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
Message-ID: <CAJU7zaL=F+9PNDV7iG4XJBTmwkjbs236YVORxOCJqiASGdr9OQ@mail.gmail.com>

On Tue, Apr 10, 2018 at 5:37 AM, Daniel Lenski <dlenski at gmail.com> wrote:
> On Apr 6, 2018 2:23 PM, "David Woodhouse" <dwmw2 at infradead.org> wrote:
>>On Fri, 2018-04-06 at 11:54 -0500, Daniel Lenski wrote:
>>> On Fri, Apr 6, 2018 at 11:27 AM, Luis l <chelapa at hotmail.com> wrote:
>>> > Hi Guys, I am using the latests version of OC w/ Palo Alto VPN ?
>>>
>>> As explained on the page for the fork with PAN GlobalProtect support
>>> (https://github.com/dlenski/openconnect#feedback-and-troubleshooting),
>>> you should report problems which are specific to PAN-GP as a new issue
>>> on Github, rather than on this mailing list. GlobalProtect support is
>>> not yet part of the official OpenConnect.
>>
>> FWIW I have no objection to using the mailing list for it even when it
>> isn't merged yet.
>
> Great, okay! I think I added that admonition on the Github project
> README when it was at a much less functional state.
>
>> Where *are* we with merging it?
>
> I gave you another round of cleaned-up-and-rebased patches on March 4,
> and one more patch on top on March 27 (for tolerance of oversize ESP
> packets, in the same vein as previous patches for tolerating oversize
> ONCP and GPST packets).
>
>> I did some heckling
>> at the last round of patches as there was some string allocation
>> confusion, and it looked like it hadn't been run in valgrind. Did you
>> give me another set after that?
>
> valgrind credibly accuses me of a lengthy of memory-allocation crimes.
> I haven't fixed them all yet. :-(
>
> Do you have a preferred invocation for valgrind'ing openconnect, by
> the way? To test the GP protocol, I've been using variants of this:

Not sure about valgrind, but if you want to run the testsuite of the
openconnect, you can fork the project from gitlab, and check the
existing tests + your tests outputs on the pipelines:
https://gitlab.com/ocserv/openconnect

regards,
Nikos


From dopey at moonteeth.com  Tue Apr 10 14:26:20 2018
From: dopey at moonteeth.com (Andy Wang)
Date: Tue, 10 Apr 2018 16:26:20 -0500
Subject: [PATCH] Fix stoken support for Juniper VPN
In-Reply-To: <CAGjaHhtsw_TRowBp9FwfAvb6QYp531nYg4aHVX8BHnSJJLpv5A@mail.gmail.com>
References: <20171123023356.31450-1-dopey@moonteeth.com>
 <CAGjaHhtsw_TRowBp9FwfAvb6QYp531nYg4aHVX8BHnSJJLpv5A@mail.gmail.com>
Message-ID: <CAGjaHhv2HnTAdvdW6R-+AW_AEvEa4vfZdJH0T7Fff=Zss4MKng@mail.gmail.com>

Another follow up on this.  Is there something I should change to get
this considered for inclusion?

Thanks,
Andy

On Wed, Dec 20, 2017 at 9:05 PM, Andy Wang <dopey at moonteeth.com> wrote:
> Any thoughts on this?  Something I should do different?
>
> Thanks,
> Andy
>
> On Wed, Nov 22, 2017 at 8:33 PM, Andy Wang <dopey at moonteeth.com> wrote:
>> Allow using stoken code for frmLogin form type.
>> Ensure stoken seed is properly prepared using block copied from Cisco
>> VPN support in auth.c
>>
>> Signed-off-by: Andy Wang <dopey at moonteeth.com>
>> ---
>>  auth-juniper.c | 11 ++++++++++-
>>  1 file changed, 10 insertions(+), 1 deletion(-)
>>
>> diff --git a/auth-juniper.c b/auth-juniper.c
>> index 4b889d6..d818cf3 100644
>> --- a/auth-juniper.c
>> +++ b/auth-juniper.c
>> @@ -77,7 +77,8 @@ static int oncp_can_gen_tokencode(struct openconnect_info *vpninfo,
>>
>>         if (strcmp(form->auth_id, "frmDefender") &&
>>             strcmp(form->auth_id, "frmNextToken") &&
>> -           strcmp(form->auth_id, "ftmTotpToken"))
>> +           strcmp(form->auth_id, "ftmTotpToken") &&
>> +           strcmp(form->auth_id, "frmLogin"))
>>                 return -EINVAL;
>>
>>         return can_gen_tokencode(vpninfo, form, opt);
>> @@ -570,6 +571,14 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo)
>>         char *form_id = NULL;
>>         int try_tncc = !!vpninfo->csd_wrapper;
>>
>> +#ifdef HAVE_LIBSTOKEN
>> +        if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
>> +                ret = prepare_stoken(vpninfo);
>> +                if (ret)
>> +                        goto out;
>> +        }
>> +#endif
>> +
>>         resp_buf = buf_alloc();
>>         if (buf_error(resp_buf))
>>                 return -ENOMEM;
>> --
>> 2.14.3
>>


From dlenski at gmail.com  Tue Apr 10 14:52:58 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Tue, 10 Apr 2018 14:52:58 -0700
Subject: [PATCH] Fix stoken support for Juniper VPN
In-Reply-To: <CAGjaHhv2HnTAdvdW6R-+AW_AEvEa4vfZdJH0T7Fff=Zss4MKng@mail.gmail.com>
References: <20171123023356.31450-1-dopey@moonteeth.com>
 <CAGjaHhtsw_TRowBp9FwfAvb6QYp531nYg4aHVX8BHnSJJLpv5A@mail.gmail.com>
 <CAGjaHhv2HnTAdvdW6R-+AW_AEvEa4vfZdJH0T7Fff=Zss4MKng@mail.gmail.com>
Message-ID: <CAOw_LSEL1zdC8KrwAnvvHXcdxyfEzOsySsy4jALXDoWi3oEcXw@mail.gmail.com>

On Tue, Apr 10, 2018 at 2:26 PM, Andy Wang <dopey at moonteeth.com> wrote:
>
> Another follow up on this.  Is there something I should change to get
> this considered for inclusion?

I don't know if there are any developers who have access to a Juniper
VPN with RSA soft-token in order to test this but?

> On Wed, Dec 20, 2017 at 9:05 PM, Andy Wang <dopey at moonteeth.com> wrote:
> > On Wed, Nov 22, 2017 at 8:33 PM, Andy Wang <dopey at moonteeth.com> wrote:
> >> Allow using stoken code for frmLogin form type.
> >> Ensure stoken seed is properly prepared using block copied from Cisco
> >> VPN support in auth.c
> >>
> >> Signed-off-by: Andy Wang <dopey at moonteeth.com>
> >> ---
> >>  auth-juniper.c | 11 ++++++++++-
> >>  1 file changed, 10 insertions(+), 1 deletion(-)
> >>
> >> diff --git a/auth-juniper.c b/auth-juniper.c
> >> index 4b889d6..d818cf3 100644
> >> --- a/auth-juniper.c
> >> +++ b/auth-juniper.c
> >> @@ -77,7 +77,8 @@ static int oncp_can_gen_tokencode(struct openconnect_info *vpninfo,
> >>
> >>         if (strcmp(form->auth_id, "frmDefender") &&
> >>             strcmp(form->auth_id, "frmNextToken") &&
> >> -           strcmp(form->auth_id, "ftmTotpToken"))
> >> +           strcmp(form->auth_id, "ftmTotpToken") &&
> >> +           strcmp(form->auth_id, "frmLogin"))
> >>                 return -EINVAL;
> >>
> >>         return can_gen_tokencode(vpninfo, form, opt);

The concern here would be the frmLogin is the "default" login form for
Juniper. If there's a form field in frmLogin with type="password" ?
how can openconnect distinguish whether this is the token field, or
the "normal" password field?

I believe most Juniper VPNs using RSA/stoken would use the token in a
secondary login form, with one of the form names that are already in
the source code.

There are a few reports on the mailing list of VPNs like yours where
the token-code goes in the "primary" password field, rather than a
specifically-identified "secondary" token field. No solution
implemented yet, but see David's proposal for a more general-purpose
solution here:
http://lists.infradead.org/pipermail/openconnect-devel/2017-August/004450.html

> >> @@ -570,6 +571,14 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo)
> >>         char *form_id = NULL;
> >>         int try_tncc = !!vpninfo->csd_wrapper;
> >>
> >> +#ifdef HAVE_LIBSTOKEN
> >> +        if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
> >> +                ret = prepare_stoken(vpninfo);
> >> +                if (ret)
> >> +                        goto out;
> >> +        }
> >> +#endif
> >> +
> >>         resp_buf = buf_alloc();
> >>         if (buf_error(resp_buf))
> >>                 return -ENOMEM;
> >> --
> >> 2.14.3
> >>

This one seems like a simple oversight that would be required for
_any_ Juniper VPN to use stoken correctly. Thumbs up.

Dan


From dlenski at gmail.com  Tue Apr 10 20:03:46 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Tue, 10 Apr 2018 20:03:46 -0700
Subject: no-route a.k.a X-CSTP-Split-Exclude configure on ocserv not
 working with openconnect cli/gui
In-Reply-To: <CA+pj79UhjGSoto-7opnNE45FD5T0Gpab5RY48+ideJUQp0c83A@mail.gmail.com>
References: <CA+pj79UhjGSoto-7opnNE45FD5T0Gpab5RY48+ideJUQp0c83A@mail.gmail.com>
Message-ID: <CAOw_LSHGbW0AeMpjdbQ0SJbGPCfTZKWe3roDGo_cShON4B-pVQ@mail.gmail.com>

On Mon, Apr 9, 2018 at 1:37 AM, Kee K Y CHEN <keekychen at gmail.com> wrote:
> Do you mind to check why X-CSTP-Split-Exclude not triggering local
> routing commands rewrite local routing table?

openconnect can read split-exclude subnets for all supported
protocols, and provides them to the routing configuration script
("vpnc-script") in the CISCO_SPLIT_EXC environment variables.

However, the standard vpnc-script script does *not* actually handle
split-exclude variables in any way. Figuring out how to handle
"split-excludes" in a general and cross-platform way is pretty tricky.
See the patches on this thread for more details:

    http://lists.infradead.org/pipermail/openconnect-devel/2017-October/004530.html

You might want to test and give feedback on those patches if they work for you.

Dan


From chelapa at hotmail.com  Wed Apr 11 14:25:23 2018
From: chelapa at hotmail.com (Luis l)
Date: Wed, 11 Apr 2018 21:25:23 +0000
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>,
 <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
Message-ID: <BN3PR0401MB1496AE20ABFE4C030CC2B7DEBDBD0@BN3PR0401MB1496.namprd04.prod.outlook.com>

Thank you guys, I wasnt sure where to post it so any guidance would help. 


So yes Okta / IDP = SSO = Multifactor Auth doesnt work


I saw that in the link i pasted they get presented with it, but if its still not an official release to OC then i will either wait or find another way for linux users to connect to vpn. which sucks bc i would rather use OC. Let me know what info is needed to maybe get this working. 


thank you!




From: Daniel Lenski <dlenski at gmail.com>
Sent: Tuesday, April 10, 2018 3:37 AM
To: David Woodhouse
Cc: Luis l; openconnect-devel
Subject: Re: Openconnect - Palo Alto - Okta SSO / MFA
? 

On Apr 6, 2018 2:23 PM, "David Woodhouse" <dwmw2 at infradead.org> wrote:
>On Fri, 2018-04-06 at 11:54 -0500, Daniel Lenski wrote:
>> On Fri, Apr 6, 2018 at 11:27 AM, Luis l <chelapa at hotmail.com> wrote:
>> > Hi Guys, I am using the latests version of OC w/ Palo Alto VPN ?
>>
>> As explained on the page for the fork with PAN GlobalProtect support
>> (https://github.com/dlenski/openconnect#feedback-and-troubleshooting),

 https://avatars2.githubusercontent.com/u/128716?s=400&v=4 

GitHub - dlenski/openconnect: OpenConnect client extended ...
github.com
This is a modified version of the fantastic open-source VPN client OpenConnect which supports the PAN GlobalProtect VPN in its native modes (SSL and ESP)?with no assistance or cooperation needed from your VPN administrators. This is a work in progress. That said, I've been using it for real work ...

>> you should report problems which are specific to PAN-GP as a new issue
>> on Github, rather than on this mailing list. GlobalProtect support is
>> not yet part of the official OpenConnect.
>
> FWIW I have no objection to using the mailing list for it even when it
> isn't merged yet.

Great, okay! I think I added that admonition on the Github project
README when it was at a much less functional state.

> Where *are* we with merging it?

I gave you another round of cleaned-up-and-rebased patches on March 4,
and one more patch on top on March 27 (for tolerance of oversize ESP
packets, in the same vein as previous patches for tolerating oversize
ONCP and GPST packets).

> I did some heckling
> at the last round of patches as there was some string allocation
> confusion, and it looked like it hadn't been run in valgrind. Did you
> give me another set after that?

valgrind credibly accuses me of a lengthy of memory-allocation crimes.
I haven't fixed them all yet. :-(

Do you have a preferred invocation for valgrind'ing openconnect, by
the way? To test the GP protocol, I've been using variants of this:

??? valgrind --tool=memcheck --leak-check=full
--log-file=/tmp/valgrind.log -v $OPENCONNECT_BIN
--protocol=globalprotect -u $USERNAME --csd-wrapper ./hipreport.sh
$SERVER

Thanks,
Dan
    

From dlenski at gmail.com  Wed Apr 11 23:52:17 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Wed, 11 Apr 2018 23:52:17 -0700
Subject: [PATCH 2/5] add PAN GlobalProtect protocol support (HTTPS tunnel
 only)
In-Reply-To: <1520415242.31298.113.camel@infradead.org>
References: <1520155921-13047-1-git-send-email-dlenski@gmail.com>
 <1520155921-13047-3-git-send-email-dlenski@gmail.com>
 <1520329252.31298.8.camel@infradead.org>
 <CAOw_LSHnyEadO5_bSdYHnOw-8Av8J+7mSTs1zoSnQ_Se=rR+8g@mail.gmail.com>
 <1520415242.31298.113.camel@infradead.org>
Message-ID: <CAOw_LSHxawDQ81X5e1P-MQ0bd_6w51W7fbD1FqNvcpuD5Bv5vw@mail.gmail.com>

On Wed, Mar 7, 2018 at 11:34 AM, David Woodhouse <dwmw2 at infradead.org> wrote:
>
>
> On Wed, 2018-03-07 at 10:01 +0200, Daniel Lenski wrote:
>> What do you prefer? Refactoring the two versions of xmlnode_get_text()
>> down to a single function, renaming the gpst.c version, something
>> else??
>
> Don't know... one option is to ditch it entirely. Some of those cases
> where you're just using atoi(s); free(s); after it might be better done
> with xmlnode_is_named() and atoi(xml_node->content) perhaps?
>
> ISTR you saying that referencing node->content doesn't work? I don't
> recall the details...

Right, it doesn't work.
https://stackoverflow.com/questions/10363380/libxml2-can%C2%B4t-get-content-from-node

>> > > +/* We behave like CSTP ? create a linked list in vpninfo->cstp_options
>> > > + * with the strings containing the information we got from the server,
>> > > + * and oc_ip_info contains const copies of those pointers.
>> > > + *
>> > > + * (unlike version in oncp.c, val is stolen rather than strdup'ed) */
>> > Er...
>> >
>> > >
>> > > +
>> > > +static const char *add_option(struct openconnect_info *vpninfo, const char *opt, const char *val)
>> > .... make it 'char *val' then, if it's stolen...
>> >
>> > >
>> > > +{
>> > > +       struct oc_vpn_option *new = malloc(sizeof(*new));
>> > > +       if (!new)
>> > > +               return NULL;
>> > > +
>> > > +       new->option = strdup(opt);
>> > > +       if (!new->option) {
>> > > +               free(new);
>> > > +               return NULL;
>> > > +       }
>> > > +       new->value = strdup(val);
>> >
>> > ... but I still see a strdup(). And should it *free* 'val' in the error
>> > patch if it was going to take ownership of it?
>>
>> Comment is straight-up wrong and obsolete. We changed this in a
>> previous iteration of the patches. Good catch.
>
> But then you are *using* add_option as the comment describes it:
>
>         for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) {
>                 if (!xmlnode_get_text(xml_node, "ip-address", &s))
>                         vpninfo->ip_info.addr = add_option(vpninfo, "ipaddr", s);
>
> That add_option() call *is* assuming that the 's' value will be stolen, surely?

Indeed you're right. Current version is leaking memory every time I do
this and then don't free(s) ? :facepalm:

This botched add_option() is the source of the memory leaks in the GP
version, as I'm now clearly seeing with valgrind. Will resubmit the patch.

Thanks,
dan


From dlenski at gmail.com  Wed Apr 11 23:58:01 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Wed, 11 Apr 2018 23:58:01 -0700
Subject: [PATCH 2/5 v2] add PAN GlobalProtect protocol support (HTTPS tunnel
 only)
In-Reply-To: <1520155921-13047-3-git-send-email-dlenski@gmail.com>
Message-ID: <1523516281-32040-1-git-send-email-dlenski@gmail.com>

This includes support for generating an xmlconfig so that NetworkManager can
list all the gateway servers, when connecting to a GlobalProtect portal (not
gateway):

GlobalProtect distinguishes "portal" and "gateway" servers.  Often the same
server supports both (/global-protect URLs are for the portal, /ssl-vpn URLs
are for the gateway).  The official clients always connect through the
portal.  Mostly, the portal configuration is not useful for OpenConnect; it
restricts the behavior of the official clients.

However, the portal configuration does contain a list of allowed gateways
(just as AnyConnect VPNs can list other servers).  We therefore generate an
xmlconfig in the same format as AnyConnect VPNs, so that the NetworkManager
plugins can list all the supported gateways.

Signed-off-by: Daniel Lenski <dlenski at gmail.com>
Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 Makefile.am             |   5 +-
 auth-globalprotect.c    | 468 ++++++++++++++++++++++++++++
 gpst.c                  | 802 ++++++++++++++++++++++++++++++++++++++++++++++++
 http.c                  |  22 +-
 library.c               |  10 +
 openconnect-internal.h  |  17 +
 openconnect.8.in        |   7 +-
 www/Makefile.am         |   2 +-
 www/globalprotect.xml   |  64 ++++
 www/mail.xml            |   4 +-
 www/menu2-protocols.xml |   1 +
 11 files changed, 1394 insertions(+), 8 deletions(-)
 create mode 100644 auth-globalprotect.c
 create mode 100644 gpst.c
 create mode 100644 www/globalprotect.xml

diff --git a/Makefile.am b/Makefile.am
index d3ae26a..fc36c59 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,6 +30,7 @@ endif
 library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c
 lib_srcs_cisco = auth.c cstp.c
 lib_srcs_juniper = oncp.c lzo.c auth-juniper.c
+lib_srcs_globalprotect = gpst.c auth-globalprotect.c
 lib_srcs_gnutls = gnutls.c gnutls_tpm.c
 lib_srcs_openssl = openssl.c openssl-pkcs11.c
 lib_srcs_win32 = tun-win32.c sspi.c
@@ -42,14 +43,14 @@ lib_srcs_stoken = stoken.c
 lib_srcs_esp = esp.c esp-seqno.c
 lib_srcs_dtls = dtls.c
 
-POTFILES = $(openconnect_SOURCES) $(lib_srcs_cisco) $(lib_srcs_juniper) \
+POTFILES = $(openconnect_SOURCES) $(lib_srcs_cisco) $(lib_srcs_juniper) $(lib_srcs_globalprotect) \
 	   gnutls-esp.c gnutls-dtls.c openssl-esp.c openssl-dtls.c \
 	   $(lib_srcs_esp) $(lib_srcs_dtls) \
 	   $(lib_srcs_openssl) $(lib_srcs_gnutls) $(library_srcs) \
 	   $(lib_srcs_win32) $(lib_srcs_posix) $(lib_srcs_gssapi) $(lib_srcs_iconv) \
 	   $(lib_srcs_oath) $(lib_srcs_yubikey) $(lib_srcs_stoken) openconnect-internal.h
 
-library_srcs += $(lib_srcs_juniper) $(lib_srcs_cisco) $(lib_srcs_oath)
+library_srcs += $(lib_srcs_juniper) $(lib_srcs_cisco) $(lib_srcs_oath) $(lib_srcs_globalprotect)
 if OPENCONNECT_LIBPCSCLITE
 library_srcs += $(lib_srcs_yubikey)
 endif
diff --git a/auth-globalprotect.c b/auth-globalprotect.c
new file mode 100644
index 0000000..a32dedb
--- /dev/null
+++ b/auth-globalprotect.c
@@ -0,0 +1,468 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright ? 2016-2017 Daniel Lenski
+ *
+ * Author: Dan Lenski <dlenski at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <config.h>
+
+#include <errno.h>
+
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#include "openconnect-internal.h"
+
+void gpst_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf)
+{
+	char *orig_ua = vpninfo->useragent;
+	vpninfo->useragent = (char *)"PAN GlobalProtect";
+
+	http_common_headers(vpninfo, buf);
+
+	vpninfo->useragent = orig_ua;
+}
+
+/* our "auth form" always has a username and password or challenge */
+static struct oc_auth_form *auth_form(struct openconnect_info *vpninfo,
+				      const char *prompt, const char *auth_id)
+{
+	struct oc_auth_form *form;
+	struct oc_form_opt *opt, *opt2;
+
+	form = calloc(1, sizeof(*form));
+	if (!form)
+		return NULL;
+
+	if (prompt)
+		form->message = strdup(prompt);
+
+	form->auth_id = strdup(auth_id ? : "_gateway");
+
+	opt = form->opts = calloc(1, sizeof(*opt));
+	if (!opt) {
+	nomem:
+		free_auth_form(form);
+		return NULL;
+	}
+	opt->name = strdup("user");
+	opt->label = strdup(_("Username: "));
+	opt->type = OC_FORM_OPT_TEXT;
+
+	opt2 = opt->next = calloc(1, sizeof(*opt));
+	if (!opt2)
+		goto nomem;
+	opt2->name = strdup("passwd");
+	opt2->label = auth_id ? strdup(_("Challenge: ")) : strdup(_("Password: "));
+
+	/* XX: Some VPNs use a password in the first form, followed by a
+	 * a token in the second ("challenge") form. Others use only a
+	 * token. How can we distinguish these? */
+	if (!can_gen_tokencode(vpninfo, form, opt2))
+		opt2->type = OC_FORM_OPT_TOKEN;
+	else
+		opt2->type = OC_FORM_OPT_PASSWORD;
+
+	return form;
+}
+
+/* Return value:
+ *  < 0, on error
+ *  = 0, on success; *form is populated
+ */
+struct gp_login_arg {
+	const char *opt;
+	unsigned save:1;
+	unsigned show:1;
+	unsigned warn_missing:1;
+	unsigned err_missing:1;
+	const char *check;
+};
+static const struct gp_login_arg gp_login_args[] = {
+	{ .opt="unknown-arg0", .show=1 },
+	{ .opt="authcookie", .save=1, .err_missing=1 },
+	{ .opt="persistent-cookie", .warn_missing=1 },  /* 40 hex digits; persists across sessions */
+	{ .opt="portal", .save=1, .warn_missing=1 },
+	{ .opt="user", .save=1, .err_missing=1 },
+	{ .opt="authentication-source", .show=1 },      /* LDAP-auth, AUTH-RADIUS_RSA_OTP, etc. */
+	{ .opt="configuration", .warn_missing=1 },      /* usually vsys1 (sometimes vsys2, etc.) */
+	{ .opt="domain", .save=1, .warn_missing=1 },
+	{ .opt="unknown-arg8", .show=1 },
+	{ .opt="unknown-arg9", .show=1 },
+	{ .opt="unknown-arg10", .show=1 },
+	{ .opt="unknown-arg11", .show=1 },
+	{ .opt="connection-type", .err_missing=1, .check="tunnel" },
+	{ .opt="password-expiration-days", .show=1 },   /* days until password expires, if not -1 */
+	{ .opt="clientVer", .err_missing=1, .check="4100" },
+	{ .opt="preferred-ip", .save=1 },
+	{ .opt=NULL },
+};
+
+static int parse_login_xml(struct openconnect_info *vpninfo, xmlNode *xml_node)
+{
+	struct oc_text_buf *cookie = buf_alloc();
+	char *value = NULL;
+	const struct gp_login_arg *arg;
+
+	if (!xmlnode_is_named(xml_node, "jnlp"))
+		goto err_out;
+
+	xml_node = xml_node->children;
+	if (!xmlnode_is_named(xml_node, "application-desc"))
+		goto err_out;
+
+	xml_node = xml_node->children;
+	for (arg = gp_login_args; arg->opt; arg++) {
+		if (xml_node && !xmlnode_is_named(xml_node, "argument"))
+			goto err_out;
+		else if (xml_node) {
+			value = (char *)xmlNodeGetContent(xml_node);
+			if (value && (!value[0] || !strcmp(value, "(null)") || !strcmp(value, "-1"))) {
+				free(value);
+				value = NULL;
+			}
+			xml_node = xml_node->next;
+		}
+
+		if (arg->check && (!value || strcmp(value, arg->check))) {
+			vpn_progress(vpninfo, arg->err_missing ? PRG_ERR : PRG_DEBUG,
+				     _("GlobalProtect login returned %s=%s (expected %s)\n"),
+				     arg->opt, value, arg->check);
+			if (arg->err_missing)
+				goto err_out;
+		} else if ((arg->err_missing || arg->warn_missing) && !value) {
+			vpn_progress(vpninfo, arg->err_missing ? PRG_ERR : PRG_DEBUG,
+				     _("GlobalProtect login returned empty or missing %s\n"),
+				     arg->opt);
+			if (arg->err_missing)
+				goto err_out;
+		} else if (value && arg->show) {
+			vpn_progress(vpninfo, PRG_INFO,
+				     _("GlobalProtect login returned %s=%s\n"),
+				     arg->opt, value);
+		}
+
+		if (value && arg->save)
+			append_opt(cookie, arg->opt, value);
+		free(value);
+		value = NULL;
+	}
+
+	vpninfo->cookie = cookie->data;
+	cookie->data = NULL;
+	return buf_free(cookie);
+
+err_out:
+	free(value);
+	buf_free(cookie);
+	return -EINVAL;
+}
+
+static int parse_portal_xml(struct openconnect_info *vpninfo, xmlNode *xml_node)
+{
+	struct oc_auth_form *form;
+	xmlNode *x = NULL;
+	struct oc_form_opt_select *opt;
+	struct oc_text_buf *buf = NULL;
+	int max_choices = 0, result;
+	char *portal = NULL;
+
+	form = calloc(1, sizeof(*form));
+	if (!form)
+		return -ENOMEM;
+
+	form->message = strdup(_("Please select GlobalProtect gateway."));
+	form->auth_id = strdup("_portal");
+
+	opt = form->authgroup_opt = calloc(1, sizeof(*opt));
+	if (!opt) {
+		result = -ENOMEM;
+		goto out;
+	}
+	opt->form.type = OC_FORM_OPT_SELECT;
+	opt->form.name = strdup("gateway");
+	opt->form.label = strdup(_("GATEWAY:"));
+	form->opts = (void *)opt;
+
+	/* The portal contains a ton of stuff, but basically none of it is useful to a VPN client
+	 * that wishes to give control to the client user, as opposed to the VPN administrator.
+	 * The exception is the list of gateways in policy/gateways/external/list
+	 */
+	if (xmlnode_is_named(xml_node, "policy")) {
+		for (x = xml_node->children, xml_node = NULL; x; x = x->next) {
+			if (xmlnode_is_named(x, "portal-name"))
+				portal = (char *)xmlNodeGetContent(x);
+			else if (xmlnode_is_named(x, "gateways"))
+				xml_node = x;
+		}
+	}
+
+	if (xml_node) {
+		for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next)
+			if (xmlnode_is_named(xml_node, "external"))
+				for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next)
+					if (xmlnode_is_named(xml_node, "list"))
+						goto gateways;
+	}
+	result = -EINVAL;
+	goto out;
+
+gateways:
+	if (vpninfo->write_new_config) {
+		buf = buf_alloc();
+		buf_append(buf, "<GPPortal>\n  <ServerList>\n");
+		if (portal) {
+			buf_append(buf, "      <HostEntry><HostName>");
+			buf_append_xmlescaped(buf, portal);
+			buf_append(buf, "</HostName><HostAddress>%s", vpninfo->hostname);
+			if (vpninfo->port!=443)
+				buf_append(buf, ":%d", vpninfo->port);
+			buf_append(buf, "/global-protect</HostAddress></HostEntry>\n");
+		}
+	}
+
+	/* first, count the number of gateways */
+	for (x = xml_node->children; x; x = x->next)
+		if (xmlnode_is_named(x, "entry"))
+			max_choices++;
+
+	opt->choices = calloc(max_choices, sizeof(opt->choices[0]));
+	if (!opt->choices) {
+		result = -ENOMEM;
+		goto out;
+	}
+
+	/* each entry looks like <entry name="host[:443]"><description>Label</description></entry> */
+	vpn_progress(vpninfo, PRG_INFO, _("%d gateway servers available:\n"), max_choices);
+	for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
+		if (xmlnode_is_named(xml_node, "entry")) {
+			struct oc_choice *choice = calloc(1, sizeof(*choice));
+			if (!choice) {
+				result = -ENOMEM;
+				goto out;
+			}
+
+			xmlnode_get_prop(xml_node, "name", &choice->name);
+			for (x = xml_node->children; x; x=x->next)
+				if (xmlnode_is_named(x, "description")) {
+					choice->label = (char *)xmlNodeGetContent(x);
+					if (vpninfo->write_new_config) {
+						buf_append(buf, "      <HostEntry><HostName>");
+						buf_append_xmlescaped(buf, choice->label);
+						buf_append(buf, "</HostName><HostAddress>%s/ssl-vpn</HostAddress></HostEntry>\n",
+								   choice->name);
+					}
+				}
+
+			opt->choices[opt->nr_choices++] = choice;
+			vpn_progress(vpninfo, PRG_INFO, _("  %s (%s)\n"),
+				     choice->label, choice->name);
+		}
+	}
+
+	if (vpninfo->write_new_config) {
+		buf_append(buf, "  </ServerList>\n</GPPortal>\n");
+		if ((result = buf_error(buf)))
+			goto out;
+		if ((result = vpninfo->write_new_config(vpninfo, buf->data, buf->pos)))
+			goto out;
+	}
+
+	/* process auth form to select gateway */
+	result = process_auth_form(vpninfo, form);
+	if (result != OC_FORM_RESULT_NEWGROUP)
+		goto out;
+
+	/* redirect to the gateway (no-op if it's the same host) */
+	free(vpninfo->redirect_url);
+	if (asprintf(&vpninfo->redirect_url, "https://%s", vpninfo->authgroup) == 0) {
+		result = -ENOMEM;
+		goto out;
+	}
+	result = handle_redirect(vpninfo);
+
+out:
+	buf_free(buf);
+	free(portal);
+	free_auth_form(form);
+	return result;
+}
+
+static int gpst_login(struct openconnect_info *vpninfo, int portal)
+{
+	int result;
+
+	struct oc_auth_form *form = NULL;
+	struct oc_text_buf *request_body = buf_alloc();
+	const char *request_body_type = "application/x-www-form-urlencoded";
+	char *xml_buf = NULL, *orig_path;
+	char *prompt = NULL, *auth_id = NULL;
+
+#ifdef HAVE_LIBSTOKEN
+	/* Step 1: Unlock software token (if applicable) */
+	if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
+		result = prepare_stoken(vpninfo);
+		if (result)
+			goto out;
+	}
+#endif
+
+	form = auth_form(vpninfo, _("Please enter your username and password"), NULL);
+	if (!form)
+		return -ENOMEM;
+
+	/* Ask the user to fill in the auth form; repeat as necessary */
+	for (;;) {
+		/* process auth form (username and password or challenge) */
+		result = process_auth_form(vpninfo, form);
+		if (result)
+			goto out;
+
+	redo_gateway:
+		buf_truncate(request_body);
+
+		/* generate token code if specified */
+		result = do_gen_tokencode(vpninfo, form);
+		if (result) {
+			vpn_progress(vpninfo, PRG_ERR, _("Failed to generate OTP tokencode; disabling token\n"));
+			vpninfo->token_bypassed = 1;
+			goto out;
+		}
+
+		/* submit gateway login (ssl-vpn/login.esp) or portal config (global-protect/getconfig.esp) request */
+		buf_truncate(request_body);
+		buf_append(request_body, "jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:");
+		if (!strcmp(vpninfo->platname, "win"))
+			append_opt(request_body, "clientos", "Windows");
+		else
+			append_opt(request_body, "clientos", vpninfo->platname);
+		append_opt(request_body, "server", vpninfo->hostname);
+		append_opt(request_body, "computer", vpninfo->localname);
+		/* Note: auth_id is non-NULL but freed, and an actual copy of it is in form->auth_id.
+		   This checks if form->auth_id was explcitly set from auth_id and uses it if so. */
+		if (auth_id)
+			append_opt(request_body, "inputStr", form->auth_id);
+		append_form_opts(vpninfo, form, request_body);
+		if ((result = buf_error(request_body)))
+			goto out;
+
+		orig_path = vpninfo->urlpath;
+		vpninfo->urlpath = strdup(portal ? "global-protect/getconfig.esp" : "ssl-vpn/login.esp");
+		result = do_https_request(vpninfo, "POST", request_body_type, request_body,
+					  &xml_buf, 0);
+		free(vpninfo->urlpath);
+		vpninfo->urlpath = orig_path;
+
+		/* Result could be either a JavaScript challenge or XML */
+		result = gpst_xml_or_error(vpninfo, result, xml_buf,
+		                           portal ? parse_portal_xml : parse_login_xml, &prompt, &auth_id);
+		if (result == -EAGAIN) {
+			free_auth_form(form);
+			form = auth_form(vpninfo, prompt, auth_id);
+			free(prompt);
+			free(auth_id);
+			if (!form)
+				return -ENOMEM;
+			continue;
+		} else if (portal && result == 0) {
+			portal = 0;
+			goto redo_gateway;
+		} else if (result == -EACCES) /* Invalid username/password */
+			continue;
+		else
+			break;
+	}
+
+out:
+	free_auth_form(form);
+	buf_free(request_body);
+	free(xml_buf);
+	return result;
+}
+
+int gpst_obtain_cookie(struct openconnect_info *vpninfo)
+{
+	int result;
+
+	if (vpninfo->urlpath && (!strcmp(vpninfo->urlpath, "portal") || !strncmp(vpninfo->urlpath, "global-protect", 14))) {
+		/* assume the server is a portal */
+		return gpst_login(vpninfo, 1);
+	} else if (vpninfo->urlpath && (!strcmp(vpninfo->urlpath, "gateway") || !strncmp(vpninfo->urlpath, "ssl-vpn", 7))) {
+		/* assume the server is a gateway */
+		return gpst_login(vpninfo, 0);
+	} else {
+		/* first try handling it as a gateway, then a portal */
+		result = gpst_login(vpninfo, 0);
+		if (result == -EEXIST) {
+			/* XX: Don't we want to start by trying the same username/password the user just
+			   entered for the 'gateway' attempt? */
+			result = gpst_login(vpninfo, 1);
+			if (result == -EEXIST)
+				vpn_progress(vpninfo, PRG_ERR, _("Server is neither a GlobalProtect portal nor a gateway.\n"));
+		}
+		return result;
+	}
+}
+
+int gpst_bye(struct openconnect_info *vpninfo, const char *reason)
+{
+	char *orig_path;
+	int result;
+	struct oc_text_buf *request_body = buf_alloc();
+	const char *request_body_type = "application/x-www-form-urlencoded";
+	const char *method = "POST";
+	char *xml_buf = NULL;
+
+	/* In order to logout successfully, the client must send not only
+	 * the session's authcookie, but also the portal, user, computer,
+	 * and domain matching the values sent with the getconfig request.
+	 *
+	 * You read that right: the client must send a bunch of irrelevant
+	 * non-secret values in its logout request. If they're wrong or
+	 * missing, the logout will fail and the authcookie will remain
+	 * valid -- which is a security hole.
+	 *
+	 * Don't blame me. I didn't design this.
+	 */
+	append_opt(request_body, "computer", vpninfo->localname);
+	buf_append(request_body, "&%s", vpninfo->cookie);
+	if ((result = buf_error(request_body)))
+		goto out;
+
+	/* We need to close and reopen the HTTPS connection (to kill
+	 * the tunnel session) and submit a new HTTPS request to
+	 * logout.
+	 */
+	orig_path = vpninfo->urlpath;
+	vpninfo->urlpath = strdup("ssl-vpn/logout.esp");
+	openconnect_close_https(vpninfo, 0);
+	result = do_https_request(vpninfo, method, request_body_type, request_body,
+				  &xml_buf, 0);
+	free(vpninfo->urlpath);
+	vpninfo->urlpath = orig_path;
+
+	/* logout.esp returns HTTP status 200 and <response status="success"> when
+	 * successful, and all manner of malformed junk when unsuccessful.
+	 */
+	result = gpst_xml_or_error(vpninfo, result, xml_buf, NULL, NULL, NULL);
+	if (result < 0)
+		vpn_progress(vpninfo, PRG_ERR, _("Logout failed.\n"));
+	else
+		vpn_progress(vpninfo, PRG_INFO, _("Logout successful\n"));
+
+out:
+	buf_free(request_body);
+	free(xml_buf);
+	return result;
+}
diff --git a/gpst.c b/gpst.c
new file mode 100644
index 0000000..d3fb831
--- /dev/null
+++ b/gpst.c
@@ -0,0 +1,802 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright ? 2016-2017 Daniel Lenski
+ *
+ * Author: Daniel Lenski <dlenski at gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ */
+
+#include <config.h>
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#ifdef HAVE_LZ4
+#include <lz4.h>
+#endif
+
+#if defined(__linux__)
+/* For TCP_INFO */
+# include <linux/tcp.h>
+#endif
+
+#include <assert.h>
+
+#include "openconnect-internal.h"
+
+/*
+ * Data packets are encapsulated in the SSL stream as follows:
+ *
+ * 0000: Magic "\x1a\x2b\x3c\x4d"
+ * 0004: Big-endian EtherType (0x0800 for IPv4)
+ * 0006: Big-endian 16-bit length (not including 16-byte header)
+ * 0008: Always "\x01\0\0\0\0\0\0\0"
+ * 0010: data payload
+ */
+
+/* Strange initialisers here to work around GCC PR#10676 (which was
+ * fixed in GCC 4.6 but it takes a while for some systems to catch
+ * up. */
+static const struct pkt dpd_pkt = {
+	.next = NULL,
+	{ .gpst.hdr = { 0x1a, 0x2b, 0x3c, 0x4d } }
+};
+
+/* similar to auth.c's xmlnode_get_text, including that *var should be freed by the caller,
+   but without the hackish param / %s handling that Cisco needs. And without freeing up
+   the old contents of *var, which is likely to lead to bugs? */
+static int xmlnode_get_text(xmlNode *xml_node, const char *name, char **var)
+{
+	char *str;
+
+	if (name && !xmlnode_is_named(xml_node, name))
+		return -EINVAL;
+
+	str = (char *)xmlNodeGetContent(xml_node);
+	if (!str)
+		return -ENOENT;
+
+	*var = str;
+	return 0;
+}
+
+/* We behave like CSTP ? create a linked list in vpninfo->cstp_options
+ * with the strings containing the information we got from the server,
+ * and oc_ip_info contains const copies of those pointers.
+ *
+ * (unlike version in oncp.c, val is stolen rather than strdup'ed) */
+
+static const char *add_option(struct openconnect_info *vpninfo, const char *opt, const char *val)
+{
+	struct oc_vpn_option *new = malloc(sizeof(*new));
+	if (!new)
+		return NULL;
+
+	new->option = strdup(opt);
+	if (!new->option) {
+		free(new);
+		return NULL;
+	}
+	new->value = val;
+	new->next = vpninfo->cstp_options;
+	vpninfo->cstp_options = new;
+
+	return new->value;
+}
+
+static int filter_opts(struct oc_text_buf *buf, const char *query, const char *incexc, int include)
+{
+	const char *f, *endf, *eq;
+	const char *found, *comma;
+
+	for (f = query; *f; f=(*endf) ? endf+1 : endf) {
+		endf = strchr(f, '&') ? : f+strlen(f);
+		eq = strchr(f, '=');
+		if (!eq || eq > endf)
+			eq = endf;
+
+		for (found = incexc; *found; found=(*comma) ? comma+1 : comma) {
+			comma = strchr(found, ',') ? : found+strlen(found);
+			if (!strncmp(found, f, MAX(comma-found, eq-f)))
+				break;
+		}
+
+		if ((include && *found) || (!include && !*found)) {
+			if (buf->pos && buf->data[buf->pos-1] != '?' && buf->data[buf->pos-1] != '&')
+				buf_append(buf, "&");
+			buf_append_bytes(buf, f, (int)(endf-f));
+		}
+	}
+	return buf_error(buf);
+}
+
+/* Parse this JavaScript-y mess:
+
+	"var respStatus = \"Challenge|Error\";\n"
+	"var respMsg = \"<prompt>\";\n"
+	"thisForm.inputStr.value = "<inputStr>";\n"
+*/
+static int parse_javascript(char *buf, char **prompt, char **inputStr)
+{
+	const char *start, *end = buf;
+	int status;
+
+	const char *pre_status = "var respStatus = \"",
+	           *pre_prompt = "var respMsg = \"",
+	           *pre_inputStr = "thisForm.inputStr.value = \"";
+
+	/* Status */
+	while (isspace(*end))
+		end++;
+	if (strncmp(end, pre_status, strlen(pre_status)))
+		goto err;
+
+	start = end+strlen(pre_status);
+	end = strchr(start, '\n');
+	if (!end || end[-1] != ';' || end[-2] != '"')
+		goto err;
+
+	if (!strncmp(start, "Challenge", 8))    status = 0;
+	else if (!strncmp(start, "Error", 5))   status = 1;
+	else                                    goto err;
+
+	/* Prompt */
+	while (isspace(*end))
+		end++;
+	if (strncmp(end, pre_prompt, strlen(pre_prompt)))
+		goto err;
+
+	start = end+strlen(pre_prompt);
+	end = strchr(start, '\n');
+	if (!end || end[-1] != ';' || end[-2] != '"')
+		goto err;
+
+	if (prompt)
+		*prompt = strndup(start, end-start-2);
+
+	/* inputStr */
+	while (isspace(*end))
+		end++;
+	if (strncmp(end, pre_inputStr, strlen(pre_inputStr)))
+		goto err2;
+
+	start = end+strlen(pre_inputStr);
+	end = strchr(start, '\n');
+	if (!end || end[-1] != ';' || end[-2] != '"')
+		goto err2;
+
+	if (inputStr)
+		*inputStr = strndup(start, end-start-2);
+
+	while (isspace(*end))
+		end++;
+	if (*end != '\0')
+		goto err3;
+
+	return status;
+
+err3:
+	if (inputStr) free(*inputStr);
+err2:
+	if (prompt) free(*prompt);
+err:
+	return -EINVAL;
+}
+
+int gpst_xml_or_error(struct openconnect_info *vpninfo, int result, char *response,
+					  int (*xml_cb)(struct openconnect_info *, xmlNode *xml_node),
+					  char **prompt, char **inputStr)
+{
+	xmlDocPtr xml_doc;
+	xmlNode *xml_node;
+	char *err = NULL;
+
+	/* custom error codes returned by /ssl-vpn/login.esp and maybe others */
+	if (result == -EACCES)
+		vpn_progress(vpninfo, PRG_ERR, _("Invalid username or password.\n"));
+	else if (result == -EBADMSG)
+		vpn_progress(vpninfo, PRG_ERR, _("Invalid client certificate.\n"));
+
+	if (result < 0)
+		return result;
+
+	if (!response) {
+		vpn_progress(vpninfo, PRG_DEBUG,
+			     _("Empty response from server\n"));
+		return -EINVAL;
+	}
+
+	/* is it XML? */
+	xml_doc = xmlReadMemory(response, strlen(response), "noname.xml", NULL,
+				XML_PARSE_NOERROR);
+	if (!xml_doc) {
+		/* is it Javascript? */
+		char *p, *i;
+		result = parse_javascript(response, &p, &i);
+		switch (result) {
+		case 1:
+			vpn_progress(vpninfo, PRG_ERR, _("%s\n"), p);
+			break;
+		case 0:
+			vpn_progress(vpninfo, PRG_INFO, _("Challenge: %s\n"), p);
+			if (prompt && inputStr) {
+				*prompt=p;
+				*inputStr=i;
+				return -EAGAIN;
+			}
+			break;
+		default:
+			goto bad_xml;
+		}
+		free((char *)p);
+		free((char *)i);
+		goto out;
+	}
+
+	xml_node = xmlDocGetRootElement(xml_doc);
+
+	/* is it <response status="error"><error>..</error></response> ? */
+	if (xmlnode_is_named(xml_node, "response")
+	    && !xmlnode_match_prop(xml_node, "status", "error")) {
+		for (xml_node=xml_node->children; xml_node; xml_node=xml_node->next) {
+			if (!xmlnode_get_text(xml_node, "error", &err))
+				goto out;
+		}
+		goto bad_xml;
+	}
+
+	if (xml_cb)
+		result = xml_cb(vpninfo, xml_node);
+
+	if (result == -EINVAL) {
+	bad_xml:
+		vpn_progress(vpninfo, PRG_ERR,
+					 _("Failed to parse server response\n"));
+		vpn_progress(vpninfo, PRG_DEBUG,
+					 _("Response was:%s\n"), response);
+	}
+
+out:
+	if (err) {
+		if (!strcmp(err, "GlobalProtect gateway does not exist")
+		    || !strcmp(err, "GlobalProtect portal does not exist")) {
+			vpn_progress(vpninfo, PRG_DEBUG, "%s\n", err);
+			result = -EEXIST;
+		} else if (!strcmp(err, "Invalid authentication cookie")) {
+			vpn_progress(vpninfo, PRG_ERR, "%s\n", err);
+			result = -EPERM;
+		} else {
+			vpn_progress(vpninfo, PRG_ERR, "%s\n", err);
+			result = -EINVAL;
+		}
+		free(err);
+	}
+	if (xml_doc)
+		xmlFreeDoc(xml_doc);
+	return result;
+}
+
+#define ESP_OVERHEAD (4 /* SPI */ + 4 /* sequence number */ + \
+         20 /* biggest supported MAC (SHA1) */ + 32 /* biggest supported IV (AES-256) */ + \
+	 1 /* pad length */ + 1 /* next header */ + \
+         16 /* max padding */ )
+#define UDP_HEADER_SIZE 8
+#define IPV4_HEADER_SIZE 20
+#define IPV6_HEADER_SIZE 40
+
+static int calculate_mtu(struct openconnect_info *vpninfo)
+{
+	int mtu = vpninfo->reqmtu, base_mtu = vpninfo->basemtu;
+
+#if defined(__linux__) && defined(TCP_INFO)
+	if (!mtu || !base_mtu) {
+		struct tcp_info ti;
+		socklen_t ti_size = sizeof(ti);
+
+		if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_INFO,
+				&ti, &ti_size)) {
+			vpn_progress(vpninfo, PRG_DEBUG,
+				     _("TCP_INFO rcv mss %d, snd mss %d, adv mss %d, pmtu %d\n"),
+				     ti.tcpi_rcv_mss, ti.tcpi_snd_mss, ti.tcpi_advmss, ti.tcpi_pmtu);
+
+			if (!base_mtu) {
+				base_mtu = ti.tcpi_pmtu;
+			}
+
+			if (!base_mtu) {
+				if (ti.tcpi_rcv_mss < ti.tcpi_snd_mss)
+					base_mtu = ti.tcpi_rcv_mss - 13;
+				else
+					base_mtu = ti.tcpi_snd_mss - 13;
+			}
+		}
+	}
+#endif
+#ifdef TCP_MAXSEG
+	if (!base_mtu) {
+		int mss;
+		socklen_t mss_size = sizeof(mss);
+		if (!getsockopt(vpninfo->ssl_fd, IPPROTO_TCP, TCP_MAXSEG,
+				&mss, &mss_size)) {
+			vpn_progress(vpninfo, PRG_DEBUG, _("TCP_MAXSEG %d\n"), mss);
+			base_mtu = mss - 13;
+		}
+	}
+#endif
+	if (!base_mtu) {
+		/* Default */
+		base_mtu = 1406;
+	}
+
+	if (base_mtu < 1280)
+		base_mtu = 1280;
+
+	if (!mtu) {
+		/* remove IP/UDP and ESP overhead from base MTU to calculate tunnel MTU */
+		mtu = base_mtu - ESP_OVERHEAD - UDP_HEADER_SIZE;
+		if (vpninfo->peer_addr->sa_family == AF_INET6)
+			mtu -= IPV6_HEADER_SIZE;
+		else
+			mtu -= IPV4_HEADER_SIZE;
+	}
+	return mtu;
+}
+
+/* Return value:
+ *  < 0, on error
+ *  = 0, on success; *form is populated
+ */
+static int gpst_parse_config_xml(struct openconnect_info *vpninfo, xmlNode *xml_node)
+{
+	xmlNode *member;
+	char *s;
+	int ii;
+
+	if (!xml_node || !xmlnode_is_named(xml_node, "response"))
+		return -EINVAL;
+
+	/* Clear old options which will be overwritten */
+	vpninfo->ip_info.addr = vpninfo->ip_info.netmask = NULL;
+	vpninfo->ip_info.addr6 = vpninfo->ip_info.netmask6 = NULL;
+	vpninfo->ip_info.domain = NULL;
+	vpninfo->ip_info.mtu = 0;
+	vpninfo->ssl_times.rekey_method = REKEY_NONE;
+	vpninfo->cstp_options = NULL;
+
+	for (ii = 0; ii < 3; ii++)
+		vpninfo->ip_info.dns[ii] = vpninfo->ip_info.nbns[ii] = NULL;
+	free_split_routes(vpninfo);
+
+	/* Parse config */
+	for (xml_node = xml_node->children; xml_node; xml_node=xml_node->next) {
+		if (!xmlnode_get_text(xml_node, "ip-address", &s))
+			vpninfo->ip_info.addr = add_option(vpninfo, "ipaddr", s);
+		else if (!xmlnode_get_text(xml_node, "netmask", &s))
+			vpninfo->ip_info.netmask = add_option(vpninfo, "netmask", s);
+		else if (!xmlnode_get_text(xml_node, "mtu", &s)) {
+			vpninfo->ip_info.mtu = atoi(s);
+			free(s);
+		} else if (!xmlnode_get_text(xml_node, "ssl-tunnel-url", &s)) {
+			free(vpninfo->urlpath);
+			vpninfo->urlpath = s;
+			if (strcmp(s, "/ssl-tunnel-connect.sslvpn"))
+				vpn_progress(vpninfo, PRG_INFO, _("Non-standard SSL tunnel path: %s\n"), s);
+		} else if (!xmlnode_get_text(xml_node, "timeout", &s)) {
+			int sec = atoi(s);
+			vpn_progress(vpninfo, PRG_INFO, _("Tunnel timeout (rekey interval) is %d minutes.\n"), sec/60);
+			vpninfo->ssl_times.last_rekey = time(NULL);
+			vpninfo->ssl_times.rekey = sec - 60;
+			vpninfo->ssl_times.rekey_method = REKEY_TUNNEL;
+			free(s);
+		} else if (!xmlnode_get_text(xml_node, "gw-address", &s)) {
+			/* As remarked in oncp.c, "this is a tunnel; having a
+			 * gateway is meaningless."
+			 */
+			if (strcmp(s, vpninfo->ip_info.gateway_addr))
+				vpn_progress(vpninfo, PRG_DEBUG,
+							 _("Gateway address in config XML (%s) differs from external gateway address (%s).\n"), s, vpninfo->ip_info.gateway_addr);
+			free(s);
+		} else if (xmlnode_is_named(xml_node, "dns")) {
+			for (ii=0, member = xml_node->children; member && ii<3; member=member->next)
+				if (!xmlnode_get_text(member, "member", &s))
+					vpninfo->ip_info.dns[ii++] = add_option(vpninfo, "DNS", s);
+		} else if (xmlnode_is_named(xml_node, "wins")) {
+			for (ii=0, member = xml_node->children; member && ii<3; member=member->next)
+				if (!xmlnode_get_text(member, "member", &s))
+					vpninfo->ip_info.nbns[ii++] = add_option(vpninfo, "WINS", s);
+		} else if (xmlnode_is_named(xml_node, "dns-suffix")) {
+			for (ii=0, member = xml_node->children; member && ii<1; member=member->next)
+				if (!xmlnode_get_text(member, "member", &s)) {
+					vpninfo->ip_info.domain = add_option(vpninfo, "search", s);
+					ii++;
+				}
+		} else if (xmlnode_is_named(xml_node, "access-routes")) {
+			for (member = xml_node->children; member; member=member->next) {
+				if (!xmlnode_get_text(member, "member", &s)) {
+					struct oc_split_include *inc = malloc(sizeof(*inc));
+					if (!inc)
+						continue;
+					inc->route = add_option(vpninfo, "split-include", s);
+					inc->next = vpninfo->ip_info.split_includes;
+					vpninfo->ip_info.split_includes = inc;
+				}
+			}
+		} else if (xmlnode_is_named(xml_node, "ipsec")) {
+			vpn_progress(vpninfo, PRG_DEBUG, _("Ignoring ESP keys since ESP support not available in this build\n"));
+		}
+	}
+
+	/* No IPv6 support for SSL VPN:
+	 * https://live.paloaltonetworks.com/t5/Learning-Articles/IPv6-Support-on-the-Palo-Alto-Networks-Firewall/ta-p/52994 */
+	openconnect_disable_ipv6(vpninfo);
+
+	/* Set 10-second DPD/keepalive (same as Windows client) unless
+	 * overridden with --force-dpd */
+	if (!vpninfo->ssl_times.dpd)
+		vpninfo->ssl_times.dpd = 10;
+	vpninfo->ssl_times.keepalive = vpninfo->ssl_times.dpd;
+
+	return 0;
+}
+
+static int gpst_get_config(struct openconnect_info *vpninfo)
+{
+	char *orig_path;
+	int result;
+	struct oc_text_buf *request_body = buf_alloc();
+	struct oc_vpn_option *old_cstp_opts = vpninfo->cstp_options;
+	const char *old_addr = vpninfo->ip_info.addr, *old_netmask = vpninfo->ip_info.netmask;
+	const char *request_body_type = "application/x-www-form-urlencoded";
+	const char *method = "POST";
+	char *xml_buf=NULL;
+
+	/* submit getconfig request */
+	buf_append(request_body, "client-type=1&protocol-version=p1&app-version=3.0.1-10");
+	append_opt(request_body, "os-version", vpninfo->platname);
+	if (!strcmp(vpninfo->platname, "win"))
+		append_opt(request_body, "clientos", "Windows");
+	else
+		append_opt(request_body, "clientos", vpninfo->platname);
+	append_opt(request_body, "hmac-algo", "sha1,md5");
+	append_opt(request_body, "enc-algo", "aes-128-cbc,aes-256-cbc");
+	if (old_addr) {
+		append_opt(request_body, "preferred-ip", old_addr);
+		filter_opts(request_body, vpninfo->cookie, "preferred-ip", 0);
+	} else
+		buf_append(request_body, "&%s", vpninfo->cookie);
+	if ((result = buf_error(request_body)))
+		goto out;
+
+	orig_path = vpninfo->urlpath;
+	vpninfo->urlpath = strdup("ssl-vpn/getconfig.esp");
+	result = do_https_request(vpninfo, method, request_body_type, request_body,
+				  &xml_buf, 0);
+	free(vpninfo->urlpath);
+	vpninfo->urlpath = orig_path;
+
+	if (result < 0)
+		goto out;
+
+	/* parse getconfig result */
+	result = gpst_xml_or_error(vpninfo, result, xml_buf, gpst_parse_config_xml, NULL, NULL);
+	if (result)
+		return result;
+
+	if (!vpninfo->ip_info.mtu) {
+		/* FIXME: GP gateway config always seems to be <mtu>0</mtu> */
+		vpninfo->ip_info.mtu = calculate_mtu(vpninfo);
+		vpn_progress(vpninfo, PRG_ERR,
+			     _("No MTU received. Calculated %d\n"), vpninfo->ip_info.mtu);
+		/* return -EINVAL; */
+	}
+	if (!vpninfo->ip_info.addr) {
+		vpn_progress(vpninfo, PRG_ERR,
+			     _("No IP address received. Aborting\n"));
+		result = -EINVAL;
+		goto out;
+	}
+	if (old_addr) {
+		if (strcmp(old_addr, vpninfo->ip_info.addr)) {
+			vpn_progress(vpninfo, PRG_ERR,
+				     _("Reconnect gave different Legacy IP address (%s != %s)\n"),
+				     vpninfo->ip_info.addr, old_addr);
+			result = -EINVAL;
+			goto out;
+		}
+	}
+	if (old_netmask) {
+		if (strcmp(old_netmask, vpninfo->ip_info.netmask)) {
+			vpn_progress(vpninfo, PRG_ERR,
+				     _("Reconnect gave different Legacy IP netmask (%s != %s)\n"),
+				     vpninfo->ip_info.netmask, old_netmask);
+			result = -EINVAL;
+			goto out;
+		}
+	}
+
+out:
+	buf_free(request_body);
+	free_optlist(old_cstp_opts);
+	free(xml_buf);
+	return result;
+}
+
+static int gpst_connect(struct openconnect_info *vpninfo)
+{
+	int ret;
+	struct oc_text_buf *reqbuf;
+	const char start_tunnel[12] = "START_TUNNEL"; /* NOT zero-terminated */
+	char buf[256];
+
+	/* Connect to SSL VPN tunnel */
+	vpn_progress(vpninfo, PRG_DEBUG,
+		     _("Connecting to HTTPS tunnel endpoint ...\n"));
+
+	ret = openconnect_open_https(vpninfo);
+	if (ret)
+		return ret;
+
+	reqbuf = buf_alloc();
+	buf_append(reqbuf, "GET %s?", vpninfo->urlpath);
+	filter_opts(reqbuf, vpninfo->cookie, "user,authcookie", 1);
+	buf_append(reqbuf, " HTTP/1.1\r\n\r\n");
+	if ((ret = buf_error(reqbuf)))
+		goto out;
+
+	if (vpninfo->dump_http_traffic)
+		dump_buf(vpninfo, '>', reqbuf->data);
+
+	vpninfo->ssl_write(vpninfo, reqbuf->data, reqbuf->pos);
+
+	if ((ret = vpninfo->ssl_read(vpninfo, buf, 12)) < 0) {
+		if (ret == -EINTR)
+			goto out;
+		vpn_progress(vpninfo, PRG_ERR,
+		             _("Error fetching GET-tunnel HTTPS response.\n"));
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (!strncmp(buf, start_tunnel, sizeof(start_tunnel))) {
+		ret = 0;
+	} else if (ret==0) {
+		vpn_progress(vpninfo, PRG_ERR,
+			     _("Gateway disconnected immediately after GET-tunnel request.\n"));
+		ret = -EPIPE;
+	} else {
+		if (ret==sizeof(start_tunnel)) {
+			ret = vpninfo->ssl_gets(vpninfo, buf+sizeof(start_tunnel), sizeof(buf)-sizeof(start_tunnel));
+			ret = (ret>0 ? ret : 0) + sizeof(start_tunnel);
+		}
+		vpn_progress(vpninfo, PRG_ERR,
+		             _("Got inappropriate HTTP GET-tunnel response: %.*s\n"), ret, buf);
+		ret = -EINVAL;
+	}
+
+	if (ret < 0)
+		openconnect_close_https(vpninfo, 0);
+	else {
+		monitor_fd_new(vpninfo, ssl);
+		monitor_read_fd(vpninfo, ssl);
+		monitor_except_fd(vpninfo, ssl);
+		vpninfo->ssl_times.last_rekey = vpninfo->ssl_times.last_rx = vpninfo->ssl_times.last_tx = time(NULL);
+	}
+
+out:
+	buf_free(reqbuf);
+	return ret;
+}
+
+int gpst_setup(struct openconnect_info *vpninfo)
+{
+	int ret;
+
+	/* Get configuration */
+	ret = gpst_get_config(vpninfo);
+	if (ret)
+		return ret;
+
+	ret = gpst_connect(vpninfo);
+	return ret;
+}
+
+int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout)
+{
+	int ret;
+	int work_done = 0;
+	uint16_t ethertype;
+	uint32_t one, zero, magic;
+
+	if (vpninfo->ssl_fd == -1)
+		goto do_reconnect;
+
+	while (1) {
+		int receive_mtu = MAX(2048, vpninfo->ip_info.mtu + 256);
+		int len, payload_len;
+
+		if (!vpninfo->cstp_pkt) {
+			vpninfo->cstp_pkt = malloc(sizeof(struct pkt) + receive_mtu);
+			if (!vpninfo->cstp_pkt) {
+				vpn_progress(vpninfo, PRG_ERR, _("Allocation failed\n"));
+				break;
+			}
+		}
+
+		len = ssl_nonblock_read(vpninfo, vpninfo->cstp_pkt->gpst.hdr, receive_mtu + 16);
+		if (!len)
+			break;
+		if (len < 0) {
+			vpn_progress(vpninfo, PRG_ERR, _("Packet receive error: %s\n"), strerror(-len));
+			goto do_reconnect;
+		}
+		if (len < 16) {
+			vpn_progress(vpninfo, PRG_ERR, _("Short packet received (%d bytes)\n"), len);
+			vpninfo->quit_reason = "Short packet received";
+			return 1;
+		}
+
+		/* check packet header */
+		magic = load_be32(vpninfo->cstp_pkt->gpst.hdr);
+		ethertype = load_be16(vpninfo->cstp_pkt->gpst.hdr + 4);
+		payload_len = load_be16(vpninfo->cstp_pkt->gpst.hdr + 6);
+		one = load_le32(vpninfo->cstp_pkt->gpst.hdr + 8);
+		zero = load_le32(vpninfo->cstp_pkt->gpst.hdr + 12);
+
+		if (magic != 0x1a2b3c4d)
+			goto unknown_pkt;
+
+		if (len != 16 + payload_len) {
+			vpn_progress(vpninfo, PRG_ERR,
+				     _("Unexpected packet length. SSL_read returned %d (includes 16 header bytes) but header payload_len is %d\n"),
+			             len, payload_len);
+			dump_buf_hex(vpninfo, PRG_ERR, '<', vpninfo->cstp_pkt->gpst.hdr, 16);
+			continue;
+		}
+
+		vpninfo->ssl_times.last_rx = time(NULL);
+		switch (ethertype) {
+		case 0:
+			vpn_progress(vpninfo, PRG_DEBUG,
+				     _("Got GPST DPD/keepalive response\n"));
+
+			if (one != 0 || zero != 0) {
+				vpn_progress(vpninfo, PRG_DEBUG,
+					     _("Expected 0000000000000000 as last 8 bytes of DPD/keepalive packet header, but got:\n"));
+				dump_buf_hex(vpninfo, PRG_DEBUG, '<', vpninfo->cstp_pkt->gpst.hdr + 8, 8);
+			}
+			continue;
+		case 0x0800:
+			vpn_progress(vpninfo, PRG_TRACE,
+				     _("Received data packet of %d bytes\n"),
+				     payload_len);
+			vpninfo->cstp_pkt->len = payload_len;
+			queue_packet(&vpninfo->incoming_queue, vpninfo->cstp_pkt);
+			vpninfo->cstp_pkt = NULL;
+			work_done = 1;
+
+			if (one != 1 || zero != 0) {
+				vpn_progress(vpninfo, PRG_DEBUG,
+					     _("Expected 0100000000000000 as last 8 bytes of data packet header, but got:\n"));
+				dump_buf_hex(vpninfo, PRG_DEBUG, '<', vpninfo->cstp_pkt->gpst.hdr + 8, 8);
+			}
+			continue;
+		}
+
+	unknown_pkt:
+		vpn_progress(vpninfo, PRG_ERR,
+			     _("Unknown packet. Header dump follows:\n"));
+		dump_buf_hex(vpninfo, PRG_ERR, '<', vpninfo->cstp_pkt->gpst.hdr, 16);
+		vpninfo->quit_reason = "Unknown packet received";
+		return 1;
+	}
+
+
+	/* If SSL_write() fails we are expected to try again. With exactly
+	   the same data, at exactly the same location. So we keep the
+	   packet we had before.... */
+	if (vpninfo->current_ssl_pkt) {
+	handle_outgoing:
+		vpninfo->ssl_times.last_tx = time(NULL);
+		unmonitor_write_fd(vpninfo, ssl);
+
+		ret = ssl_nonblock_write(vpninfo,
+					 vpninfo->current_ssl_pkt->gpst.hdr,
+					 vpninfo->current_ssl_pkt->len + 16);
+		if (ret < 0)
+			goto do_reconnect;
+		else if (!ret) {
+			switch (ka_stalled_action(&vpninfo->ssl_times, timeout)) {
+			case KA_REKEY:
+				goto do_rekey;
+			case KA_DPD_DEAD:
+				goto peer_dead;
+			case KA_NONE:
+				return work_done;
+			}
+		}
+
+		if (ret != vpninfo->current_ssl_pkt->len + 16) {
+			vpn_progress(vpninfo, PRG_ERR,
+				     _("SSL wrote too few bytes! Asked for %d, sent %d\n"),
+				     vpninfo->current_ssl_pkt->len + 16, ret);
+			vpninfo->quit_reason = "Internal error";
+			return 1;
+		}
+		/* Don't free the 'special' packets */
+		if (vpninfo->current_ssl_pkt != &dpd_pkt)
+			free(vpninfo->current_ssl_pkt);
+
+		vpninfo->current_ssl_pkt = NULL;
+	}
+
+	switch (keepalive_action(&vpninfo->ssl_times, timeout)) {
+	case KA_REKEY:
+	do_rekey:
+		vpn_progress(vpninfo, PRG_INFO, _("GlobalProtect rekey due\n"));
+		goto do_reconnect;
+	case KA_DPD_DEAD:
+	peer_dead:
+		vpn_progress(vpninfo, PRG_ERR,
+			     _("GPST Dead Peer Detection detected dead peer!\n"));
+	do_reconnect:
+		ret = ssl_reconnect(vpninfo);
+		if (ret) {
+			vpn_progress(vpninfo, PRG_ERR, _("Reconnect failed\n"));
+			vpninfo->quit_reason = "GPST reconnect failed";
+			return ret;
+		}
+		return 1;
+
+	case KA_KEEPALIVE:
+		/* No need to send an explicit keepalive
+		   if we have real data to send */
+		if (vpninfo->dtls_state != DTLS_CONNECTED &&
+		    vpninfo->outgoing_queue.head)
+			break;
+
+	case KA_DPD:
+		vpn_progress(vpninfo, PRG_DEBUG, _("Send GPST DPD/keepalive request\n"));
+
+		vpninfo->current_ssl_pkt = (struct pkt *)&dpd_pkt;
+		goto handle_outgoing;
+	}
+
+
+	/* Service outgoing packet queue */
+	while (vpninfo->dtls_state != DTLS_CONNECTED &&
+	       (vpninfo->current_ssl_pkt = dequeue_packet(&vpninfo->outgoing_queue))) {
+		struct pkt *this = vpninfo->current_ssl_pkt;
+
+		/* store header */
+		store_be32(this->gpst.hdr, 0x1a2b3c4d);
+		store_be16(this->gpst.hdr + 4, 0x0800); /* IPv4 EtherType */
+		store_be16(this->gpst.hdr + 6, this->len);
+		store_le32(this->gpst.hdr + 8, 1);
+		store_le32(this->gpst.hdr + 12, 0);
+
+		vpn_progress(vpninfo, PRG_TRACE,
+			     _("Sending data packet of %d bytes\n"),
+			     this->len);
+
+		goto handle_outgoing;
+	}
+
+	/* Work is not done if we just got rid of packets off the queue */
+	return work_done;
+}
diff --git a/http.c b/http.c
index e53f3fc..a860ae4 100644
--- a/http.c
+++ b/http.c
@@ -54,6 +54,19 @@ void buf_append_urlencoded(struct oc_text_buf *buf, const char *str)
 	}
 }
 
+void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str)
+{
+	while (str && *str) {
+		unsigned char c = *str;
+		if (c=='<' || c=='>' || c=='&' || c=='"' || c=='\'')
+			buf_append(buf, "&#x%02x;", c);
+		else
+			buf_append_bytes(buf, str, 1);
+
+		str++;
+	}
+}
+
 void buf_append_hex(struct oc_text_buf *buf, const void *str, unsigned len)
 {
 	const unsigned char *data = str;
@@ -958,7 +971,14 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method,
 		vpn_progress(vpninfo, PRG_ERR,
 			     _("Unexpected %d result from server\n"),
 			     result);
-		result = -EINVAL;
+		if (result == 401 || result == 403)
+			result = -EPERM;
+		else if (result == 512) /* GlobalProtect invalid username/password */
+			result = -EACCES;
+		else if (result == 513) /* GlobalProtect invalid client cert */
+			result = -EBADMSG;
+		else
+			result = -EINVAL;
 		goto out;
 	}
 
diff --git a/library.c b/library.c
index b0d635b..6e503ab 100644
--- a/library.c
+++ b/library.c
@@ -141,6 +141,16 @@ const struct vpn_proto openconnect_protos[] = {
 		.udp_send_probes = oncp_esp_send_probes,
 		.udp_catch_probe = oncp_esp_catch_probe,
 #endif
+	}, {
+		.name = "gp",
+		.pretty_name = N_("Palo Alto Networks GlobalProtect"),
+		.description = N_("Compatible with Palo Alto Networks (PAN) GlobalProtect SSL VPN"),
+		.flags = OC_PROTO_PROXY | OC_PROTO_AUTH_CERT | OC_PROTO_AUTH_OTP | OC_PROTO_AUTH_STOKEN,
+		.vpn_close_session = gpst_bye,
+		.tcp_connect = gpst_setup,
+		.tcp_mainloop = gpst_mainloop,
+		.add_http_headers = gpst_common_headers,
+		.obtain_cookie = gpst_obtain_cookie,
 	},
 	{ /* NULL */ }
 };
diff --git a/openconnect-internal.h b/openconnect-internal.h
index e96610b..96bad7c 100644
--- a/openconnect-internal.h
+++ b/openconnect-internal.h
@@ -146,6 +146,10 @@ struct pkt {
 			unsigned char pad[16];
 			unsigned char hdr[8];
 		} cstp;
+		struct {
+			unsigned char pad[8];
+			unsigned char hdr[16];
+		} gpst;
 	};
 	unsigned char data[];
 };
@@ -860,6 +864,18 @@ int oncp_bye(struct openconnect_info *vpninfo, const char *reason);
 int oncp_esp_send_probes(struct openconnect_info *vpninfo);
 int oncp_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt);
 
+/* auth-globalprotect.c */
+int gpst_obtain_cookie(struct openconnect_info *vpninfo);
+void gpst_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
+int gpst_bye(struct openconnect_info *vpninfo, const char *reason);
+
+/* gpst.c */
+int gpst_xml_or_error(struct openconnect_info *vpninfo, int result, char *response,
+					  int (*xml_cb)(struct openconnect_info *, xmlNode *xml_node),
+					  char **prompt, char **inputStr);
+int gpst_setup(struct openconnect_info *vpninfo);
+int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout);
+
 /* lzs.c */
 int lzs_decompress(unsigned char *dst, int dstlen, const unsigned char *src, int srclen);
 int lzs_compress(unsigned char *dst, int dstlen, const unsigned char *src, int srclen);
@@ -1017,6 +1033,7 @@ int get_utf8char(const char **utf8);
 void buf_append_from_utf16le(struct oc_text_buf *buf, const void *utf16);
 void buf_truncate(struct oc_text_buf *buf);
 void buf_append_urlencoded(struct oc_text_buf *buf, const char *str);
+void buf_append_xmlescaped(struct oc_text_buf *buf, const char *str);
 int buf_error(struct oc_text_buf *buf);
 int buf_free(struct oc_text_buf *buf);
 char *openconnect_create_useragent(const char *base);
diff --git a/openconnect.8.in b/openconnect.8.in
index c97dec2..5e1b933 100644
--- a/openconnect.8.in
+++ b/openconnect.8.in
@@ -422,11 +422,12 @@ Select VPN protocol
 .I PROTO
 to be used for the connection. Supported protocols are
 .I anyconnect
-for Cisco AnyConnect (the default), and
+for Cisco AnyConnect (the default),
 .I nc
 for experimental support for Juniper Network Connect (also supported
-by Junos Pulse servers).
-
+by Junos Pulse servers), and
+.I gp
+for experimental support for PAN GlobalProtect.
 .TP
 .B \-\-token\-mode=MODE
 Enable one-time password generation using the
diff --git a/www/Makefile.am b/www/Makefile.am
index 51a242b..f791a00 100644
--- a/www/Makefile.am
+++ b/www/Makefile.am
@@ -6,7 +6,7 @@ CONV 	= "$(srcdir)/html.py"
 FTR_PAGES = csd.html charset.html token.html pkcs11.html tpm.html features.html gui.html nonroot.html
 START_PAGES = building.html connecting.html manual.html vpnc-script.html 
 INDEX_PAGES = changelog.html download.html index.html packages.html platforms.html
-PROTO_PAGES = anyconnect.html juniper.html
+PROTO_PAGES = anyconnect.html juniper.html globalprotect.html
 TOPLEVEL_PAGES = contribute.html mail.html
 
 ALL_PAGES = $(FTR_PAGES) $(START_PAGES) $(INDEX_PAGES) $(TOPLEVEL_PAGES) $(PROTO_PAGES)
diff --git a/www/globalprotect.xml b/www/globalprotect.xml
new file mode 100644
index 0000000..408eb2e
--- /dev/null
+++ b/www/globalprotect.xml
@@ -0,0 +1,64 @@
+<PAGE>
+	<INCLUDE file="inc/header.tmpl" />
+
+	<VAR match="VAR_SEL_PROTOCOLS" replace="selected" />
+	<VAR match="VAR_SEL_GLOBALPROTECT" replace="selected" />
+	<PARSE file="menu1.xml" />
+	<PARSE file="menu2-protocols.xml" />
+
+	<INCLUDE file="inc/content.tmpl" />
+
+<h1>PAN GlobalProtect</h1>
+
+<h2>How the VPN works</h2>
+
+<p>This VPN is based on HTTPS and <a
+href="https://tools.ietf.org/html/rfc3948">ESP</a>, with routing and
+configuration information distributed in XML format.</p>
+
+<p>To authenticate, you connect to the secure web server (<tt>POST
+/ssl-vpn/login.esp</tt>), provide a username, password, and (optionally) a
+certificate, and receive an authcookie.  The username, authcookie, and a
+couple other bits of information obtained at login are combined into the
+OpenConnect cookie.</p>
+
+<p>To connect to the secure tunnel, the cookie is used to read routing and
+tunnel configuration information (<tt>POST /ssl-vpn/getconfig.esp</tt>).</p>
+
+<p>Finally, either an HTTPS-based or ESP-based tunnel is setup:</p>
+
+<ol>
+  <li>The cookie is used in a non-standard HTTP request (<tt>GET
+      /ssl-tunnel-connect.sslvpn</tt>, which acts more like a
+      <tt>CONNECT</tt>).  Arbitrary IP packets can be passed over the
+      resulting tunnel.</li>
+  <li>The ESP keys provided by the configuration request are used to set up
+      a <a href="https://tools.ietf.org/html/rfc3948">UDP-encapsulated
+      ESP</a> tunnel.</li>
+</ol>
+
+<p>This version of OpenConnect supports <b>only</b> the HTTPS tunnel.</p>
+
+<h2>Quirks and issues</h2>
+
+<p>There appears to be no reasonable mechanism to negotiate the <a
+href="https://en.wikipedia.org/wiki/Maximum_transmission_unit">MTU</a> for
+the link, or discover the MTU of the accessed network.  The configuration
+always shows <tt><![CDATA[&lt;mtu&gt;0&lt;/mtu&gt;]]></tt>.  OpenConnect attempts to
+calculate the MTU by starting from the base MTU with the overhead of
+encapsulating each packets within ESP, UDP, and IP.</p>
+
+<p>There is currently no IPv6 support.  <a
+href="https://live.paloaltonetworks.com/t5/Learning-Articles/IPv6-Support-on-the-Palo-Alto-Networks-Firewall/ta-p/52994">PAN's
+documentation</a> suggests that recent versions of GlobalProtect may support
+IPv6 over the ESP tunnel, though not the HTTPS tunnel.</p>
+
+<p>Compared to the AnyConnect or Juniper protocols, the GlobalProtect
+protocol appears to have very little in the way of <a
+href="https://en.wikipedia.org/wiki/In-band_signaling">in-band
+signaling</a>.  The HTTPS tunnel can only send or receive IPv4 packets and a
+simple DPD/keepalive packet (always sent by the client and echoed by the
+server).</p>
+
+	<INCLUDE file="inc/footer.tmpl" />
+</PAGE>
diff --git a/www/mail.xml b/www/mail.xml
index 3cb1a13..5ce2a13 100644
--- a/www/mail.xml
+++ b/www/mail.xml
@@ -43,7 +43,9 @@
 	  automatically filter this out of the debugging output for you.
 	</p>
 	<p>For Juniper VPN, the equivalent is a <tt>DSID</tt> cookie, which is not yet filtered
-	out of any output <i>(the authentication support in Juniper is still very new)</i>.</p>
+	out of any output <i>(the authentication support in Juniper is still very new)</i>.
+	For PAN GlobalConnect, the equivalent is a URL-encoded
+	<tt>authcookie</tt> parameter, which is also not filtered out of any output.</p>
 
 	<h1>Internet Relay Chat (IRC)</h1>
 
diff --git a/www/menu2-protocols.xml b/www/menu2-protocols.xml
index 6ac7e4f..2e51cb5 100644
--- a/www/menu2-protocols.xml
+++ b/www/menu2-protocols.xml
@@ -2,5 +2,6 @@
 	<STARTMENU level="2"/>
         <MENU topic="AnyConnect" link="anyconnect.html" mode="VAR_SEL_ANYCONNECT" />
         <MENU topic="Juniper" link="juniper.html" mode="VAR_SEL_JUNIPER" />
+        <MENU topic="GlobalProtect" link="globalprotect.html" mode="VAR_SEL_GLOBALPROTECT" />
 	<ENDMENU />
 </PAGE>
-- 
2.7.4



From ctwise at gmail.com  Thu Apr 12 05:34:03 2018
From: ctwise at gmail.com (Charles Wise)
Date: Thu, 12 Apr 2018 08:34:03 -0400
Subject: Unknown DTLS packets
Message-ID: <CA+V=dZ8e06nLJ_eMLUrMWKZ1j-JwnEd=hmd6E9zoK1+2N6OFXA@mail.gmail.com>

Hello, I'm using the latest version - OpenConnect version
v7.08-unknown - on FreeBSD ARM. I have to disable DTLS (--no-dtls) or
my VPN connection is unusable and spits out lots of unknown DTLS
values. Is this a known issue? I don't know the Cisco AnyConnect
server version but I can ask my IT support guys.

Thanks - ctwise


From kosala.atapattu at gmail.com  Thu Apr 12 18:30:56 2018
From: kosala.atapattu at gmail.com (Kosala Atapattu)
Date: Fri, 13 Apr 2018 13:30:56 +1200
Subject: Second password with STDIN
Message-ID: <CAP7S5Ha2raemtWxUOoKPLZA1Kp2CerZzWT2n1zCs7-F0rVzaLg@mail.gmail.com>

Hi,

I'm trying to use CLI mode to connect to my VPN using Duo
authentication. I can do this easily with the interactive mode and
that works fine. Is there a way to use --passwd-on-stdin with a second
password? How can I specify my second password?

Cheers,
Kosala


From dlenski at gmail.com  Thu Apr 12 19:14:14 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 12 Apr 2018 19:14:14 -0700
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <1523039030.7862.254.camel@infradead.org>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
Message-ID: <CAOw_LSGiHDa2+7NSod1cOn20AVQUj7OqyHttT-_hHuW2CHMEQQ@mail.gmail.com>

On Fri, Apr 6, 2018 at 11:23 AM, David Woodhouse <dwmw2 at infradead.org> wrote:
> Where *are* we with merging it? I did some heckling
> at the last round of patches as there was some string allocation
> confusion, and it looked like it hadn't been run in valgrind. Did you
> give me another set after that?

David,
I fixed a couple memory leaks with valgrind and a quick-and-dirty
"GlobalProtect server simulator" (<shudder>) that I wrote with Flask.

I can give you a whole new series of patches but first, to avoid
confusion, let me know?

- Do you want everything squashed into a clean series on top of your
'master' branch?
- Or do you want a series on top of your 'gpst' branch? (which
includes a patch from you on top of my previous series)
- Or something else?

Also, don't forget this other patch I sent for oversize ESP packets ?
not GP-specific even though it was first noticed on a GP VPN :-D
http://lists.infradead.org/pipermail/openconnect-devel/2018-March/004806.html

Thanks,
Dan


From dlenski at gmail.com  Thu Apr 12 19:23:45 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 12 Apr 2018 19:23:45 -0700
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <BN3PR0401MB149670321416E28058A356FFBDBD0@BN3PR0401MB1496.namprd04.prod.outlook.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
 <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
 <BN3PR0401MB149670321416E28058A356FFBDBD0@BN3PR0401MB1496.namprd04.prod.outlook.com>
Message-ID: <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>

On Wed, Apr 11, 2018 at 8:14 AM, Luis l <chelapa at hotmail.com> wrote:
>
> Thank you guys, I wasnt sure where to post it so any guidance would help.
>
>
> So yes Okta / IDP = SSO = Multifactor Auth doesnt work
>
>
> I saw that in the link i pasted they get presented with it, but if its still not an official release to OC then i will either wait or find another way for linux users to connect to vpn. which sucks bc i would rather use OC. Let me know what info is needed to maybe get this working.
>
>
> thank you!

Luis,
Other users have reported similar issues with external authentication
flows in GlobalProtect.

They're all different, but what they all have in common is that the
user goes through web-based authentication forms, and then at the end
they get some kind of cookie ("portal-userauthcookie",
"prelogin-cookie", etc.) which then needs to be used _in place of the
normal password_ to login.

Another user wrote some scripts to do the login with Okta, and I came
up with a way to submit the resulting cookie. See this discussion and
please give us feedback on whether the solution works for you:
https://github.com/dlenski/openconnect/issues/98

-Dan


From dlenski at gmail.com  Thu Apr 12 19:28:16 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 12 Apr 2018 19:28:16 -0700
Subject: Unknown DTLS packets
In-Reply-To: <CA+V=dZ8e06nLJ_eMLUrMWKZ1j-JwnEd=hmd6E9zoK1+2N6OFXA@mail.gmail.com>
References: <CA+V=dZ8e06nLJ_eMLUrMWKZ1j-JwnEd=hmd6E9zoK1+2N6OFXA@mail.gmail.com>
Message-ID: <CAOw_LSGAS1T6zGvYAa_dK7bAm7Q1mM=3DNEyLR07FuNs78A1kA@mail.gmail.com>

On Thu, Apr 12, 2018 at 5:34 AM, Charles Wise <ctwise at gmail.com> wrote:
> Hello, I'm using the latest version - OpenConnect version
> v7.08-unknown - on FreeBSD ARM.

Is that big-endian or little-endian? (Have you tested on a more
conventional system, say, Linux or BSD on arm64?)

> I have to disable DTLS (--no-dtls) or
> my VPN connection is unusable and spits out lots of unknown DTLS
> values. Is this a known issue?

Add -vvvv --dump to show the highest level of logging detail, and post
the relevant parts of the log.

> I don't know the Cisco AnyConnect
> server version but I can ask my IT support guys.

I wrote this tool which can "fingerpint" the versions of many Cisco
ASA devices, among other SSL VPN servers:
https://github.com/dlenski/what-vpn

Dan


From dlenski at gmail.com  Thu Apr 12 19:35:22 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 12 Apr 2018 19:35:22 -0700
Subject: [PATCH v2] Allow specifying server in configuration file
In-Reply-To: <20180404000349.GB19931@bebop>
References: <20180403060317.GA31272@bebop>
 <CAOw_LSExN2064EU3VJEVHODyjVPznxwbDz8K+ZHe-Z6=2MrFHw@mail.gmail.com>
 <20180403203724.GA19931@bebop>
 <CAOw_LSEMK7hc3gsh9PzfpVj6bpaA=OzFEvuBwYFnjmqbuxaGdg@mail.gmail.com>
 <1522797052.3773.51.camel@infradead.org> <20180404000349.GB19931@bebop>
Message-ID: <CAOw_LSEXaz8n6wBFakp6dy-sFW6=-CFA_5zn7A54657f=JNG6A@mail.gmail.com>

On Tue, Apr 3, 2018 at 5:03 PM, Max Rees <maxcrees at me.com> wrote:
> On Apr 04 12:10 AM, David Woodhouse wrote:
>> I'd probably be inclined to suggest we should accept whatever comes
>> last. We don't reject multiple '--server xxx' options, do we? It allows
>> the value in the config file to be overridden on the command line.
>
> Here is an updated version of the patch that always takes the last
> argument without a corresponding --option (if there is such an argument
> present) as the server URL regardless of any previous options.
>
> Max
>
> From fa9c058ed259e2d76fe35c993b155e6bd6c49f50 Mon Sep 17 00:00:00 2001
> From: Max Rees <maxcrees at me.com>
> Date: Mon, 2 Apr 2018 01:28:16 -0400
> Subject: [PATCH] Allow specifying server in configuration file
>
> This allows the configuration file to have an entry of the form:
> server https://server[:port][/group]
> similar to the CLI invocation.
>
> Signed-off-by: Max Rees <maxcrees at me.com>
> ---
>  main.c | 13 +++++++++++--
>  1 file changed, 11 insertions(+), 2 deletions(-)
>
> diff --git a/main.c b/main.c
> index d09efd5..7d41811 100644
> --- a/main.c
> +++ b/main.c
> @@ -187,6 +187,7 @@ enum {
>         OPT_HTTP_AUTH,
>         OPT_LOCAL_HOSTNAME,
>         OPT_PROTOCOL,
> +       OPT_SERVER,
>         OPT_PASSTOS,
>  };
>
> @@ -269,6 +270,7 @@ static const struct option long_options[] = {
>         OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP),
>         OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST),
>         OPTION("protocol", 1, OPT_PROTOCOL),
> +       OPTION("server", 1, OPT_SERVER),
>  #ifdef OPENCONNECT_GNUTLS
>         OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG),
>  #endif
> @@ -1164,6 +1166,10 @@ int main(int argc, char **argv)
>                         if (openconnect_set_protocol(vpninfo, config_arg))
>                                 exit(1);
>                         break;
> +               case OPT_SERVER:
> +                       if (openconnect_parse_url(vpninfo, config_arg))
> +                               exit(1);
> +                       break;
>                 case OPT_JUNIPER:
>                         fprintf(stderr, "WARNING: Juniper Network Connect support is experimental.\n");
>                         fprintf(stderr, "It will probably be superseded by Junos Pulse support.\n");
> @@ -1457,7 +1463,7 @@ int main(int argc, char **argv)
>         if (optind < argc - 1) {
>                 fprintf(stderr, _("Too many arguments on command line\n"));
>                 usage();
> -       } else if (optind > argc - 1) {
> +       } else if (optind > argc - 1 && !vpninfo->hostname) {
>                 fprintf(stderr, _("No server specified\n"));
>                 usage();
>         }
> @@ -1513,7 +1519,10 @@ int main(int argc, char **argv)
>         if (config_lookup_host(vpninfo, argv[optind]))
>                 exit(1);
>
> -       if (!vpninfo->hostname) {
> +       /* The last argument without a corresponding --option is taken
> +        * to be the server URL and overrides any --server option on the
> +        * command line or from a --config */
> +       if (!vpninfo->hostname || optind < argc) {
>                 char *url = strdup(argv[optind]);
>
>                 if (openconnect_parse_url(vpninfo, url))
> --
> 2.15.0

Works for me.

Acked-by: Daniel Lenski <dlenski at gmail.com>


From dlenski at gmail.com  Thu Apr 12 19:54:21 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 12 Apr 2018 19:54:21 -0700
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <CAOw_LSGiHDa2+7NSod1cOn20AVQUj7OqyHttT-_hHuW2CHMEQQ@mail.gmail.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
 <CAOw_LSGiHDa2+7NSod1cOn20AVQUj7OqyHttT-_hHuW2CHMEQQ@mail.gmail.com>
Message-ID: <CAOw_LSF8t4_RdHyhCbSs8NqJBz6j3A0rqS4Q3+nTUEtDcMjF+Q@mail.gmail.com>

On Thu, Apr 12, 2018 at 7:14 PM, Daniel Lenski <dlenski at gmail.com> wrote:
> On Fri, Apr 6, 2018 at 11:23 AM, David Woodhouse <dwmw2 at infradead.org> wrote:
>> Where *are* we with merging it? I did some heckling
>> at the last round of patches as there was some string allocation
>> confusion, and it looked like it hadn't been run in valgrind. Did you
>> give me another set after that?
>
> David,
> I fixed a couple memory leaks with valgrind and a quick-and-dirty
> "GlobalProtect server simulator" (<shudder>) that I wrote with Flask.
>
> I can give you a whole new series of patches but first, to avoid
> confusion, let me know?
>
> - Do you want everything squashed into a clean series on top of your
> 'master' branch?
> - Or do you want a series on top of your 'gpst' branch? (which
> includes a patch from you on top of my previous series)
> - Or something else?
>
> Also, don't forget this other patch I sent for oversize ESP packets ?
> not GP-specific even though it was first noticed on a GP VPN :-D
> http://lists.infradead.org/pipermail/openconnect-devel/2018-March/004806.html

Assuming the right answer is "squash everything into a clean series of
patches on top of your 'master' branch", please see here:

    https://github.com/dlenski/openconnect/commits/gpst-squash

These are rebased on top of your 'master', and get a clean bill of
health from valgrind.

Dan


From dlenski at gmail.com  Thu Apr 12 20:40:17 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 12 Apr 2018 20:40:17 -0700
Subject: Unknown DTLS packets
In-Reply-To: <CA+V=dZ-UfFXMz76hVHkEevSNRzG0USqTT1zwGMfresTM=XmmGg@mail.gmail.com>
References: <CA+V=dZ8e06nLJ_eMLUrMWKZ1j-JwnEd=hmd6E9zoK1+2N6OFXA@mail.gmail.com>
 <CAOw_LSGAS1T6zGvYAa_dK7bAm7Q1mM=3DNEyLR07FuNs78A1kA@mail.gmail.com>
 <CA+V=dZ-UfFXMz76hVHkEevSNRzG0USqTT1zwGMfresTM=XmmGg@mail.gmail.com>
Message-ID: <CAOw_LSFwsaj+VMQ4cBBUc=0mZqZFh_54ffjh02aKzGka+dUygQ@mail.gmail.com>

On Thu, Apr 12, 2018 at 8:18 PM, Charles Wise <ctwise at gmail.com> wrote:
> Looks like it's the MTU. I did the -vvvv and --dump and the output
> said the MTU should be 1322 (DTLS option X-DTLS-MTU : 1322). When I
> enable DTLS and _don't_ set the MTU, I run iperf3 and the traffic
> drops to zero almost immediately. When I set it explicitly (-m 1322)
> the traffic goes through (plus it's much faster then with --no-dtls).

I'm glad it works, but I'm still confused.

1. If the server says the MTU should be 1322 (via the "X-DTLS-MTU"
header), then openconnect (via the vpnc-script) is setting the MTU of
the interface to 1322.
2. If you specify `-m 1322` explicitly, openconnect (via the
vpnc-script) is also setting the MTU of the interface to 1322.

What is happening differently when you specify the MTU explicitly?
What do the operating system configuration utilities say that the MTU
of the tunnel device is in (1) vs (2)?

Dan


From cgrundman at gmail.com  Fri Apr 13 05:38:26 2018
From: cgrundman at gmail.com (Chaskiel Grundman)
Date: Fri, 13 Apr 2018 08:38:26 -0400
Subject: Unknown DTLS packets
In-Reply-To: <CAOw_LSFwsaj+VMQ4cBBUc=0mZqZFh_54ffjh02aKzGka+dUygQ@mail.gmail.com>
References: <CA+V=dZ8e06nLJ_eMLUrMWKZ1j-JwnEd=hmd6E9zoK1+2N6OFXA@mail.gmail.com>
 <CAOw_LSGAS1T6zGvYAa_dK7bAm7Q1mM=3DNEyLR07FuNs78A1kA@mail.gmail.com>
 <CA+V=dZ-UfFXMz76hVHkEevSNRzG0USqTT1zwGMfresTM=XmmGg@mail.gmail.com>
 <CAOw_LSFwsaj+VMQ4cBBUc=0mZqZFh_54ffjh02aKzGka+dUygQ@mail.gmail.com>
Message-ID: <CA+-VZgDif4HPscU837Z9qNa7RQ9CppsxhpyuSkAn38ZaeRzpjw@mail.gmail.com>

It's possible that this is related to the issue I reported in january
(http://lists.infradead.org/pipermail/openconnect-devel/2018-January/004647.html),
which involves a bug in gnutls. The bug has been fixed upstream, but
debian stable and ubuntu have not taken new versions of gnutls 3.5 or
3.6 since that time.

openconnect could hack around this by adjusting the values passed to
gnutls_set_data_mtu upward until gnutls_get_data_mtu returns a value
that is at least what was requested by the gateway in X-DTLS-MTU

On Thu, Apr 12, 2018 at 11:40 PM, Daniel Lenski <dlenski at gmail.com> wrote:
> On Thu, Apr 12, 2018 at 8:18 PM, Charles Wise <ctwise at gmail.com> wrote:
>> Looks like it's the MTU. I did the -vvvv and --dump and the output
>> said the MTU should be 1322 (DTLS option X-DTLS-MTU : 1322). When I
>> enable DTLS and _don't_ set the MTU, I run iperf3 and the traffic
>> drops to zero almost immediately. When I set it explicitly (-m 1322)
>> the traffic goes through (plus it's much faster then with --no-dtls).
>
> I'm glad it works, but I'm still confused.
>
> 1. If the server says the MTU should be 1322 (via the "X-DTLS-MTU"
> header), then openconnect (via the vpnc-script) is setting the MTU of
> the interface to 1322.
> 2. If you specify `-m 1322` explicitly, openconnect (via the
> vpnc-script) is also setting the MTU of the interface to 1322.
>
> What is happening differently when you specify the MTU explicitly?
> What do the operating system configuration utilities say that the MTU
> of the tunnel device is in (1) vs (2)?
>
> Dan
>
> _______________________________________________
> openconnect-devel mailing list
> openconnect-devel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/openconnect-devel


From chelapa at hotmail.com  Fri Apr 13 07:54:59 2018
From: chelapa at hotmail.com (Luis l)
Date: Fri, 13 Apr 2018 14:54:59 +0000
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
 <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
 <BN3PR0401MB149670321416E28058A356FFBDBD0@BN3PR0401MB1496.namprd04.prod.outlook.com>,
 <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>
Message-ID: <BN3PR0401MB1496F2577AD3FA2705F2F112BDB30@BN3PR0401MB1496.namprd04.prod.outlook.com>

Not sure where the instructions are for the specific commit. Currently can't find those files after a recent fetch 




From: Daniel Lenski <dlenski at gmail.com>
Sent: Friday, April 13, 2018 2:23 AM
To: Luis l
Cc: David Woodhouse; openconnect-devel
Subject: Re: Openconnect - Palo Alto - Okta SSO / MFA
? 

On Wed, Apr 11, 2018 at 8:14 AM, Luis l <chelapa at hotmail.com> wrote:
>
> Thank you guys, I wasnt sure where to post it so any guidance would help.
>
>
> So yes Okta / IDP = SSO = Multifactor Auth doesnt work
>
>
> I saw that in the link i pasted they get presented with it, but if its still not an official release to OC then i will either wait or find another way for linux users to connect to vpn. which sucks bc i would rather use OC. Let me know what info is needed  to maybe get this working.
>
>
> thank you!

Luis,
Other users have reported similar issues with external authentication
flows in GlobalProtect.

They're all different, but what they all have in common is that the
user goes through web-based authentication forms, and then at the end
they get some kind of cookie ("portal-userauthcookie",
"prelogin-cookie", etc.) which then needs to be used _in place of the
normal password_ to login.

Another user wrote some scripts to do the login with Okta, and I came
up with a way to submit the resulting cookie. See this discussion and
please give us feedback on whether the solution works for you:
https://github.com/dlenski/openconnect/issues/98

-Dan
    

From chelapa at hotmail.com  Fri Apr 13 08:02:20 2018
From: chelapa at hotmail.com (Luis l)
Date: Fri, 13 Apr 2018 15:02:20 +0000
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
 <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
 <BN3PR0401MB149670321416E28058A356FFBDBD0@BN3PR0401MB1496.namprd04.prod.outlook.com>,
 <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>
Message-ID: <BN3PR0401MB1496FE47E871C7D9C3F2C093BDB30@BN3PR0401MB1496.namprd04.prod.outlook.com>

Example or I just didnt have enough coffee

script_that_obtains_the_portal_userauthcookie ? cant find that and dont think thats an actual file 




From: Daniel Lenski <dlenski at gmail.com>
Sent: Friday, April 13, 2018 2:23 AM
To: Luis l
Cc: David Woodhouse; openconnect-devel
Subject: Re: Openconnect - Palo Alto - Okta SSO / MFA
? 

On Wed, Apr 11, 2018 at 8:14 AM, Luis l <chelapa at hotmail.com> wrote:
>
> Thank you guys, I wasnt sure where to post it so any guidance would help.
>
>
> So yes Okta / IDP = SSO = Multifactor Auth doesnt work
>
>
> I saw that in the link i pasted they get presented with it, but if its still not an official release to OC then i will either wait or find another way for linux users to connect to vpn. which sucks bc i would rather use OC. Let me know what info is needed  to maybe get this working.
>
>
> thank you!

Luis,
Other users have reported similar issues with external authentication
flows in GlobalProtect.

They're all different, but what they all have in common is that the
user goes through web-based authentication forms, and then at the end
they get some kind of cookie ("portal-userauthcookie",
"prelogin-cookie", etc.) which then needs to be used _in place of the
normal password_ to login.

Another user wrote some scripts to do the login with Okta, and I came
up with a way to submit the resulting cookie. See this discussion and
please give us feedback on whether the solution works for you:
https://github.com/dlenski/openconnect/issues/98

-Dan
    

From chelapa at hotmail.com  Fri Apr 13 08:31:29 2018
From: chelapa at hotmail.com (Luis l)
Date: Fri, 13 Apr 2018 15:31:29 +0000
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
 <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
 <BN3PR0401MB149670321416E28058A356FFBDBD0@BN3PR0401MB1496.namprd04.prod.outlook.com>,
 <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>
Message-ID: <BN3PR0401MB14965470426CE0CF839B087BBDB30@BN3PR0401MB1496.namprd04.prod.outlook.com>

After digging around i THINK its a part of this?

https://github.com/arthepsy/pan-globalprotect-okta/

I downloaded it added the totp of that moment, removed pw to prompt me instead of conf and i get the below from debug = 1. My "Guess" if this worked its to be used against the command i sent prior and piped into the openconnect cmd? 

---
# status:
MFA_REQUIRED
---
err: no factor url found


From: Daniel Lenski <dlenski at gmail.com>
Sent: Friday, April 13, 2018 2:23 AM
To: Luis l
Cc: David Woodhouse; openconnect-devel
Subject: Re: Openconnect - Palo Alto - Okta SSO / MFA
? 

On Wed, Apr 11, 2018 at 8:14 AM, Luis l <chelapa at hotmail.com> wrote:
>
> Thank you guys, I wasnt sure where to post it so any guidance would help.
>
>
> So yes Okta / IDP = SSO = Multifactor Auth doesnt work
>
>
> I saw that in the link i pasted they get presented with it, but if its still not an official release to OC then i will either wait or find another way for linux users to connect to vpn. which sucks bc i would rather use OC. Let me know what info is needed  to maybe get this working.
>
>
> thank you!

Luis,
Other users have reported similar issues with external authentication
flows in GlobalProtect.

They're all different, but what they all have in common is that the
user goes through web-based authentication forms, and then at the end
they get some kind of cookie ("portal-userauthcookie",
"prelogin-cookie", etc.) which then needs to be used _in place of the
normal password_ to login.

Another user wrote some scripts to do the login with Okta, and I came
up with a way to submit the resulting cookie. See this discussion and
please give us feedback on whether the solution works for you:
https://github.com/dlenski/openconnect/issues/98

-Dan
    

From dlenski at gmail.com  Fri Apr 13 10:49:46 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Fri, 13 Apr 2018 10:49:46 -0700
Subject: Unknown DTLS packets
In-Reply-To: <CA+V=dZ9rFnXdmzG1JKFfEu6Vr184gLRCf3sVxSTs+Vir8AFERA@mail.gmail.com>
References: <CA+V=dZ8e06nLJ_eMLUrMWKZ1j-JwnEd=hmd6E9zoK1+2N6OFXA@mail.gmail.com>
 <CAOw_LSGAS1T6zGvYAa_dK7bAm7Q1mM=3DNEyLR07FuNs78A1kA@mail.gmail.com>
 <CA+V=dZ-UfFXMz76hVHkEevSNRzG0USqTT1zwGMfresTM=XmmGg@mail.gmail.com>
 <CAOw_LSFwsaj+VMQ4cBBUc=0mZqZFh_54ffjh02aKzGka+dUygQ@mail.gmail.com>
 <CA+V=dZ9rFnXdmzG1JKFfEu6Vr184gLRCf3sVxSTs+Vir8AFERA@mail.gmail.com>
Message-ID: <CAOw_LSFPYoDuA_ZTQgQyp64=GPNZPqNDqwKB14zfhVrUPm3+hQ@mail.gmail.com>

On Fri, Apr 13, 2018 at 5:39 AM, Charles Wise <ctwise at gmail.com> wrote:
> I'm confused as well. But it reliably fails w/o setting the explicit
> MTU in the arguments.
>
> What command(s) do I run to tell what MTU value is _really_ being
> used? This is FreeBSD 11.1-RELEASE-p7 (pfSense firewall/router).
>
> BTW, I was mistaken in my original post - this isn't ARM, it's an AMD x86-64.
>

On Linux the ifconfig command will show MTU, and I know that it at
least _exists_ on FreeBSD. (That's about all I know about FreeBSD.)

Dan


From dlenski at gmail.com  Fri Apr 13 13:06:10 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Fri, 13 Apr 2018 13:06:10 -0700
Subject: Unknown DTLS packets
In-Reply-To: <CA+V=dZ9N3gBqgwRLzZq=WRyH5ueVWM+aVoMqpvo+6DfmgvMT4w@mail.gmail.com>
References: <CA+V=dZ8e06nLJ_eMLUrMWKZ1j-JwnEd=hmd6E9zoK1+2N6OFXA@mail.gmail.com>
 <CAOw_LSGAS1T6zGvYAa_dK7bAm7Q1mM=3DNEyLR07FuNs78A1kA@mail.gmail.com>
 <CA+V=dZ-UfFXMz76hVHkEevSNRzG0USqTT1zwGMfresTM=XmmGg@mail.gmail.com>
 <CAOw_LSFwsaj+VMQ4cBBUc=0mZqZFh_54ffjh02aKzGka+dUygQ@mail.gmail.com>
 <CA+V=dZ9rFnXdmzG1JKFfEu6Vr184gLRCf3sVxSTs+Vir8AFERA@mail.gmail.com>
 <CAOw_LSFPYoDuA_ZTQgQyp64=GPNZPqNDqwKB14zfhVrUPm3+hQ@mail.gmail.com>
 <CA+V=dZ9N3gBqgwRLzZq=WRyH5ueVWM+aVoMqpvo+6DfmgvMT4w@mail.gmail.com>
Message-ID: <CAOw_LSFjF13-wxE3A-ch2Ani67=UZrFDGt=GFtHXLpM3z0oL6g@mail.gmail.com>

On Fri, Apr 13, 2018 at 11:13 AM, Charles Wise <ctwise at gmail.com> wrote:
> It shows 1322 both with and without the '-m 1322'. And now it's
> working at full speed, both with and without the '-m 1322'. I checked
> the config before and after the failures and the '-m 1322' is the only
> difference.
>
> Any ideas for anything else I can check?

Nope. Are you sure the initial problem you reported wasn't caused by
an _intermittent_ network issue?

You should keep the list cc'ed on your replies since others may have
other ideas about what else could be going on here.

Dan


From dlenski at gmail.com  Fri Apr 13 13:23:19 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Fri, 13 Apr 2018 13:23:19 -0700
Subject: Openconnect - Palo Alto - Okta SSO / MFA
In-Reply-To: <BN3PR0401MB14965470426CE0CF839B087BBDB30@BN3PR0401MB1496.namprd04.prod.outlook.com>
References: <BN3PR0401MB14964D6846B0FE052F638867BDBA0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSGzO8oEKBi=i+a+OnWo_fxY_P-3dRQHW5pCotgDobk4hQ@mail.gmail.com>
 <1523039030.7862.254.camel@infradead.org>
 <CAOw_LSHgDknuAj33YhMEK+Yh1a8A_qpOiEYZz_NiCyZh8s+9Fw@mail.gmail.com>
 <BN3PR0401MB149670321416E28058A356FFBDBD0@BN3PR0401MB1496.namprd04.prod.outlook.com>
 <CAOw_LSFWP5swQ=ZUthcmYjddoy6ECgTQ+b71drDVJ3YbYZezRA@mail.gmail.com>
 <BN3PR0401MB14965470426CE0CF839B087BBDB30@BN3PR0401MB1496.namprd04.prod.outlook.com>
Message-ID: <CAOw_LSFPy1+1OSe3Cyz1Wray4K85XHoe9WqOWQZ9Vea=C1g9kg@mail.gmail.com>

On Fri, Apr 13, 2018 at 8:31 AM, Luis l <chelapa at hotmail.com> wrote:
> After digging around i THINK its a part of this?
>
> https://github.com/arthepsy/pan-globalprotect-okta/
>
> I downloaded it added the totp of that moment, removed pw to prompt me instead of conf and i get the below from debug = 1. My "Guess" if this worked its to be used against the command i sent prior and piped into the openconnect cmd?
>
> ---
> # status:
> MFA_REQUIRED
> ---
> err: no factor url found

Luis,

I have a lot of trouble following your explanations here, but? yes,
you need to figure out a way to generate the appropriate cookie and
submit it to openconnect in place of the password, using the new
mechanism that I added in the fun_with_cookies branch, as described on
Github.

I don't use Okta, can't use Okta, and know nothing about Okta. I do
not have access to a GP VPN that uses this kind of authentication
flow. So I cannot test the authentication scripts in any way.

All I can do is provide a mechanism for openconnect to accept the
cookie produced by the alternative authentication flows, and rely on
users to tell me if it solves the problem.

Dan


From n.mavrogiannopoulos at gmail.com  Sun Apr 15 02:59:34 2018
From: n.mavrogiannopoulos at gmail.com (Nikos Mavrogiannopoulos)
Date: Sun, 15 Apr 2018 11:59:34 +0200
Subject: fix the openconnect CI
Message-ID: <CAJU7zaL5yLYh+RSwzZ5-NvAF8+mRnEq31rg-L1ZBZLALU5ZVLA@mail.gmail.com>

Currently the openconnect CI contains many expected failures making CI
failure the norm. Fix that and mark expected failures as such.

regards,
Nikos
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-.gitlab-ci.yml-added-explicit-XFAIL_TESTS-for-known-.patch
Type: text/x-patch
Size: 1627 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/openconnect-devel/attachments/20180415/16062366/attachment.bin>

From lubomir.carik at gmail.com  Sun Apr 15 14:08:52 2018
From: lubomir.carik at gmail.com (=?UTF-8?q?=C4=BDubom=C3=ADr=20Carik?=)
Date: Sun, 15 Apr 2018 23:08:52 +0200
Subject: [PATCH 0/1] vpnc-script output on win32
Message-ID: <20180415210853.13944-1-Lubomir.Carik@gmail.com>

Hi David, team,

this change is related to windows/mingw only.
The vpnc-script write the output to console in this implementation.
It is fine for console app, but it isn't suitable for windows GUI apps.

This patch try to redirect the outputs, read them and use openconnect
logging mechanism with info/error log-level to store them.
In console it will go to output, but in GUI we can read output
via progress callback.

Please review/comment.

Thanks, Best Regards,
Lubomir.


?ubom?r Carik (1):
  Apply application logging approach for vpnc-script output

 openconnect.h |   2 ++
 script.c      | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 94 insertions(+), 12 deletions(-)

-- 
2.16.2



From lubomir.carik at gmail.com  Sun Apr 15 14:08:53 2018
From: lubomir.carik at gmail.com (=?UTF-8?q?=C4=BDubom=C3=ADr=20Carik?=)
Date: Sun, 15 Apr 2018 23:08:53 +0200
Subject: [PATCH 1/1] Apply application logging approach for vpnc-script output
In-Reply-To: <20180415210853.13944-1-Lubomir.Carik@gmail.com>
References: <20180415210853.13944-1-Lubomir.Carik@gmail.com>
Message-ID: <20180415210853.13944-2-Lubomir.Carik@gmail.com>

- inherit handlers for child process
- update close app handles
- update flags

Signed-off-by: ?ubom?r Carik <Lubomir.Carik at gmail.com>
---
 openconnect.h |   2 ++
 script.c      | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++-------
 2 files changed, 94 insertions(+), 12 deletions(-)

diff --git a/openconnect.h b/openconnect.h
index e97dacbc..75dfdd18 100644
--- a/openconnect.h
+++ b/openconnect.h
@@ -29,6 +29,8 @@ extern "C" {
 #endif
 
 #ifdef _WIN32
+#include <winsock2.h>
+
 #define uid_t unsigned
 #endif
 
diff --git a/script.c b/script.c
index 5b751ad5..695ebd38 100644
--- a/script.c
+++ b/script.c
@@ -24,6 +24,8 @@
 #include <unistd.h>
 #ifndef _WIN32
 #include <sys/wait.h>
+#else
+#include <stdbool.h>
 #endif
 #include <errno.h>
 #include <ctype.h>
@@ -32,6 +34,15 @@
 
 #include "openconnect-internal.h"
 
+#ifdef _WIN32
+#define BUFSIZE 4096
+
+HANDLE g_hChildStd_OUT_Rd = NULL;
+HANDLE g_hChildStd_OUT_Wr = NULL;
+HANDLE g_hChildStd_ERR_Rd = NULL;
+HANDLE g_hChildStd_ERR_Wr = NULL;
+#endif
+
 int script_setenv(struct openconnect_info *vpninfo,
 		  const char *opt, const char *val, int append)
 {
@@ -459,17 +470,52 @@ int script_config_tun(struct openconnect_info *vpninfo, const char *reason)
 	int ret;
 	char *cmd;
 	PROCESS_INFORMATION pi;
+	SECURITY_ATTRIBUTES sa;
 	STARTUPINFOW si;
 	DWORD cpflags;
 
+	DWORD dwRead = 0;
+	CHAR chBuf[BUFSIZE];
+	bool bSuccess = FALSE;
+	char* token = NULL;
+
 	if (!vpninfo->vpnc_script || vpninfo->script_tun)
 		return 0;
 
-	memset(&si, 0, sizeof(si));
-	si.cb = sizeof(si);
+	// Set the bInheritHandle flag so pipe handles are inherited.
+	sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+	sa.bInheritHandle = TRUE;
+	sa.lpSecurityDescriptor = NULL;
+	// Create a pipe for the child process's STDERR.
+	if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &sa, 0)) {
+		return -EPIPE;
+	}
+	// Ensure the read handle to the pipe for STDERR is not inherited.
+	if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) {
+		return -EPIPE;
+	}
+	// Create a pipe for the child process's STDOUT.
+	if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &sa, 0)) {
+		return -EPIPE;
+	}
+	// Ensure the read handle to the pipe for STDOUT is not inherited
+	if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) {
+		return -EPIPE;
+	}
+
+	// Set up members of the PROCESS_INFORMATION structure.
+	ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
+
+	// Set up members of the STARTUPINFO structure.
+	// This structure specifies the STDERR and STDOUT handles for redirection.
+	ZeroMemory(&si, sizeof(STARTUPINFO));
+	si.cb = sizeof(STARTUPINFO);
 	/* probably superfluous */
-	si.dwFlags = STARTF_USESHOWWINDOW;
+	si.dwFlags |= STARTF_USESHOWWINDOW;
+	si.dwFlags |= STARTF_USESTDHANDLES;
 	si.wShowWindow = SW_HIDE;
+	si.hStdError = g_hChildStd_ERR_Wr;
+	si.hStdOutput = g_hChildStd_OUT_Wr;
 
 	script_setenv(vpninfo, "reason", reason, 0);
 
@@ -495,19 +541,53 @@ int script_config_tun(struct openconnect_info *vpninfo, const char *reason)
 	if (!GetConsoleWindow())
 		cpflags |= CREATE_NO_WINDOW;
 
-	if (CreateProcessW(NULL, script_w, NULL, NULL, FALSE, cpflags,
-			   script_env, NULL, &si, &pi)) {
-		ret = WaitForSingleObject(pi.hProcess,10000);
-		CloseHandle(pi.hThread);
-		CloseHandle(pi.hProcess);
-		if (ret == WAIT_TIMEOUT)
-			ret = -ETIMEDOUT;
-		else
-			ret = 0;
+	if (CreateProcessW(NULL,
+		script_w, // command line
+		NULL, // process security attributes
+		NULL, // primary thread security attributes
+		TRUE, // handles are inherited
+		cpflags, // creation flags
+		script_env, // use parent's environment
+		NULL, // use parent's current directory
+		&si, // STARTUPINFO pointer
+		&pi) // receives PROCESS_INFORMATION
+        ) {
+		CloseHandle(g_hChildStd_ERR_Wr);
+		CloseHandle(g_hChildStd_OUT_Wr);
+		ret = 0;
 	} else {
 		ret = -EIO;
 	}
 
+	// Read from pipe that is the standard output for child process.
+	for (;;) {
+		bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
+		if (!bSuccess || dwRead == 0) break;
+
+		chBuf[dwRead] = 0;
+		token = strtok(chBuf, "\r\n");
+		while (token) {
+			vpn_progress(vpninfo, PRG_INFO,
+						 _("vpnc: %s\n"),
+						 token);
+			token = strtok(NULL, "\r\n");
+		}
+	}
+	dwRead = 0;
+	for (;;) {
+		bSuccess = ReadFile(g_hChildStd_ERR_Rd, chBuf, BUFSIZE, &dwRead, NULL);
+		if (!bSuccess || dwRead == 0) break;
+
+		chBuf[dwRead] = 0;
+		token = strtok(chBuf, "\r\n");
+		while (token) {
+			vpn_progress(vpninfo, PRG_ERR,
+						 _("vpnc: %s\n"),
+						 token);
+			token = strtok(NULL, "\r\n");
+		}
+	}
+
 	free(script_env);
 
 	if (ret < 0) {
-- 
2.16.2



From dlenski at gmail.com  Sun Apr 15 16:47:59 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Sun, 15 Apr 2018 16:47:59 -0700
Subject: [PATCH] move sending of ONCP control packets for enabling/disabling
 ESP into oncp.c
Message-ID: <1523836079-15795-1-git-send-email-dlenski@gmail.com>

This is another small case of Juniper-specific handling in the ESP code.
The ONCP protocol needs to tell the server to explicitly enable or disable
the ESP tunnel.

I don't really understand why the separate oncp_control_queue is needed,
since these control packets only get sent when dtls_state != DTLS_CONNECTED
and therefore they would always automatically get sent via the ONCP tunnel
instead of the ESP tunnel anyway.

Signed-off-by: Daniel Lenski <dlenski at gmail.com>
---
 esp.c                  |  8 ++++----
 library.c              |  2 +-
 oncp.c                 | 15 ++++++++++++++-
 openconnect-internal.h |  2 +-
 4 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/esp.c b/esp.c
index 5cea3fb..4557b79 100644
--- a/esp.c
+++ b/esp.c
@@ -192,7 +192,6 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout)
 				if (vpninfo->dtls_state == DTLS_SLEEPING) {
 					vpn_progress(vpninfo, PRG_INFO,
 						     _("ESP session established with server\n"));
-					queue_esp_control(vpninfo, 1);
 					vpninfo->dtls_state = DTLS_CONNECTING;
 				}
 				continue;
@@ -234,8 +233,8 @@ int esp_mainloop(struct openconnect_info *vpninfo, int *timeout)
 
 	case KA_DPD_DEAD:
 		vpn_progress(vpninfo, PRG_ERR, _("ESP detected dead peer\n"));
-		queue_esp_control(vpninfo, 0);
-		esp_close(vpninfo);
+		if (vpninfo->proto->udp_close)
+			vpninfo->proto->udp_close(vpninfo);
 		if (vpninfo->proto->udp_send_probes)
 			vpninfo->proto->udp_send_probes(vpninfo);
 		return 1;
@@ -310,5 +309,6 @@ void esp_shutdown(struct openconnect_info *vpninfo)
 	destroy_esp_ciphers(&vpninfo->esp_in[0]);
 	destroy_esp_ciphers(&vpninfo->esp_in[1]);
 	destroy_esp_ciphers(&vpninfo->esp_out);
-	esp_close(vpninfo);
+	if (vpninfo->proto->udp_close)
+		vpninfo->proto->udp_close(vpninfo);
 }
diff --git a/library.c b/library.c
index b0d635b..4826c66 100644
--- a/library.c
+++ b/library.c
@@ -136,7 +136,7 @@ const struct vpn_proto openconnect_protos[] = {
 #ifdef HAVE_ESP
 		.udp_setup = esp_setup,
 		.udp_mainloop = esp_mainloop,
-		.udp_close = esp_close,
+		.udp_close = oncp_esp_close,
 		.udp_shutdown = esp_shutdown,
 		.udp_send_probes = oncp_esp_send_probes,
 		.udp_catch_probe = oncp_esp_catch_probe,
diff --git a/oncp.c b/oncp.c
index bc01a3f..dbfb1ef 100644
--- a/oncp.c
+++ b/oncp.c
@@ -452,7 +452,7 @@ static const struct pkt esp_enable_pkt = {
 	.len = 13
 };
 
-int queue_esp_control(struct openconnect_info *vpninfo, int enable)
+static int queue_esp_control(struct openconnect_info *vpninfo, int enable)
 {
 	struct pkt *new = malloc(sizeof(*new) + 13);
 	if (!new)
@@ -931,6 +931,12 @@ int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout)
 	if (vpninfo->ssl_fd == -1)
 		goto do_reconnect;
 
+	/* Queue the ESP enable message. We will start sending packets
+	   via ESP once the enable message has been *sent* over the
+	   TCP channel. */
+	if (vpninfo->dtls_state == DTLS_CONNECTING)
+		queue_esp_control(vpninfo, 1);
+
 	/* FIXME: The poll() handling here is fairly simplistic. Actually,
 	   if the SSL connection stalls it could return a WANT_WRITE error
 	   on _either_ of the SSL_read() or SSL_write() calls. In that case,
@@ -1303,6 +1309,13 @@ int oncp_bye(struct openconnect_info *vpninfo, const char *reason)
 }
 
 #ifdef HAVE_ESP
+void oncp_esp_close(struct openconnect_info *vpninfo)
+{
+	/* Tell server to stop sending on ESP channel */
+	queue_esp_control(vpninfo, 0);
+	esp_close(vpninfo);
+}
+
 int oncp_esp_send_probes(struct openconnect_info *vpninfo)
 {
 	struct pkt *pkt;
diff --git a/openconnect-internal.h b/openconnect-internal.h
index e96610b..9890ff6 100644
--- a/openconnect-internal.h
+++ b/openconnect-internal.h
@@ -853,10 +853,10 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo);
 void oncp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *buf);
 
 /* oncp.c */
-int queue_esp_control(struct openconnect_info *vpninfo, int enable);
 int oncp_connect(struct openconnect_info *vpninfo);
 int oncp_mainloop(struct openconnect_info *vpninfo, int *timeout);
 int oncp_bye(struct openconnect_info *vpninfo, const char *reason);
+void oncp_esp_close(struct openconnect_info *vpninfo);
 int oncp_esp_send_probes(struct openconnect_info *vpninfo);
 int oncp_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt);
 
-- 
2.7.4



From dlenski at gmail.com  Sun Apr 15 16:50:04 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Sun, 15 Apr 2018 16:50:04 -0700
Subject: [PATCH] fix memory leak in Juniper logout function (caught with
 valgrind)
Message-ID: <1523836204-16059-1-git-send-email-dlenski@gmail.com>

Signed-off-by: Daniel Lenski <dlenski at gmail.com>
---
 oncp.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/oncp.c b/oncp.c
index dbfb1ef..c5fd34a 100644
--- a/oncp.c
+++ b/oncp.c
@@ -1297,6 +1297,7 @@ int oncp_bye(struct openconnect_info *vpninfo, const char *reason)
 	orig_path = vpninfo->urlpath;
 	vpninfo->urlpath = strdup("dana-na/auth/logout.cgi"); /* redirect segfaults without strdup */
 	ret = do_https_request(vpninfo, "GET", NULL, NULL, &res_buf, 0);
+	free(vpninfo->urlpath);
 	vpninfo->urlpath = orig_path;
 
 	if (ret < 0)
-- 
2.7.4



From vvs at vs.kiev.ua  Tue Apr 17 04:42:51 2018
From: vvs at vs.kiev.ua (Vit)
Date: Tue, 17 Apr 2018 14:42:51 +0300
Subject: error loading the certificate or key file
Message-ID: <e5f09a31-38ce-81f9-14b0-aac95eef5667@vs.kiev.ua>

Hi Group!
I configure ocserv to use Letsencrypt keys. My config part is:
-=-=-
auth = "plain[passwd=/usr/local/etc/ocserv/passwd]"
server-cert = /usr/local/etc/letsencrypt/live/site.com/fullchain.pem
server-key =? /usr/local/etc/letsencrypt/live/site.com/privkey.pem
ca-cert =? /usr/local/etc/letsencrypt/live/site.com/chain.pem
run-as-user = _ocserv
run-as-group = _ocserv
-=-=-

when i run ocserv i have errors

Apr 17 14:13:38 mx2 ocserv[69358]: main: tlslib.c:854: error loading 
file '/usr/local/etc/letsencrypt/live/site.com/fullchain.pem '
Apr 17 14:13:38 mx2 ocserv[69358]: main: tlslib.c:1000: error loading 
the certificate or key file
Apr 17 14:13:38 mx2 ocserv[69359]: sec-mod: error receiving msg head 
from main
Apr 17 14:13:38 mx2 ocserv[69359]: sec-mod: error processing sync 
command from main

i try to change user&group to exist users and have same errors.

root at mx2/usr/local/etc/ocserv> ocserv -v
ocserv 0.11.11

Compiled with: tcp-wrappers, oath, PAM, PKCS#11, AnyConnect
GnuTLS version: 3.5.18
root at mx2/usr/local/etc/ocserv>


Help me understand this situation


-- 
Regards, *Vit*


From YageH at supermicro.com  Wed Apr 18 18:08:51 2018
From: YageH at supermicro.com (Yage Hu)
Date: Thu, 19 Apr 2018 01:08:51 +0000
Subject: Fix for Juniper Google 2FA
Message-ID: <b9f86fe924294d1a94e53d142a9e0e4d@Ex2013-MBX2.supermicro.com>

The Google 2FA support for Juniper was not working due to a minor typo
in `auth-juniper.c`.

When attempting to connect with --juniper --token-mode=totp
--token-secret=<XXX> the error:

    Unknown form ID 'frmTotpToken'

would pop up along with some HTML dump.

I simply corrected the typo (ftmTotpToken -> frmTotpToken). `git am` to
apply patch.

Signed-off-by: Yage Hu <yageh at supermicro.com>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-Fix-Google-2FA-typo.patch
Type: text/x-patch
Size: 792 bytes
Desc: 0001-Fix-Google-2FA-typo.patch
URL: <http://lists.infradead.org/pipermail/openconnect-devel/attachments/20180419/fcd0ca20/attachment.bin>

From dlenski at gmail.com  Thu Apr 19 13:32:29 2018
From: dlenski at gmail.com (Daniel Lenski)
Date: Thu, 19 Apr 2018 13:32:29 -0700
Subject: Fix for Juniper Google 2FA
In-Reply-To: <b9f86fe924294d1a94e53d142a9e0e4d@Ex2013-MBX2.supermicro.com>
References: <b9f86fe924294d1a94e53d142a9e0e4d@Ex2013-MBX2.supermicro.com>
Message-ID: <CAOw_LSFBOTv0DV_32Fx=VDBhKAv6sDTB374YU94RpoKSp_kjMg@mail.gmail.com>

On Wed, Apr 18, 2018 at 6:08 PM, Yage Hu <YageH at supermicro.com> wrote:
> The Google 2FA support for Juniper was not working due to a minor typo
> in `auth-juniper.c`.
>
> When attempting to connect with --juniper --token-mode=totp
> --token-secret=<XXX> the error:
>
>     Unknown form ID 'frmTotpToken'
>
> would pop up along with some HTML dump.
>
> I simply corrected the typo (ftmTotpToken -> frmTotpToken). `git am` to
> apply patch.

Good catch.

There's a thread from November 2017 in which Kevin Cernekee and Andy
Wang discuss a Juniper VPN where TOTP fails to work:
http://lists.infradead.org/pipermail/openconnect-devel/2017-November/004569.html

? and in that thread both "ftmTotpToken" and "frmTotpToken" are
mentioned. It appears that the reason for the misbehavior discussed in
the November 2017 is because of the (overlooked) typo in the code.

A bit of code archaeology here?

But I'm not sure how the "ftm" version got committed in the first
place. ?\_(?)_/?. I did a bit of archeological digging, and I can't
find the original patch (from November 29, 2016) anywhere on the
mailing list, but it's obviously in the Git history:
http://git.infradead.org/users/dwmw2/openconnect.git/commitdiff/1ff34cb9689fbaf57decac537df1e32e799bb9c7

Dan


From n.mavrogiannopoulos at gmail.com  Sun Apr 22 01:53:08 2018
From: n.mavrogiannopoulos at gmail.com (Nikos Mavrogiannopoulos)
Date: Sun, 22 Apr 2018 10:53:08 +0200
Subject: ocserv 0.11.12
Message-ID: <ddb487cccf69e248dc77aa3786513dd6b6bd5c10.camel@gmail.com>

Hello,
  I've released ocserv 0.11.12. This is a bug fix update in the
0.11.x branch.

* Version 0.11.12 (released 2018-04-22)
- Corrected crash on certain cases when proxy protocol is in use (#146).
- Corrected use of new features in GnuTLS 3.6.x
- Include crypt.h when necessary for crypt() - new glibc systems
  do not define it in unistd.h.


The current release is available at:
ftp://ftp.infradead.org/pub/ocserv/ocserv-0.11.12.tar.xz
ftp://ftp.infradead.org/pub/ocserv/ocserv-0.11.12.tar.xz.sig

The VPN server's web-site is at:
http://www.infradead.org/ocserv

regards,
Nikos



From nmav at gnutls.org  Sun Apr 22 01:58:20 2018
From: nmav at gnutls.org (Nikos Mavrogiannopoulos)
Date: Sun, 22 Apr 2018 10:58:20 +0200
Subject: ocserv 0.12.0
Message-ID: <f211395a7b64776b2d1dfaf399399a62da37ca29.camel@gnutls.org>

Hello,
  I've just released ocserv 0.12.0. This release has improvements
for openconnect server to support virtual hosts, and
other improvements in the supported authentication methods.


* Version 0.12.0 (released 2018-04-22)
- Allow DTLS stream to come from different IP from TLS stream.
  There are situations where internet providers send the UDP 
  stream from different IP (#61).
- Increased possibilities of allowed combinations of authentication
  methods (#108).
- Corrected regression since 0.11.8 with OTP authentication (#137).
- Added support for hostname-based virtual hosts, utilizing TLS
  SNI. With that change it is possible to configure multiple servers
  running over the same port (#133).
- Rename the tun device on BSD systems which support SIOCSIFNAME
  ioctl.
- Correctly handle proxy-protocol's health commands. That eliminates
  few connection drops when proxy protocol is in use.
- Corrected crash on certain cases when proxy protocol is in use (#146).


The current release is available at:
ftp://ftp.infradead.org/pub/ocserv/ocserv-0.12.0.tar.xz
ftp://ftp.infradead.org/pub/ocserv/ocserv-0.12.0.tar.xz.sig

The VPN server's web-site is at:
http://www.infradead.org/ocserv

regards,
Nikos



From sainathgaddam08 at gmail.com  Tue Apr 24 12:37:40 2018
From: sainathgaddam08 at gmail.com (sainath Gaddam)
Date: Tue, 24 Apr 2018 15:37:40 -0400
Subject: open connect client for Amazon Linux.
Message-ID: <CAE1UTCQu-vR1BFYHQ-m_1pmTw5U9JY7NbVPScJKJxXMwons+VQ@mail.gmail.com>

Hi Team,

I was trying to install occlient on my Amazon Linux server but ended
up with lot of dependencies. I can't now migrate to other OS. It would
be good if you provide for Amazon linux server as well.

Thanks & Regards
Sainath


