dapphp / radius

A pure PHP RADIUS client based on SysCo/al implementation
GNU Lesser General Public License v3.0
80 stars 43 forks source link

Cannot authentificate with a specific special character in password #5

Open mneitsabes opened 5 years ago

mneitsabes commented 5 years ago

Hi,

I'm using the version 2.5.4 of your library. When a user try to authentificate with a password containing the £ character, the authentification fails.

I used a Windows Server 2016 and the password is working within the domain.

Best regards,

VitriolCZ commented 5 years ago

I have same problem with czech symbols ěščřžýáíé

dapphp commented 5 years ago

Hi,

Sorry for the delay to the initial post. I started to look in to this and then dropped it.

Coming back to it today, I don't have an answer but we don't seem to be alone with the problem. Here are a couple similar reports:

https://serverfault.com/questions/644046/%C2%A3-sign-in-password-fails-windows-nps-radius-authentication-against-sonicwall-sra https://feedback.azure.com/forums/169401-azure-active-directory/suggestions/36021112-allow-for-multi-byte-unicode-characters-to-be-al

Using dapphp/radius client to auth against FreeRADIUS, I can auth fine with passwords such as Password1£, ěščřžýáíé1@#, 😎 🐈 🏃 🐎 ¯_(ツ)_/¯, пассворд1! etc etc. Similarly, radclient from FreeRADIUS works similarly.

Using Windows Server 2016, copying the £ from this post and changing the password to Password1£ from AD Users & Computers (with or without store reversible password), I am unable to auth with either radius client using PAP, CHAP, CHAP-MD5, or EAP-MSCHAPv2.

For example:

$ radtest nemo 'Password1!' xx.xx.xx.xx 20 xyzzy5461
Sending Access-Request of id 30 to xx.xx.xx.xx port 1812
    User-Name = "nemo"
    User-Password = "Password1!"
    NAS-IP-Address = 127.0.0.1
    NAS-Port = 20
    Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Accept packet from host xx.xx.xx.xx port 1812, id=30, length=90
    Class = 0xc5a10a930000013700010200408e122f00000000c56bc667a3f4cdae01d4a8532ea252dc000000000000000b
    MS-Link-Utilization-Threshold = 50
    MS-Link-Drop-Time-Limit = 120

Then changing password:

$ radtest nemo 'Password1£' xx.xx.xx.xx 20 xyzzy5461
Sending Access-Request of id 25 to xx.xx.xx.xx port 1812
    User-Name = "nemo"
    User-Password = "Password1£"
    NAS-IP-Address = 127.0.0.1
    NAS-Port = 20
    Message-Authenticator = 0x00000000000000000000000000000000
rad_recv: Access-Reject packet from host xx.xx.xx.xx port 1812, id=25, length=20

Similarly, it will not work with ěščřžýáíé1@#.

In all cases, the NPS server logs a generic password mismatch error like:

Authentication Details:
    Connection Request Policy Name: Use Windows authentication for all users
    Network Policy Name:        -
    Authentication Provider:        Windows
    Authentication Server:      WIN-A3MG0N4A3EC.testing.local
    Authentication Type:        MS-CHAPv1
    EAP Type:           -
    Account Session Identifier:     -
    Logging Results:            Accounting information was written to the local log file.
    Reason Code:            16
    Reason:             Authentication failed due to a user credentials mismatch. Either the user name provided does not map to an existing user account or the password was incorrect.

It would seem that PAP would be a good test case as it's the simplest algorithm for encoding a password. I've also tried with and without multi-byte aware code, though it seems we should be operating on bytes, and not characters. I have confirmed that PAP auth using the current code works fine against FreeRADIUS.

I can't turn up any useful information as to what the problem is or what, if any, solutions there are.

Anyone have any ideas?

I'm going to try some Radius clients made for Windows next, locally on the server.

mportelag commented 5 years ago

Hi,

We´re also using version 2.5.4 and detect the authentication error with special characters. We use Windows Server 2016 and the passwords work with our the domain.

