Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature request] DTLS support #473

Open
mrbaseman opened this issue Oct 7, 2019 · 22 comments
Open

[feature request] DTLS support #473

mrbaseman opened this issue Oct 7, 2019 · 22 comments

Comments

@mrbaseman
Copy link
Collaborator

see here
#428 (comment)

@mrbaseman
Copy link
Collaborator Author

The discussion about DTLS has continued in the other issue. I think this comment provides helpful links

@joshualamorie
Copy link

The discussion about DTLS doesn't seem to be continuing within the MacOS discussion (which has switched to PPP). I've seen significant differences in performance with the Forticlient DTLS enabled under windows. It doesn't sound like the official forticlient for linux will be getting this any time soon. Is there anything I can do to help test any suggestions here?

@dlenski
Copy link

dlenski commented Feb 8, 2021

Fortinet servers appear to use a standard DTLS handshake, running on the same UDP port as the HTTPS interface runs on… for example, I have access to a server with its HTTPS/PPP interface on TCP port 10443

$ echo BLAHBLAHBLAH | gnutls-cli --insecure --udp my.fortinet.vpn.com:10443
<certificate info …>
- Description: (DTLS1.2)-(ECDHE-SECP384R1)-(RSA-SHA512)-(AES-256-GCM)
- Session ID: 2E:DB:EF:F3:FF:21:29:2D:AF:DF:7E:C2:A3:E5:08:B8:8D:25:AE:27:C8:12:AF:6F:29:36:7A:15:CE:B4:C3:E1
- Ephemeral EC Diffie-Hellman parameters
 - Using curve: SECP384R1
 - Curve size: 384 bits
- Version: DTLS1.2
- Key Exchange: ECDHE-RSA
- Server Signature: RSA-SHA512
- Cipher: AES-256-GCM
- MAC: AEAD
- Compression: NULL
- Options: extended master secret, safe renegotiation,
- Handshake was completed

- Simple Client Mode:
\0!GFtype\0svrhello\0handshake\0fail\0

That last line is a response from the server. I presume Fortinet is similar to every other TLS+UDP VPN out there, and there's some initial exchange of a cookie/secret needed to establish the DTLS connection after the TLS connection has been established.

@dlenski
Copy link

dlenski commented Feb 8, 2021

Is there anything I can do to help test any suggestions here?

