adrienverge / openfortivpn

Client for PPP+TLS VPN tunnel services
GNU General Public License v3.0
2.64k stars 317 forks source link

Does authentication with certificate work? #941

Open xvybihal opened 2 years ago

xvybihal commented 2 years ago

I did not want to hijack #937, so I created this new issue. Is it possible, that the certificate auth is broken? I am not able to get it working in 1.17.1 (archilnux) or 1.17.0 (centos 8).

From what we have gathered, the openfortivpn client does never send the certificate to gateway. (assumed this from logs)

openfortivpn vpn.ACME.COM --user-cert=/tmp/ssl-vpn/cert.pem --user-key=/tmp/ssl-vpn/key.pem --realm=vpn-acme -u User -vvvvvvv
DEBUG:  ATTENTION: the output contains sensitive information such as the THE CLEAR TEXT PASSWORD.
DEBUG:  openfortivpn 1.17.0
DEBUG:  revision unavailable
DEBUG:  Loaded configuration file "/etc/openfortivpn/config".
VPN account password: 
DEBUG:  Configuration host = "vpn.ACME.COM"
DEBUG:  Configuration realm = "vpn-acme"
DEBUG:  Configuration port = "443"
DEBUG:  Configuration username = "User"
DEBUG:  Configuration password = "REDACTED"
DEBUG:  Resolving gateway host ip
DEBUG:  Establishing ssl connection
DEBUG:  SO_KEEPALIVE: 0
DEBUG:  SO_SNDBUF: 6
DEBUG:  SO_RCVBUF: 60
DEBUG:  server_addr: 1.2.3.4
DEBUG:  server_port: 443
DEBUG:  gateway_addr: 1.2.3.4
DEBUG:  gateway_port: 443
DEBUG:  Setting cipher list to: HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4
DEBUG:  Setting minimum protocol version to: 0x303.
DEBUG:  Gateway certificate validation succeeded.
INFO:   Connected to gateway.
DEBUG:  http_send:
POST /remote/logincheck HTTP/1.1
Host: vpn.ACME.COM:443
User-Agent: Mozilla/5.0 SV1
Accept: */*
Accept-Encoding: gzip, deflate, br
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate
If-Modified-Since: Sat, 1 Jan 2000 00:00:00 GMT
Content-Type: application/x-www-form-urlencoded
Cookie: 
Content-Length: 62

username=User&credential=REDACTED&realm=vpn-acme&ajax=1
DEBUG:  http_receive:
HTTP/1.1 200 OK
Date: Wed, 06 Oct 2021 11:44:28 GMT
Server: xxxxxxxx-xxxxx
Set-Cookie:  SVPNCOOKIE=; path=/; expires=Sun, 11 Mar 1984 12:00:00 GMT; secure; httponly;
Set-Cookie: SVPNNETWORKCOOKIE=; path=/remote/network; expires=Sun, 11 Mar 1984 12:00:00 GMT; secure; httponly
Transfer-Encoding: chunked
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'; object-src 'none'; script-src 'self' https 'unsafe-eval' 'unsafe-inline';
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000

753   
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="cache-control" content="must-revalidate">
<meta http-equiv="cache-control" content="no-store">
<title>SSL VPN Remote Access Web Portal</title>
<link href="/sslvpn/css/ssl_style.css" rel="stylesheet" type="text/css">
<script type="text/javascript" src="/remote/fgt_lang?lang=en"></script></head>
<body class="main">
<table class="container" cellpadding="0" cellspacing="0">
<tr>
<td><table class="dialog" width=300 align="center" cellpadding="0" cellspacing="0">
<tr>
<td><table class="header" cellpadding="0" cellspacing="0">
<tr>
<td id="err_title"></td>
</tr>
</table></td>
</tr>
<script>document.getElementById('err_title').innerHTML=fgt_lang['error'];</script>
<!--sslvpnerrmsg=Permission denied.-->
<tr>
<td class="body" height=100>
<table class="body"><tr><td id='err_val' title='-37' align="center">
<script>
var errval_elem=document.getElementById('err_val');
var errval=errval_elem.getAttribute('title').split(',');
var err_str = fgt_lang[errval[0]];
if (err_str == undefined) {
   errval_elem.innerHTML = "some unknown error!<br>";
} else {   if (errval.length == 2) {
       err_str = err_str.replace("%d", errval[1]);
   }
   errval_elem.innerHTML = err_str;
}
</script></td></tr></table></td>
</tr>
<tr><td>
<table class="footer" cellpadding="0" cellspacing="0">
<tr><td>
<input id="ok_button" type="button" value="" onclick="chkbrowser()" style="width:80px">
</td></tr>
</table>
</td></tr>
</table>
</body>
<script language = "javascript">
document.getElementById('ok_button').value=fgt_lang['ok'];
function chkbrowser() {
if (window.location.pathname == "/remote/login")
window.location.reload();
else
window.location.href = "/remote/login";}
</script>
</html>

0

ERROR:  Could not authenticate to gateway. Please check the password, client certificate, etc.
DEBUG:  No cookie given (-7)
INFO:   Closed connection to gateway.
DEBUG:  SO_KEEPALIVE: 0
DEBUG:  SO_SNDBUF: 6
DEBUG:  SO_RCVBUF: 60
DEBUG:  server_addr: 1.2.3.4
DEBUG:  server_port: 443
DEBUG:  gateway_addr: 1.2.3.4
DEBUG:  gateway_port: 443
DEBUG:  Setting cipher list to: HIGH:!aNULL:!kRSA:!PSK:!SRP:!MD5:!RC4
DEBUG:  Setting minimum protocol version to: 0x303.
DEBUG:  Gateway certificate validation succeeded.
DEBUG:  http_send:
GET /remote/logout HTTP/1.1
Host: vpn.ACME.COM:443
User-Agent: Mozilla/5.0 SV1
Accept: */*
Accept-Encoding: gzip, deflate, br
Pragma: no-cache
Cache-Control: no-store, no-cache, must-revalidate
If-Modified-Since: Sat, 1 Jan 2000 00:00:00 GMT
Content-Type: application/x-www-form-urlencoded
Cookie: 
Content-Length: 0

