Edit report at https://bugs.php.net/bug.php?id=78680&edit=1
ID: 78680
Patch added by: geoff.montee@gmail.com
Reported by: geoff dot montee at gmail dot com
Summary: mysqlnd's mysql_clear_password does not transmit
null-terminated password
Status: Open
Type: Bug
Package: MySQLi related
Operating System: Alpine Linux v3.10
PHP Version: 7.3.10
Block user comment: N
Private report: N
New Comment:
The following pull request has been associated:
Patch Name: Fix #78680: mysqlnd pam plugin missing terminating null
On GitHub: https://github.com/php/php-src/pull/6667
Patch: https://github.com/php/php-src/pull/6667.patch
Previous Comments:
------------------------------------------------------------------------
[2021-01-13 16:36:33] cmb@php.net
Well, that looks indeed wrong in mysqldn. Would you mind to
provide a pull request[1]?
[1] <https://github.com/php/php-src/pulls>
------------------------------------------------------------------------
[2019-10-17 19:34:45] geoff dot montee at gmail dot com
This problem was discovered due to the following two MariaDB bug reports:
https://jira.mariadb.org/browse/MDEV-19882
https://jira.mariadb.org/browse/MDEV-20571
------------------------------------------------------------------------
[2019-10-17 18:47:07] geoff dot montee at gmail dot com
Description:
------------
The mysql_clear_password client authentication plugin is supposed to transmit a null-terminated
password to the server. This is described in the following code comment:
" @startuml
Server->Client: 20 bytes of scramble to be ignored
Client->Server: The clear text password. null terminated.
@enduml"
https://github.com/mysql/mysql-server/blob/mysql-8.0.18/sql-common/client.cc#L8469
PHP's implementation of mysql_clear_password in the mysqlnd driver does not seem to transmit
the terminating NULL character with the password. See here:
https://github.com/php/php-src/blob/php-7.3.10/ext/mysqlnd/mysqlnd_auth.c#L610
https://github.com/php/php-src/blob/php-7.3.10/ext/mysqlnd/mysqlnd_auth.c#L115
This can easily be reproduced.
---
Let's say that I have a server running MariaDB 10.4.8. We can configure PAM authentication by
performing the following steps:
1.) Create a Unix user account and set a password for the user:
sudo useradd alice
sudo passwd alice
The examples below assume that the password is "uGBXHxID3dJRALw2".
2.) Create the PAM service configuration:
sudo tee /etc/pam.d/mariadb <<EOF
auth required pam_unix.so audit
account required pam_unix.so audit
EOF
3.) Install the pam plugin:
sudo mysql --execute="INSTALL SONAME 'auth_pam'"
4.) Create the relevant user:
sudo mysql --execute="CREATE USER 'alice'@'localhost' IDENTIFIED VIA pam
USING 'mariadb'"
5.) Enable the pam_use_cleartext_plugin system variable:
$ sudo mysql -u root --execute="SHOW GLOBAL VARIABLES LIKE 'pam%'"
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| pam_use_cleartext_plugin | ON |
| pam_winbind_workaround | OFF |
+--------------------------+-------+
At this point, PAM is configured.
---
Let's say that I set up PHP 7.3.10 on the same server as the database.
The connectivity can easily be tested by creating the following test script:
tee pamtest.php << EOF
<?php
\$link = mysqli_connect("127.0.0.1", "alice", "uGBXHxID3dJRALw2",
"test");
if (!\$link) {
echo "Error: Unable to connect to MySQL." . PHP_EOL;
echo "Debugging errno: " . mysqli_connect_errno() . PHP_EOL;
echo "Debugging error: " . mysqli_connect_error() . PHP_EOL;
exit;
}
echo "Success: A proper connection to MySQL was made! The my_db database is great." .
PHP_EOL;
echo "Host information: " . mysqli_get_host_info(\$link) . PHP_EOL;
mysqli_close(\$link);
?>
EOF
php -f pamtest.php
---
So let's run the test script, and do some debugging.
1.) First, let's attach strace to the mysqld process:
sudo mkdir strace/
cd strace/
sudo strace -o strace.out -f -ff -p $(pidof mysqld)
2.) And then let's also run the PHP script while attaching strace to that process:
strace -o strace.out -f -ff php -f pamtest.php
Authentication will fail on MariaDB 10.4.8, since that MariaDB version currently treats the NULL
terminating character as mandatory:
Warning: mysqli_connect(): (HY000/1045): Access denied for user
'alice'@'localhost' (using password: NO) in /var/www/html/pamtest.php on line 2
Error: Unable to connect to MySQL.
Debugging errno: 1045
Debugging error: Access denied for user 'alice'@'localhost' (using password: NO)
The strace output of the PHP script shows that the NULL terminating character is not set:
./strace.out.3393-recvfrom(3, "\26\0\0\2\376mysql_clear_password\0", 32768, MSG_DONTWAIT,
NULL, NULL) = 26
./strace.out.3393:sendto(3, "\20\0\0\3uGBXHxID3dJRALw2", 20, MSG_DONTWAIT, NULL, 0) = 20
And the strace output of mysqld shows the same thing:
sendto(154, "\26\0\0\2\376mysql_clear_password\0", 26, MSG_DONTWAIT, NULL, 0) = 26
recvfrom(154, "\20\0\0\3", 4, MSG_DONTWAIT, NULL, NULL) = 4
recvfrom(154, "uGBXHxID3dJRALw2", 16, MSG_DONTWAIT, NULL, NULL) = 16
In contrast, here's the strace output of mysqld with a "working" client:
sendto(154, "\26\0\0\2\376mysql_clear_password\0", 26, MSG_DONTWAIT, NULL, 0) = 26
recvfrom(154, "\21\0\0\3", 4, MSG_DONTWAIT, NULL, NULL) = 4
recvfrom(154, "uGBXHxID3dJRALw2\0", 17, MSG_DONTWAIT, NULL, NULL) = 17
------------------------------------------------------------------------
--
Edit this bug report at https://bugs.php.net/bug.php?id=78680&edit=1