@joshualamorie, I have recently gotten Fortinet fully working in OpenConnect (see #650 (comment)).

OpenConnect already knows how to speak DTLS (since the Cisco AnyConnect protocol uses it), so implementing Fortinet-compatible DTLS is unlikely to be too complicated. However, we'll need to figure out the details of how the DTLS connection is initiated, authenticated, encapsulated… so MITM captures from the official Fortinet client will almost certainly be required.

@DimitriPapadopoulos
Copy link
Collaborator

It would help if there were clear instructions on how to use MITM, I cannot get it to run on the same machine I work from.

@dwmw2
Copy link

dwmw2 commented Feb 9, 2021

Start by capturing the traffic on the wire; it's actually possible that it's in the DTLS handshake (with PSK) instead of inside it.

@dlenski
Copy link

dlenski commented Feb 9, 2021

Start by capturing the traffic on the wire; it's actually possible that it's in the DTLS handshake (with PSK) instead of inside it.

As far as I can tell, Fortinet servers do not use DTLS-PSK. Connecting to a real Fortinet server with DTLS-PSK, based on the gnutls-cli docs.

(Only allow PSK) gnutls-cli --insecure --udp --pskuser=nobody --priority NORMAL:-KX-ALL:+PSK:+DHE-PSK:+ECDHE-PSK vpn2.case.edu:443
→ Times out, tcpdump shows handshake failure replies

(Allow all standard KX) gnutls-cli --insecure --udp --pskuser=nobody vpn2.case.edu:443
→ Connects with non-PSK ciphersuite

UPDATE: It's possible that they do use DTLS-PSK, but also require a specific session ID to get the server to respond (what Cisco AnyConnect servers do too). Although the fact that they allow non-PSK connections and respond with \0!GFtype\0svrhello\0handshake\0fail\0 (where the first 2 bytes are the be16 length of the message) suggests that they do their own Vary Speshul Negotiation. 🤷‍♂️

@dlenski
Copy link

dlenski commented Feb 10, 2021

MITM captures from the official Fortinet client will almost certainly be required.

I've now MITMtortured-captured the exchange between a Windows Forticlient and server. They do not use either DTLS-PSK, nor session resumption. I'm guessing that the session cookie is shared over the (anonymous) DTLS link.

The only MITM tool for DTLS that I'm aware of is https://github.com/travelping/capwap-mitm … unless someone knows another more general-purpose one? (@dwmw2?)


This guy did some decryption of DTLS packets from Fortinet in 2017, and his screenshots confirm that the Fortinet-PPP-in-DTLS encapsulation is identical to TLS (6-byte Fortinet header precedes PPP packet: l1 l1 50 50 l2 l2).

The only thing left to figure out, really, is how the connection is initiated. If anyone has access to a Fortinet server for which they control the certificate and private key, this'll be a piece of cake to decrypt.

@dlenski
Copy link

dlenski commented Feb 11, 2021

I figured out how the DTLS session is initiated, and it's quite easy. $COOKIE is the SVPNCOOKIE value. $BE16_LEN is the be16 length of the whole packet.

Client sends…

0000   01 18 47 46 74 79 70 65 00 63 6c 74 68 65 6c 6c   ..GFtype.clthell
0010   6f 00 53 56 50 4e 43 4f 4f 4b 49 45 00 34 62 6a   o.SVPNCOOKIE.4bj
0020   38 4f 64 73 5a 48 6a 6f 64 4e 73 68 59 51 2b 59   8OdsZHjodNshYQ+Y
...
0100   74 4b 6d 49 44 4f 43 34 3d 00 64 6e 73 30 00 31   tKmIDOC4=.dns0.1
0110   30 2e 30 2e 32 2e 33 00                           0.0.2.3.

Server responds with either ok or fail

0000   00 1f 47 46 74 79 70 65 00 73 76 72 68 65 6c 6c   ..GFtype.svrhell
0010   6f 00 68 61 6e 64 73 68 61 6b 65 00 6f 6b 00      o.handshake.ok.

You can reproduce this yourself by authenticating, capturing the cookie, fetching the XML config¹, not starting the TLS tunnel², calculating the total packet length, and then doing…

BE16_LEN="\x01\x18" # if the total length is 0x0118 bytes, as above
COOKIE="SVPNCOOKIE_value"
printf "${BE16_LEN}GFtype\x00clthello\x00SVPNCOOKIE\x00${COOKIE}\x00dns0\x0010.0.2.3\x00" | \
    gnutls-cli --udp --insecure vpn.server.com:port

¹ As with many SSL VPN protocols, fetching the "config" somehow activates/enables the tunnel endpoint
² Looks like enabling the TLS tunnel invalidates the UDP/DTLS tunnel, and vice versa. Similar to GlobalProtect… yay.

@mrbaseman
Copy link
Collaborator Author

@dlenski thanks for the results of your analysis. This will help implementing DTLS support in openfortivpn.

to anyone: please feel free to open a pull request for this

@jefvantongerloo
Copy link

DTLS support would be great :)

@dlenski
Copy link

dlenski commented Apr 13, 2021

We now have working a Fortinet DTLS implementation in OpenConnect. https://gitlab.com/openconnect/openconnect/-/commits/ppp_dtls_wip

I certainly don't want to detract from anyone adding this support to OpenFortivpn, but it would be excellent to have some of you test our DTLS support in OpenConnect. (Ping @emelenas, who helped me figure out how to get Fortinet 2FA working in OpenConnect. 🙏)

This is the largest group of savvy FLOSS-friendly Fortinet users out there, and both projects can benefit from additional documentation of the protocol, and understanding of corner cases and unusual behavior.

$ echo 'PASSWORD' | openconnect --prot=fortinet vpn.server.com:10443 -u USERNAME --passwd-on-stdin
GET https://vpn.server.com:10443/
Connected to 1.2.3.4:10443
SSL negotiation with vpn.server.com
Server certificate verify failed: signer not found
Connected to HTTPS on vpn.server.com with ciphersuite (TLS1.2)-(ECDHE-SECP384R1)-(RSA-SHA512)-(AES-256-GCM)
POST https://vpn.server.com:10443/remote/logincheck
GET https://vpn.server.com:10443/remote/fortisslvpn
GET https://vpn.server.com:10443/remote/fortisslvpn_xml
DTLS is enabled on port 10443
Reported platform is FGT60D v5.06.12 build 1701 branch 1701
Got search domain server.com
Got IPv4 DNS server 192.168.11.2
Got legacy IP address 10.212.134.207
Got IPv4 route 192.168.11.0/255.255.255.0
Idle timeout is 90 minutes.
Established DTLS connection (using GnuTLS). Ciphersuite (DTLS1.2)-(ECDHE-SECP384R1)-(RSA-SHA512)-(AES-256-GCM).
DTLS tunnel connected; exiting HTTPS mainloop.
Configured as 10.212.134.207, with SSL disconnected and DTLS connected
Session authentication will expire at Tue Apr 13 20:52:01 2021
...
^CUser detached from session (SIGHUP/SIGINT); exiting.

