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

Topic/verify crypto binding #37

Merged
merged 7 commits into from
Jan 18, 2021
Merged

Conversation

tisj
Copy link
Contributor

@tisj tisj commented Jan 15, 2021

Includes the axsguard/sstp-server topic/call_connected_improvements branch

Uses the sstp-pppd-plugin.so from sstp-client if available to support crypto-binding. This ppp plugin snoops the mppe keys from the ppp (higher) layer and leverages them over a unix socket to the sstp-server. These HLAK keys are necessary to (re)compute the Client MAC (CMAC) for verification.

Only when the computed CMAC matches the one provided by the client the connection can be trusted to have no man-in-the-middle.

Originally I used the Openssl library (before certtool) to obtain the certificate's fingerprints, like so:

--- a/sstpd/__main__.py
+++ b/sstpd/__main__.py
@@ -6,6 +6,7 @@ from socket import IPPROTO_TCP, TCP_NODE
 from configparser import SafeConfigParser, NoSectionError
 import ssl
 import asyncio
+from OpenSSL.crypto import load_certificate, FILETYPE_PEM
 try:
     import uvloop
 except ImportError:
@@ -126,18 +127,34 @@ def main():
     else:
         ippool = None
 
+    if args.pem_cert is not None:
+        try:
+            with open(args.pem_cert, "rb") as cert_file:
+                cert = load_certificate(FILETYPE_PEM, cert_file.read())
+            sha1 = b''
+            try:
+                sha1 = bytes.fromhex(cert.digest('sha1').decode().replace(':', ' '))
+            except:
+                pass
+            sha256 = b''
+            try:
+                sha256 = bytes.fromhex(cert.digest('sha256').decode().replace(':', ' '))
+            except:
+                pass
+            logging.info("Certificate SHA1 %s", sha1.hex())
+            logging.info("Certificate SHA256 %s", sha256.hex())
+            cert_hash = (sha1, sha256)
+        except:
+            cert_hash = None
+            pass
+
     if args.no_ssl:
         ssl_ctx = None
-        cert_hash = None
         logging.info('Running without SSL.')
     else:
         ssl_ctx = _load_cert(args.pem_cert, args.pem_key)
         if args.ciphers:
             ssl_ctx.set_ciphers(args.ciphers)
-        #sha1 = cert.digest('sha1').replace(':', '').decode('hex')
-        #sha256 = cert.digest('sha256').replace(':', '').decode('hex')
-        # TODO: set cert hash on cert_hash[sha1, sha256]
-        cert_hash = None
     on_unix_socket = args.listen.startswith('/')
 
     if uvloop is None:

When the server is not in state SERVER_CALL_CONNECTED_PENDING, send the
sstp abort response, and effectively stop processing any further,
if not to avoid the risk to propagate the server to state
SERVER_CALL_CONNECTED.

Signed-off-by: Tijs Van Buggenhout <tijs.van.buggenhout@axsguard.com>
Relevant for an admin to verify in logs when sstp-server is started in
normal (not debug) mode.

Signed-off-by: Tijs Van Buggenhout <tijs.van.buggenhout@axsguard.com>
* Ensure Crypto Binding Attribute is present in Call Connected
* Ensure Crypto Binding Attribute is of correct size (104 bytes)
* Ensure Crypto Binding Hash is either SHA1 or SHA256

Log the client's hash_type as a human readable string (ie. SHA1
or SHA256).

Signed-off-by: Tijs Van Buggenhout <tijs.van.buggenhout@axsguard.com>
The PPP SSTP API protocol allows to interact with a ppp process that has
the sstp-plugin.so plugin[1] loaded, in order to pass so-called 'higher layer
authentication keys' (ie. MPPE keys) from ppp to sstp-server.

1. http://sstp-client.sourceforge.net
2. https://sourceforge.net/p/sstp-client/code/HEAD/tree/trunk/src/pppd-plugin/

Signed-off-by: Tijs Van Buggenhout <tijs.van.buggenhout@axsguard.com>
@tisj
Copy link
Contributor Author

tisj commented Jan 15, 2021

This fixes #1

@tisj
Copy link
Contributor Author

tisj commented Jan 15, 2021

@zhdanow5a Appreciated. For crypto binding to work, the mppe session keys are needed, which are a result of the successful authentication in the PPP child process.

This can be achieved by leveraging them from the PPP process to the sstp-server daemon by means of the sstp-pppd-plugin.so (for PPP). Sstp-server detects the presence of the plugin at start-up. If not present no crypto binding is checked, otherwise it is.

The sstp-pppd-plugin.so is part of the sstp-client project, which you need to configure with the '--enable-ppp-plugin' option

# tar -xzvf sstp-client-1.0.14.tar.gz
# cd sstp-client-1.0.14
# ./configure --enable-ppp-plugin
# make
# make install

Since you are on ubuntu, maybe this can be of interest https://launchpad.net/~eivnaes/+archive/ubuntu/network-manager-sstp.

Maybe you can just type apt-get install sstp-client and get lucky.

You should end up with a sstp-pppd-plugin.so in the directory where other ppp plugins are located on your system (e.g. /usr/libexec/pppd/2.4.9/sstp-pppd-plugin.so).