DEBUG:  http_receive:
HTTP/1.1 200 OK
Date: Wed, 06 Oct 2021 11:44:28 GMT
Server: xxxxxxxx-xxxxx
Set-Cookie:  SVPNCOOKIE=; path=/; expires=Sun, 11 Mar 1984 12:00:00 GMT; secure; httponly;
Set-Cookie: SVPNNETWORKCOOKIE=; path=/remote/network; expires=Sun, 11 Mar 1984 12:00:00 GMT; secure; httponly
Content-Length: 643
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'; object-src 'none'; script-src 'self' https 'unsafe-eval' 'unsafe-inline';
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000

<html><head><script>function fgt_clear_cookies() {var cookies = document.cookie.split(';');var domain = document.domain.split('.');var new_date='; expires=Thu, 21 Sep 1979 00:00:01 UTC';if (domain.length > 1) {domain.shift();}for (var i = 0; i < cookies.length; i++) {var spcook =  cookies[i].split('=');document.cookie = spcook[0] + '=; path=/' + new_date;document.cookie = spcook[0] + '=; domain=.' + domain.join('.') + '; path=/' + new_date;document.cookie = spcook[0] + '=; domain=' + domain.join('.') + '; path=/' + new_date;}window.location.href ='/remote/login';}</script></head><body><script>fgt_clear_cookies();</script></body></html>��/9��CR.l���fH�T"�f�S%7b�
�]�I9]|݂�e���9�k(m�n������`ѱX���$�d[�Đ]��f��C?�2��eu�3.�����Kb�䏒V����T�w�&;��q*a!��ߎ����F��i��Ϝ�J�v��O��ﲊgPZ~\SWn��C�
INFO:   Logged out.

The cert and key are valid and in pem format (I also use them for 802.1x and login with them works in browser). Also the cert is working with Windows and official Forticlient.

Is my assumtion correct and there could be defect with this functionality of the openfortivpn client?

DimitriPapadopoulos commented 2 years ago

Have you tried with an empty password -p ""?

xvybihal commented 2 years ago

Have you tried with an empty password -p ""?

Yeah, tried now. Also tried without user at all. But the result is exactly same.

DimitriPapadopoulos commented 2 years ago

I haven't written that code and don't have such a setup to test, but I seem to recall it had worked in the past. If you have time, I would recommend building and testing 1.16.0 and 1.15.0, just in case.

It's hard to understand what's wrong from the log:

ERROR:  Could not authenticate to gateway. Please check the password, client certificate, etc.
DEBUG:  No cookie given (-7)

You could also give openconnect a try. Fortinet support is still recent so you'll have to build from sources.

xvybihal commented 2 years ago

I just tried 1.15.0 (33789115c88a448b4194d76c955d145ca35330b7) and 1.16.0 (cde9d69dd44f83f00de38fe5bdb163fb3ee07913), the behavior is the same.

An observation - I have also tried linux version of FortiClient (7.0.0.0018) and it is also not able to connect (correct .pfx was provided, and properly loaded according to debug log) - ends with basically same thing [sslvpn:EROR] vpn_connection:1219 Error code [3]. Login failed: Insufficient credential(s). Please check the password, client certificate, etc.

xvybihal commented 2 years ago

I have build openconnect with openssl, and it works fine with the exact same certs and gateway:

$ sudo openconnect --protocol=fortinet -u User --certificate=/tmp/ssl-vpn/cert.pem --sslkey=/tmp/ssl-vpn/key.pem --server vpn.ACME.com
[sudo] password for jv: 
GET https://vpn.ACME.com/
Connected to 1.2.3.4:443
Using client certificate '/CN=User'
SSL negotiation with vpn.ACME.com
Connected to HTTPS on vpn.ACME.com with ciphersuite TLSv1.3-TLS_AES_256_GCM_SHA384
Password: 
POST https://vpn.ACME.com/remote/logincheck
GET https://vpn.ACME.com/remote/fortisslvpn_xml
DTLS is enabled on port 443
Reported platform is FG240D v6.00.10 build 0365 branch 0365
Got search domain ACME.com
Got IPv4 DNS server READCTED
Got IPv4 DNS server READCTED
Got Legacy IP address READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Got IPv4 route READCTED
Idle timeout is 960 minutes.
EPOLL_CTL_DEL: No such file or directory
Established DTLS connection (using OpenSSL). Ciphersuite DTLSv1.2-ECDHE-RSA-AES256-GCM-SHA384.
Requesting calculated MTU of 1351
Configured as 172.22.0.8, with SSL disconnected and DTLS established
Session authentication will expire at Wed Oct  6 15:32:25 2021
mrbaseman commented 2 years ago

I remember we had this question a while ago https://github.com/adrienverge/openfortivpn/issues/602#issuecomment-604508959 and it should work with -u "" -p "" --user-cert=...cert.pem --user-key=...key.pem

I have just built 1.17.1 and verified that login with username+password+certificate works, but I don't have a device which allows to login with a certificate only.

xvybihal commented 2 years ago

I have just built 1.17.1 and verified that login with username+password+certificate works, but I don't have a device which allows to login with a certificate only.

In no way I want to dispute this, but unfortunately I can't get it to work for me. I have tested 3 different boxes (CentOS and 2x archlinux). I went with lower version, as low as 1.12.0 and the result is always the same. My goal is also to use username+password+certificate. As workaround I have to stick with openconnect, which works. It also works in combination with networkmanager-openconnect.

michaeldoddgit commented 2 years ago

I can echo what xvybihal is stating. We also cannot connect with cert auth to a Fortigate running FortiOS v7.0.1 build0157 (GA) using openfortivpn from Ubuntu 20.04 focal with an SSL VPN portal that requires a client certificate. If we turn off the certificate requirement on the Fortigate openfortivpn works fine.

We have no issues if we use the same client certificate but with openconnect instead of openfortivpn and re-enable client certificate authentication in the Fortigate.

DimitriPapadopoulos commented 2 years ago

@toy4two So if I understand you correctly, you too have tested openfortivpn -u "" -p "" --user-cert=...cert.pem --user-key=...key.pem and it doesn't work. Also, I see it fails with both FortiOS v6.00.10 build 0365 and FortiOS v7.0.1 build 0157.

Is perhaps a password required to use the user certificate? A password associated to the certificate itself?

It looks like the VPN gateway refuses the authentication, probably because openfortivpn does not send the proper sequence of HTTP requests. There's not much I can do without access to a device that fails. What I can do, and have done, is printing the HTTP requests, like openconnect does. It would be great f you could build and run the latest version from @master so that we can compare the HTTP requests.

xvybihal commented 2 years ago

Is perhaps a password required to use the user certificate? A password associated to the certificate itself?

Not in my case. I have tested it. If password is required, I am correctly asked to provide additional password for the certificate key.

It looks like the VPN gateway refuses the authentication, probably because openfortivpn does not send the proper sequence of HTTP requests. There's not much I can do without access to a device that fails. What I can do, and have done, is printing the HTTP requests, like openconnect does. It would be great f you could build and run the latest version from @master so that we can compare the HTTP requests.

Attached logs. Hope they can help.

openconnect-dump-http.log openfortivpn-v1.17.1+git2.g1f692aa.log

DimitriPapadopoulos commented 2 years ago

i see openfortivpn outputs only:

POST /remote/logincheck

while openconnect outputs:

GET /vpn-acme
GET /remote/login?realm=vpn-acme
POST /remote/logincheck
GET /remote/fortisslvpn_xml

it does look like something goes wrong with POST /remote/logincheck. Perhaps the missing GET? I'll have a look at the HTTP logs.

michaeldoddgit commented 2 years ago

@toy4two So if I understand you correctly, you too have tested openfortivpn -u "" -p "" --user-cert=...cert.pem --user-key=...key.pem and it doesn't work. Also, I see it fails with both FortiOS v6.00.10 build 0365 and FortiOS v7.0.1 build 0157.

Is perhaps a password required to use the user certificate? A password associated to the certificate itself?

It looks like the VPN gateway refuses the authentication, probably because openfortivpn does not send the proper sequence of HTTP requests. There's not much I can do without access to a device that fails. What I can do, and have done, is printing the HTTP requests, like openconnect does. It would be great f you could build and run the latest version from @master so that we can compare the HTTP requests.

@DimitriPapadopoulos I retested and believe I found the difference between openfortivpn and openconnect with client certs. I took the client cert which had the cert and private key in a single file and broke them into separate cert and key files. I re-ran using openfortivpn <hostname> -u "" -p "" --user-cert=...cert.pem --user-key=...key.pem and I am connected.

Openconnect is more flexible in this regard.

DimitriPapadopoulos commented 2 years ago

@toy4two Indeed openfortivpn requires two distinct files as arguments to --user-cert and --user-key. Thank you for coming back to us with an explanation. I don't even know if it's feasible to pass the same file with both the cert and key twice - probably not. Perhaps I should add an example in README.md.

Unfortunately that doesn't explain why it doesn't work for @xvybihal who does use --user-cert=/tmp/ssl-vpn/cert.pem --user-key=/tmp/ssl-vpn/key.pem.

Perhaps it would help if you could both provide the version of FortiOS on both VPN gateways. For @xvybihal the information can be found in the openconnect log file, it's an FG240D gateway running FortiOS 6.0.10.

xvybihal commented 2 years ago

Unfortunately that doesn't explain why it doesn't work for @xvybihal who does use --user-cert=/tmp/ssl-vpn/cert.pem --user-key=/tmp/ssl-vpn/key.pem.

Yeah, I have key and public cert separated from the beginning :/

Perhaps it would help if you could both provide the version of FortiOS on both VPN gateways. For @xvybihal the information can be found in the openconnect log file, it's an FG240D gateway running FortiOS 6.0.10.

He mentioned FortiOS v7.0.1 build0157 (GA). Unfortunately, I was informed we can't go to version 7+ on our box.