@zez3
Copy link

zez3 commented May 20, 2021

@dlenski
With which version did you tested?
I just compiled the OpenConnect v 8.10 on an old debian jessie and it seems I get Unknown VPN protocol 'fortinet'

 ./openconnect -V
OpenConnect version v8.10-unknown
Using GnuTLS 3.3.30. Features present: PKCS#11, HOTP software token, TOTP software token, DTLS, ESP
Supported protocols: anyconnect (default), nc, gp, pulse

@zez3
Copy link

zez3 commented May 20, 2021

oh, I found it later on branch https://gitlab.com/openconnect/openconnect/commits/ppp_protocols

@dwmw2
Copy link

dwmw2 commented May 20, 2021

It will be in the upcoming 9.00 release. For now you have to build from git (http://www.infradead.org/openconnect/building.html) or use the bleeding-edge builds from https://copr.fedorainfracloud.org/coprs/dwmw2/openconnect/ for Fedora/RHEL/Windows.

@dwmw2
Copy link

dwmw2 commented May 20, 2021

Don't use the ppp_protocols branch; it's all merged to master now.

@zez3
Copy link

zez3 commented May 20, 2021

hmm, it seems I have no luck today. My old debian jessie is also not helping. I cannot test on a rhel based. I wanted to try on a server with a 10Gbps link.
@dwmw2 I already tried the master branch and there is no fortinet support there.

Now with ppp_protocols branch I get a segmentation fault

./openconnect --dump --protocol=fortinet univpn.unibe.ch:443
GET https://univpn.unibe.ch/
Attempting to connect to server 130.92.245.39:443
Connected to 130.92.245.39:443
SSL negotiation with univpn.unibe.ch
Connected to HTTPS on univpn.unibe.ch with ciphersuite (TLS1.2)-(DHE-RSA-8192)-(AES-256-CBC)-(SHA256)
> GET / HTTP/1.1
> Host: univpn.unibe.ch
> User-Agent: Mozilla/5.0 SV1
>
Got HTTP response: HTTP/1.1 200 OK
Date: Thu, 20 May 2021 12:39:54 GMT
Server: xxxxxxxx-xxxxx
Last-Modified: Wed, 17 Feb 2021 22:44:12 GMT
ETag: "83-602d9c3c"
Accept-Ranges: bytes
Content-Length: 131
Content-Type: text/html
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'; object-src 'self'; script-src 'self' https   'unsafe-eval' 'unsafe-inline' blob:;
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
HTTP body length:  (131)
< <html><script type="text/javascript">
< if (window!=top) top.location=window.location;top.location="/remote/login";
< </script></html>
Segmentation fault

@dwmw2
Copy link

dwmw2 commented May 20, 2021

We should probably take this to https://gitlab.com/openconnect/openconnect/-/merge_requests/169 rather than continuing an OpenConnect discussion here?

The master branch is at commit 0cfdb7b871fd04c7a500001d2d0961779adf0ed5 which definitely includes Fortinet support, having merged the ppp_protocols branch and fixed that segfault (https://gitlab.com/openconnect/openconnect/-/issues/235)

@dwmw2
Copy link

dwmw2 commented Jun 2, 2021

@zez3 did you get it working?

@zez3
Copy link

zez3 commented Jun 2, 2021

@dwmw2
yes, but only under root. I'll open an issue in gitlab

I found this later
http://www.infradead.org/openconnect/nonroot.html

@gpavinteractiv
Copy link

I can connect with DTLS on openconnect.
With high latency (~250ms Japan to France) bandwidth is far better with openconnect on DTLS than with openfortivpn : ~30Mb/s vs ~1Mbps

Is this feature in the pipes for openfortivpn?

@AlexeySaff
Copy link

I bet no updates on that feature, right ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants