Closed antt1v closed 6 years ago
Thanks, @antt1v. Lord, I hate GlobalProtect.
All the GlobalProtect VPNs I've seen until now deliver their challenge prompts in the form of a JavaScript blob (yes, JavaScript): https://github.com/dlenski/openconnect/blob/HEAD/gpst.c#L129-L200
var respStatus = "Challenge"; var respMsg = "I herd u liek parsing JavaScript so I sent u sum. PLEEZ ENTER UR CHALLENJ CODE"; thisForm.inputStr.value = "TopSecretRandomString";
… but now you're telling me that there are some GlobalProtect VPNs which respond in a (relatively) saner format with XML:
<challenge> <user>user.name</user> <inputstr>e708</inputstr> <respmsg> Wait for token to change, then enter the new tokencode:</respmsg> </challenge>
What can you tell me about the GlobalProtect server? What version of GP is the server running?
Thanks for taking the time to look into this! Yeah, GlobalProtect is... quite terrible.
Unfortunately I don't have access to the server itself so I don't know all the details. I assume the server version is one of these two as they're inside the server response after a successful authentication:
< <portal-config-version>4100</portal-config-version>
< <version>3.0.3-7</version>
I tried the fix in https://github.com/dlenski/openconnect/commit/077c420a0db8ae96b00888726db1a05901dffa82 and now the challenge response is parsed succesfully and I receive the portal config after enterting the challenge token.
However, after that when trying to contact the gateway, it reports authentication failure and goes right back to the challenge prompt:
HTTP body length: (132)
<
< var respStatus = "Error";
< var respMsg = "Authentication failure: Invalid username or password";
< thisForm.inputStr.value = "e8aa";
<
Unexpected 512 result from server
Invalid username or password.
Wait for token to change, then enter the new tokencode:
Challenge:
Re-entering the token pin (or the password) seems to do nothing.
Here's the full output:
./openconnect --protocol=gp --usergroup=portal vpn.domain.com --dump -vvv
Please enter your username and password
Username: user.name
Password:
POST https://vpn.domain.com/global-protect/getconfig.esp
Attempting to connect to server ip:port
Connected to ip:port
SSL negotiation with vpn.domain.com
Connected to HTTPS on vpn.domain.com
> POST /global-protect/getconfig.esp HTTP/1.1
> Host: vpn.domain.com
> User-Agent: PAN GlobalProtect
> X-Pad: 00000000000000000000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 169
>
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=vpn.domain.com&computer=G0015&user=user.name&passwd=password
Got HTTP response: HTTP/1.1 200 OK
Server:
Date: Wed, 23 May 2018 07:07:05 GMT
Content-Type: application/xml; charset=UTF-8
Content-Length: 168
Connection: keep-alive
ETag: "573dc-5ef-5a0a2e6e"
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Expires: Thu, 19 Nov 1981 08:52:00 GMT
X-FRAME-OPTIONS: DENY
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
HTTP body length: (168)
< <challenge>
< <user>user.name</user>
< <inputstr>e8aa</inputstr>
< <respmsg> Wait for token to change, then enter the new tokencode:</respmsg>
< </challenge>
Wait for token to change, then enter the new tokencode:
Challenge:
POST https://vpn.domain.com/global-protect/getconfig.esp
> POST /global-protect/getconfig.esp HTTP/1.1
> Host: vpn.domain.com
> User-Agent: PAN GlobalProtect
> Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931
> X-Pad: 0000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 185
>
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=vpn.domain.com&computer=G0015&inputStr=e8aa&user=user.name&passwd=token_pin
Got HTTP response: HTTP/1.1 200 OK
Server:
Date: Wed, 23 May 2018 07:07:18 GMT
Content-Type: application/xml; charset=UTF-8
Content-Length: 10486
Connection: keep-alive
ETag: "573dc-5ef-5a0a2e6e"
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Expires: Thu, 19 Nov 1981 08:52:00 GMT
X-FRAME-OPTIONS: DENY
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
Set-Cookie: PHPSESSID=04b1ddc8b0194f2ae293ca2541698931; secure; HttpOnly
HTTP body length: (10486)
< <?xml version="1.0" encoding="UTF-8" ?>
< <policy>
< <portal-name>VPN</portal-name>
< <portal-config-version>4100</portal-config-version>
< <version>3.0.3-7 </version>
< <client-role>global-protect-full</client-role>
< <agent-user-override-key>****</agent-user-override-key>
< <root-ca>
< <entry name="CA Chain">
< <cert>
< -----BEGIN CERTIFICATE-----
< ****
< -----END CERTIFICATE-----
< -----BEGIN CERTIFICATE-----
< ****
< -----END CERTIFICATE-----
< </cert>
< <install-in-cert-store>no</install-in-cert-store>
< </entry>
< </root-ca>
< <connect-method>on-demand</connect-method>
< <on-demand>yes</on-demand>
< <refresh-config>yes</refresh-config>
< <refresh-config-interval>24</refresh-config-interval>
< <authentication-modifier>
< <none/>
< </authentication-modifier>
< <authentication-override>
< <accept-cookie>no</accept-cookie>
< <generate-cookie>yes</generate-cookie>
< <cookie-lifetime><lifetime-in-hours>24</lifetime-in-hours></cookie-lifetime>
< <cookie-encrypt-decrypt-cert>GP-Cookie- certificate</cookie-encrypt-decrypt-cert>
< </authentication-override>
< <use-sso>yes</use-sso>
< <gateways>
< <cutoff-time>5</cutoff-time>
< <external>
< <list>
< <entry name="gateway1.vpn.domain.com">
< <priority>1</priority>
< <description>gateway1</description>
< </entry>
< </list>
< </external>
< </gateways>
< <agent-ui>
< <can-save-password>yes</can-save-password>
< <passcode></passcode>
< <agent-user-override-timeout>0</agent-user-override-timeout>
< <max-agent-user-overrides>0</max-agent-user-overrides>
< <help-page></help-page>
< <welcome-page>
< <display>no</display>
< <page></page>
< </welcome-page>
< <agent-user-override>with-comment</agent-user-override>
< <enable-advanced-view>yes</enable-advanced-view>
< <enable-do-not-display-this-welcome-page-again>yes</enable-do-not-display-this-welcome-page-again>
< <can-change-portal>yes</can-change-portal>
< <show-agent-icon>yes</show-agent-icon>
< <password-expiry-message></password-expiry-message>
<
< </agent-ui>
< <hip-collection>
< <hip-report-interval>3600</hip-report-interval>
< <max-wait-time>20</max-wait-time>
< <collect-hip-data>yes</collect-hip-data>
< <default>
< <category>
< <member>host-info</member>
< <member>data-loss-prevention</member>
< <member>patch-management</member>
< <member>firewall</member>
< <member>antivirus</member>
< <member>anti-spyware</member>
< <member>disk-backup</member>
< <member>disk-encryption</member>
< </category>
< </default>
< </hip-collection>
< <agent-config>
< <save-user-credentials>1</save-user-credentials>
< <portal-2fa>no</portal-2fa>
< <internal-gateway-2fa>no</internal-gateway-2fa>
< <auto-discovery-external-gateway-2fa>no</auto-discovery-external-gateway-2fa>
< <manual-only-gateway-2fa>no</manual-only-gateway-2fa>
< <client-upgrade>prompt</client-upgrade>
< <logout-remove-sso>yes</logout-remove-sso>
< <krb-auth-fail-fallback>yes</krb-auth-fail-fallback>
< <retry-tunnel>30</retry-tunnel>
< <retry-timeout>5</retry-timeout>
< <enforce-globalprotect>no</enforce-globalprotect>
< <captive-portal-exception-timeout>0</captive-portal-exception-timeout>
< <traffic-blocking-notification-delay>15</traffic-blocking-notification-delay>
< <display-traffic-blocking-notification-msg>yes</display-traffic-blocking-notification-msg>
< <traffic-blocking-notification-msg><div style="font-family:'Helvetica Neue';"><h1 style="color:red;text-align:center; margin: 0; font-size: 30px;">Notice</h1><p style="margin: 0;font-size: 15px; line-height: 1.2em;">To access the network, you must first connect to GlobalProtect.</p></div></traffic-blocking-notification-msg>
< <allow-traffic-blocking-notification-dismissal>yes</allow-traffic-blocking-notification-dismissal>
< <display-captive-portal-detection-msg>no</display-captive-portal-detection-msg>
< <captive-portal-detection-msg><div style="font-family:'Helvetica Neue';"><h1 style="color:red;text-align:center; margin: 0; font-size: 30px;">Captive Portal Detected</h1><p style="margin: 0; font-size: 15px; line-height: 1.2em;">GlobalProtect has temporarily permitted network access for you to connect to the Internet. Follow instructions from your internet provider.</p><p style="margin: 0; font-size: 15px; line-height: 1.2em;">If you let the connection time out, open GlobalProtect and click Connect to try again.</p></div></captive-portal-detection-msg>
< <certificate-store-lookup>user-and-machine</certificate-store-lookup>
< <scep-certificate-renewal-period>7</scep-certificate-renewal-period>
< <ext-key-usage-oid-for-client-cert></ext-key-usage-oid-for-client-cert>
< <retain-connection-smartcard-removal>yes</retain-connection-smartcard-removal>
< <rediscover-network>yes</rediscover-network>
< <resubmit-host-info>yes</resubmit-host-info>
< <can-continue-if-portal-cert-invalid>yes</can-continue-if-portal-cert-invalid>
< <user-switch-tunnel-rename-timeout>0</user-switch-tunnel-rename-timeout>
< <pre-logon-tunnel-rename-timeout>-1</pre-logon-tunnel-rename-timeout>
< <show-system-tray-notifications>no</show-system-tray-notifications>
< <max-internal-gateway-connection-attempts>0</max-internal-gateway-connection-attempts>
< <portal-timeout>30</portal-timeout>
< <connect-timeout>60</connect-timeout>
< <receive-timeout>30</receive-timeout>
< <flush-dns>no</flush-dns>
< <proxy-multiple-autodetect>no</proxy-multiple-autodetect>
< <wsc-autodetect>yes</wsc-autodetect>
< <ipv6-preferred>yes</ipv6-preferred>
<
< </agent-config>
< <user-email>user.name@domain.com</user-email>
< <portal-userauthcookie>****</portal-userauthcookie>
< <portal-prelogonuserauthcookie>empty</portal-prelogonuserauthcookie>
< <scep-cert-auth-cookie>****</scep-cert-auth-cookie>
< </policy>
1 gateway servers available:
gateway1(gateway1.vpn.domain.com)
Please select GlobalProtect gateway.
GATEWAY: [gateway1]:gateway1
POST https://gateway1.vpn.domain.com/ssl-vpn/login.esp
Attempting to connect to server ip:port
Connected to ip:port
SSL negotiation with gateway1.vpn.domain.com
Connected to HTTPS on gateway1.vpn.domain.com
> POST /ssl-vpn/login.esp HTTP/1.1
> Host: gateway1.vpn.domain.com
> User-Agent: PAN GlobalProtect
> X-Pad: 00000000000000000000000000000000000000000000000000000000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 197
>
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=gateway1.vpn.domain.com&computer=G0015&inputStr=e8aa&user=user.name&passwd=token_pin
Got HTTP response: HTTP/1.1 512 Custom error
Server:
Date: Wed, 23 May 2018 07:07:18 GMT
Content-Type: text/html
Content-Length: 132
Connection: keep-alive
ETag: "573f3-1e6f-5a0a2e6e"
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
x-private-pan-sslvpn: auth-failed
Expires: Thu, 19 Nov 1981 08:52:00 GMT
X-FRAME-OPTIONS: DENY
Set-Cookie: PHPSESSID=d234a7495584b0cc6fef18741041a0c0; secure; HttpOnly
Set-Cookie: PHPSESSID=d234a7495584b0cc6fef18741041a0c0; secure; HttpOnly
Set-Cookie: PHPSESSID=d234a7495584b0cc6fef18741041a0c0; secure; HttpOnly
HTTP body length: (132)
<
< var respStatus = "Error";
< var respMsg = "Authentication failure: Invalid username or password";
< thisForm.inputStr.value = "e8aa";
<
Unexpected 512 result from server
Invalid username or password.
Wait for token to change, then enter the new tokencode:
Challenge:
However, after that when trying to contact the gateway, it reports authentication failure and goes right back to the challenge prompt:
When logging in via the portal interface, the current behavior is (a) do the portal login and (b) if the portal login succeeds, reuse the same credentials from the portal form to attempt to login to the gateway.
The problem here, I think, is that the credentials being reused are those of the 2FA challenge login (username, token code, inputStr) rather than the "original" login (username, password).
Is that a correct statement?
What happens if you skip the portal entirely, and just connect to https://gateway1.vpn.domain.com/gateway? There are very few cases I've seen where the portal login is actually necessary or desirable… and they mainly involve weird external web-based authentication mechanisms.
Re-entering the token pin (or the password) seems to do nothing.
Yeah …
inputStr
field is still set, causing the gateway to interpret it as a response to a different login form.27c5568 should fix this, though it will require you to re-enter the original password again for the gateway.
I really need to clean up the GP form handling, but the problem is that I don't know all the possible weird things that can happen, such as this one :(
The problem here, I think, is that the credentials being reused are those of the 2FA challenge login (username, token code, inputStr) rather than the "original" login (username, password). Is that a correct statement?
Ah, that makes perfect sense. You are correct.
What happens if you skip the portal entirely, and just connect to https://gateway1.vpn.domain.com/gateway?
Great idea, that worked perfectly! I have some trouble with the DNS server behind the VPN not being used, but I'm guessing that's more likely due to my local setup rather than skipping the portal. I'll work it out. Other than that, no issues.
For the record, the challenge prompt from the gateway was in JavaScript, not XML like from the portal.
https://github.com/dlenski/openconnect/commit/27c556829a2c0d21d2ee5035b56365056feef96a should fix this, though it will require you to re-enter the original password again for the gateway.
I gave this a try but no luck. The inputStr got screwed up and it goes back to the challenge prompt. Here's the result:
1 gateway servers available:
gateway1 (gateway1.vpn.domain.com)
Please select GlobalProtect gateway.
GATEWAY: [gateway1]:gateway1
0L�
Challenge:
POST https://gateway1.vpn.domain.com/ssl-vpn/login.esp
Attempting to connect to server ip:port
Connected to ip:port
SSL negotiation with gateway1.vpn.domain.com
Connected to HTTPS on gateway1.vpn.domain.com
> POST /ssl-vpn/login.esp HTTP/1.1
> Host: gateway1.vpn.domain.com
> User-Agent: PAN GlobalProtect
> X-Pad: 0000000000000000000000000000000000000000000000000000000
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 201
>
> jnlpReady=jnlpReady&ok=Login&direct=yes&clientVer=4100&prot=https:&clientos=linux-64&server=gateway1.vpn.domain.com&computer=G0015&inputStr=p%c3%9a%01&user=user.name&passwd=password
Got HTTP response: HTTP/1.1 512 Custom error
Server:
Date: Wed, 23 May 2018 09:37:29 GMT
Content-Type: text/html
Content-Length: 139
Connection: keep-alive
ETag: "573f3-1e6f-5a0a2e6e"
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
x-private-pan-sslvpn: auth-failed
Expires: Thu, 19 Nov 1981 08:52:00 GMT
X-FRAME-OPTIONS: DENY
Set-Cookie: PHPSESSID=e12dc946f6174c97661bb3ecc4e61748; secure; HttpOnly
Set-Cookie: PHPSESSID=e12dc946f6174c97661bb3ecc4e61748; secure; HttpOnly
Set-Cookie: PHPSESSID=e12dc946f6174c97661bb3ecc4e61748; secure; HttpOnly
HTTP body length: (139)
<
< var respStatus = "Error";
< var respMsg = "Authentication failure: Invalid username or password";
< thisForm.inputStr.value = "p�";
<
Unexpected 512 result from server
Invalid username or password.
Challenge:
In any case I'm personally fine skipping the portal and using the gateway. Thanks for the help! :)
If you do choose to continue improving the form handling I'd be happy to test any changes.
For the record, the challenge prompt from the gateway was in JavaScript, not XML like from the portal.
:man_facepalming:
Internal consistency is not one of the strong points of the GP authentication flow. And that's the nicest possible thing I could say about it.
If you do choose to continue improving the form handling I'd be happy to test any changes.
I see what I screwed up in my haste. Will fix tomorrow.
@antt1v, please let me know if 49a7074 fixed the problem with the portal. It'll be good to know that this case works, even if it's not necessary for you to use it.
I can confirm that the portal authentication now works at least for me. Basically everything is done twice, first with the portal and then with the gateway. The authentication prompts are:
Not the prettiest, but it works! Thanks! :)
Great, thanks for filling me in!
I can confirm that the portal authentication now works at least for me. Basically everything is done twice, first with the portal and then with the gateway.
Yeah, that's what I expected :slightly_frowning_face:. I assume that when you do this with the official client, the portal passes off some kind of "portal authentication cookie" to prevent the gateway from having to redo the login and challenge.
Except for (4): it re-prompts you for the username? That's not supposed to happen. I wrote a quick-and-dirty GlobalProtect server "simulator" and used it to test this scenario, and at least with the simulator it does not reprompt for username…
Ah, my bad! Step 4 doesn't actually happen, just the passwords.
Problem description
I stumbled on a problem with a GlobalProtect VPN server using 2FA where openconnect doesn't understand the challenge response. Produced as follows:
Operating system and openconnect-gp version
openconnect-gp version:
operating system:
Thanks in advance!