The problem seems to be that the library interprets all characters with 1 byte (basic UTF-8) while special characters are encoded with more than 1 byte (see column UTF-8(hex.) in https://www.utf8-chartable.de/unicode-utf8-table.pl?names=-&unicodeinhtml=hex). The solution is to update the code implementing it with the functions that contemplate the possibility of more than 1 byte to code (implementations mb (multibyte string) -> https://www.php.net/manual/en/book.mbstring.php).

We have reviewed the code and have developed a possible implementation of the str2unicode($str) function (Pear_CHAP.php file):

PROBLEM: for ($i = 0; $i < strlen($str); $i++) { $a = ord($str{$i}) << 8; $uni .= sprintf("%X", $a); } SOLUTION: for ($i = 0; $i < mb_strlen($str); $i++) { $a = mb_ord(mb_substr($str,$i,1)) << 8; $uni .= sprintf("%X", $a); }

Example: Spanish character ñ

We have tested the solution on our system using EAP-MSCHAPv2 authentication. If you agree with this possible solution, could you update the library with these changes? Thank you.

Regards,

Miguel.

mportelag commented 5 years ago

Hi,

We´re also using version 2.5.4 and detect the authentication error with special characters. We use Windows Server 2016 and the passwords work with our the domain.

The problem seems to be that the library interprets all characters with 1 byte (basic UTF-8) while special characters are encoded with more than 1 byte (see column UTF-8(hex.) in https://www.utf8-chartable.de/unicode-utf8-table.pl?names=-&unicodeinhtml=hex). The solution is to update the code implementing it with the functions that contemplate the possibility of more than 1 byte to code (implementations mb (multibyte string) -> https://www.php.net/manual/en/book.mbstring.php).

We have reviewed the code and have developed a possible implementation of the str2unicode($str) function (Pear_CHAP.php file):

PROBLEM: for ($i = 0; $i < strlen($str); $i++) { $a = ord($str{$i}) << 8; $uni .= sprintf("%X", $a); } SOLUTION: for ($i = 0; $i < mb_strlen($str); $i++) { $a = mb_ord(mb_substr($str,$i,1)) << 8; $uni .= sprintf("%X", $a); }

Example: Spanish character ñ

* Bad: Library interprets two characters (1 byte + 1 byte)

  * character 1:
    UTF-8 Hex: c3
    Unicode: C300
  * character 2:
    UTF-8 Hex: b1
    Unicode: B100

* Good: Library must be interpret one character (2 bytes)

  * character:
    UTF-8 Hex: c3 b1
    Unicode: F100

We have tested the solution on our system using EAP-MSCHAPv2 authentication. If you agree with this possible solution, could you update the library with these changes? Thank you.

Regards,

Miguel.

NOTE: This proposal only work with PHP >= 7.2. mb_ord function was added in this version (https://www.php.net/manual/en/function.mb-ord.php). For use with older php versions, function can can be manually coded (like https://github.com/symfony/polyfill-mbstring/blob/master/Mbstring.php).

dapphp commented 5 years ago

Hi Miguel (@mportelag),

Thank you for the work on this. When I test it, it does fix auth for password Password1£, but I still get incorrect password errors for пассворд1@@ and ěščřžýáíé1@#. Maybe it's something with my test Windows server or NPS setup.

Are you able to auth either of those passwords using your fix?

I can't auth using any of these passwords when testing with FreeRADIUS radtest either. I've yet to figure out what M$ is doing different. With your code, I've tried converting from UTF-8 to UCS-2/UCS-4/UTF-16 and various other encodings without any luck. It doesn't give me great hope that even the FreeRADIUS client can't auth with the passwords against Windows Server 2016.

mportelag commented 5 years ago

Hi @dapphp , We test our code with your passwords and gets incorrect password too. We don´t understand what is the problem. If we develop any solution to all cases, we'll notify you. Regards,

HenkPoley commented 4 years ago

Maybe this is relevant: https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/6e803168-f140-4d23-b2d3-c3a8ab5917d2

However, the DC requires that the password value be specified in a UTF-16 encoded Unicode string containing the password surrounded by quotation marks, [..] .. Following is an example of the first steps of password encoding. Suppose the implementer wants to set unicodePwd to the string "new".

 ASCII "new":     0x6E 0x65 0x77
 UTF-16 "new":    0x6E 0x00 0x65 0x00 0x77 0x00
 UTF-16 "new"
     with quotes: 0x22 0x00 0x6E 0x00 0x65 0x00 0x77 0x00 0x22 0x00

The 10-byte octet string is then BER-encoded and sent in an LDAP Modify request as described previously.