Then restart sstp-server, and it should detect the plugin and activate crypto binding verification.

PS. I m not using uvloop myself, so it's not mandatory for sure.

@tisj
Copy link
Contributor Author

tisj commented Jan 16, 2021

@zhdanow5a yeah the whole thing is rather technical and system dependent. Maybe I should write a complete tutorial with example confits etc.

Using sstp-client I found out that it can construct the crypto binding without the need of ppp plugin if you specify the username and password to sstp-client command: sstpc --user . The password is optional and you will receive a prompt to enter if you don't specify one.

At server side you don't need to alter any settings for ppp, whatever worked for you before should continue to work. The plugin is loaded by sstp-server if detected. This is the command being used for the detection : ppp --dry-run plugin sstp-pppd-plugin.so. If this process finishes with exit code 0 crypto binding verification should be enabled.

@tisj
Copy link
Contributor Author

tisj commented Jan 16, 2021

@zhdanow5a I just realised that there may be an issue with the detection if you specify an alternate locate for pppd in sstp-server configuration. The current code expects ppp to be in the PATH. I will correct this.

Detects the presence of the sstp-pppd-plugin.so plugin on the system,
and creates a task to start communication over a unix socket of the
PPP SSTP API protocol if present.

It allows for ppp process to pass the higher layer authentication keys
to the sstp-server instance in order to verify the crypto binding.

If the plugin is not available, crypto binding verification will not be
supported.

Signed-off-by: Tijs Van Buggenhout <tijs.van.buggenhout@axsguard.com>
Implements the Crypto Binding Compound MAC (CMAC) verification,
when the Crypto Binding Attribute is present in the SSTP Call Connected
message.

The PPP (sub)process passes the MPPE send/receive keys when CHAP
authentication succeeds. When received by sstp-server, it installs them
as the Higher Layer Key (HLAK). In server mode the HLAK is obtained by
concatenating the master receive key with the send key. Ensure the key
is 32 bytes long (should be the case for MPPE) - zero-padded if needed.

The Crypto Binding CMAC is (re)calculated on server side and compared
with the value present in the Crypto Binding Attribute. In case they do
not match the connection is aborted.

CMAC uses HMAC using the Crypto Binding Hash algorithm negotiated (SHA1
or SHA256). Input for HMAC is the complete SSTP Call Connected message,
with CMAC field in Crypto Binding Attribute zeroed out. The key for the
HMAC operation is obtained using the PRF algorithm based on PRF+ from
IKEv2, called the Compound Mac Key (CMK).

CMK is obtained from the first iteration of PRF using the same HMAC
cryptographic operation. Input for CMK is the CMK seed 'SSTP inner
method derived CMK' concatenated with the little endian formatted length
of the cryptographic hash used and the iteration number, whereas the key
is HLAK.

In order to support sstp-server without sstp-pppd-plugin.so, CMAC is not
verified when plugin is not available.

Resolves sorz#1

Signed-off-by: Tijs Van Buggenhout <tijs.van.buggenhout@axsguard.com>
Crypto binding verification is now supported using the
sstp-pppd-plugin.so from sstp-client.

It leverages the Higher Layer Authentication Keys from ppp to
sstp-server.
@tisj tisj force-pushed the topic/verify_crypto_binding branch from ba9c3f3 to 8d19622 Compare January 16, 2021 20:54
@tisj
Copy link
Contributor Author

tisj commented Jan 16, 2021

@zhdanow5a Okay i just fixed the issue...

And i made a typo, it is pppd instead of ppp. You will see the following if everything is alright (please correct the path to pppd to your situation)

# /usr/sbin/pppd plugin sstp-pppd-plugin.so dryrun
Plugin sstp-pppd-plugin.so loaded.

@zhdanow5a
Copy link

zhdanow5a commented Jan 16, 2021

@tisj Thank you verryy much) now it worked. As for issue. It was in chap-secrets. Some service req to auth itself (received 3 lines instead of 1 in yr code( loaded plugin + the remore system is req to auth + i couldnt find any secret) . fixed it with "user * pass * " instead of " user sstpd pass * ". " user pppd pass * " not working also.
Last question. Why we need in debug "received sha 256 hash" of cert, if it the same local cert , not even provided from client ( trys to load invalid cert in network-manager and it is worked the same as before wo any warnings)

@tisj
Copy link
Contributor Author

tisj commented Jan 16, 2021

@zhdanow5a it is actually the client that forwards the fingerprint of the certificate it got from the server. It is an additional parameter to check at server side, and as you said... It should be the same as locally configured on the server.

So if the fingerprint doesn't match you know there was a man in the middle and you have its fingerprint.

If the man in the middle is clever enough, he will have updated the fingerprint with the correct one, and only the client mac (CMAC or channel binding) is left as a means of protection, which the mitm can not guess easily as the mppe keys are unknown to him.

Not sure how this all is related to network manager. Sstp-server currently doesn't support client certificates (verification). Thus only the server certificate is validated at the client side...

Good to hear it is working for you!

@sorz
Copy link
Owner

sorz commented Jan 18, 2021

LGTM, great work! 🎉

(I haven't got it tested yet, but since it works for both of you, I will merge it first.)

@sorz sorz merged commit e60442d into sorz:master Jan 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants