Issue with RADIUS Authentication Failing When Using TPM Device Authentication

Hello,

I’m experiencing an issue with RADIUS authentication in Pritunl when enabling Device Authentication (TPM) on the client side. The issue appears to be related to how the User-Password is encrypted and sent to the RADIUS server (Windows NPS).

What works:

  • When Device Authentication is disabled, Pritunl successfully authenticates against the RADIUS server.
  • The RADIUS request contains a standard User-Password field, encrypted using the expected method, and authentication succeeds.

What fails:

  • When Device Authentication (TPM) is enabled, Pritunl sends an additional Access-Request to the RADIUS server.
  • The User-Password (encrypted) field in this request is significantly different from the one sent without TPM.
  • Windows NPS rejects the authentication with Access-Reject.

Debugging details:

  • I captured and compared the RADIUS requests using Wireshark.
  • The only significant difference between a working and a failing request is the length and content of the User-Password field.
  • The authentication fails with Failed secondary authentication in Pritunl logs.

Possible cause:

It seems that when TPM-based authentication is enabled, the way User-Password is encrypted or hashed changes, causing Windows NPS to reject it.
Since Pritunl’s radius.py uses PwCrypt(password), I suspect that Windows NPS might be expecting a different hashing method (such as MS-CHAPv2 or EAP-TLS) instead of the standard RADIUS MD5-based encryption.

Questions:

  1. How does Device Authentication (TPM) affect the way Pritunl encrypts or hashes the password before sending it to RADIUS?
  2. Is there a way to ensure that Pritunl always sends the password in a format that Windows NPS expects?
  3. Has anyone successfully used Windows NPS with TPM-based authentication in Pritunl?

Any insights or suggestions would be greatly appreciated!

Thanks.

The cause of this is the server incorrectly attempting to validate the Radius on the second connection phase after it was already done during the initial authentication request used for device authentication and the dynamic firewall. At that point in the connection a token takes the place of the password, the password being sent to the Radius server is a NaCl encrypted string containing that token. This will be fixed in the next release.

It can be fixed by editing /usr/lib/pritunl/usr/lib/python3.9/site-packages/pritunl/authorizer/authorizer.py then in _check_sso(self) replace the call below.

        if not self.user.sso_auth_check(
                self.server, self.password, self.remote_ip, self.has_token):

With this call.

        if not self.user.sso_auth_check(
                self.server, self.password, self.remote_ip, self.has_fw_token or self.has_sso_token):

Then edit sudo nano /usr/lib/pritunl/usr/lib/python3.9/site-packages/pritunl/user/user.py and in sso_auth_check() replace elif RADIUS_SSO in modes with elif RADIUS_SSO in modes and not cached:

Once done run sudo systemctl restart pritunl.