[MDEV-20571] pam_auth_v2 still doesn't work in 10.4.8 Created: 2019-09-12  Updated: 2021-02-12  Resolved: 2019-10-28

Status: Closed
Project: MariaDB Server
Component/s: Authentication and Privilege System, Plugin - pam
Affects Version/s: 10.4.8
Fix Version/s: 10.4.9

Type: Bug Priority: Major
Reporter: Laszlo Soos Assignee: Sergei Golubchik
Resolution: Fixed Votes: 0
Labels: None

Issue Links:
Duplicate
duplicates MDEV-19882 pam v2: auth_pam_tool truncates passw... Closed

 Description   

Per https://mariadb.com/kb/en/library/authentication-plugin-pam/

modified to echo the password

tee /tmp/pam_log_script.sh <<EOF
#!/bin/bash
PASS=$(cat -)
echo $PASS
echo "\${PAM_SERVICE}:\${PAM_TYPE} - \${PAM_RUSER}@\${PAM_RHOST} is authenticating as \${PAM_USER}" 
EOF
chmod 0775 /tmp/pam_log_script.sh

Added the following into /etc/pam.d/mysql so it will send password too

auth optional pam_exec.so debug expose_authtok log=/tmp/pam_output.txt /tmp/pam_log_script.sh

V1 test:

INSTALL SONAME 'auth_pam_v1'
<authenticate from PHP with mysqli_connect("mariadb-alpine", "test_account", "uGBXHxID3dJRALw2", "test_account")>
# cat /tmp/pam_output.txt
*** Thu Sep 12 13:16:11 2019
/tmp/pam_log_script.sh: line 2: warning: command substitution: ignored null byte in input
uGBXHxID3dJRALw2
mysql:auth - @ is authenticating as test_account

V2 test:

UNINSTALL SONAME 'auth_pam_v1'
INSTALL SONAME 'auth_pam'
<authenticate from PHP with mysqli_connect("mariadb-alpine", "test_account", "uGBXHxID3dJRALw2", "test_account")>
# cat /tmp/pam_output.txt
*** Thu Sep 12 13:16:35 2019
/tmp/pam_log_script.sh: line 2: warning: command substitution: ignored null byte in input
uGBXHxID3dJRALw  <<<< LAST CHARACTER MISSING
mysql:auth - @ is authenticating as test_account

Auth fails ofc. Where did the last character from the password go?

Please fix. Thanks



 Comments   
Comment by Geoff Montee (Inactive) [ 2019-09-17 ]

Hi laca,

It works for me in MariaDB 10.4.8:

$ mysql -u alice --plugin-dir=/usr/lib64/mysql/plugin --password=uGBXHxID3dJRALw2
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 15
Server version: 10.4.8-MariaDB-log MariaDB Server
 
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
MariaDB [(none)]> SHOW CREATE USER;
+--------------------------------------------------------------------+
| CREATE USER for alice@localhost                                    |
+--------------------------------------------------------------------+
| CREATE USER 'alice'@'localhost' IDENTIFIED VIA pam USING 'mariadb' |
+--------------------------------------------------------------------+
1 row in set (0.000 sec)
 
MariaDB [(none)]> SELECT PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_STATUS FROM information_schema.PLUGINS WHERE PLUGIN_NAME='pam'\G
*************************** 1. row ***************************
   PLUGIN_NAME: pam
PLUGIN_VERSION: 2.0
 PLUGIN_STATUS: ACTIVE
1 row in set (0.000 sec)

Can you please provide the following information:

  • The SHOW CREATE USER or SHOW GRANTS for the MariaDB user account.
  • The PAM service configuration. (usually something like /etc/pam.d/mysql or /etc/pam.d/mariadb.
  • The operating system.
  • The list of MariaDB packages installed on the system. i.e. on RHEL, the output could be like this:

rpm -qa | grep -i maria

Comment by Laszlo Soos [ 2019-09-17 ]

Yes, it works for me too via mysql cli on localhost, only problem when I'm from remote (other docker container) on tcp.

Comment by Geoff Montee (Inactive) [ 2019-09-17 ]

Hi laca,

It works for me over TCP using the CLI too:

$ mysql -u alice --plugin-dir=/usr/lib64/mysql/plugin --password=uGBXHxID3dJRALw2 --host=172.30.0.123
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 28
Server version: 10.4.8-MariaDB-log MariaDB Server
 
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
MariaDB [(none)]> SELECT USER(), CURRENT_USER();
+--------------------------------------------------+----------------+
| USER()                                           | CURRENT_USER() |
+--------------------------------------------------+----------------+
| alice@ip-172-30-0-123.us-west-2.compute.internal | alice@%        |
+--------------------------------------------------+----------------+
1 row in set (0.000 sec)
 
MariaDB [(none)]> SHOW CREATE USER;
+------------------------------------------------------------+
| CREATE USER for alice@%                                    |
+------------------------------------------------------------+
| CREATE USER 'alice'@'%' IDENTIFIED VIA pam USING 'mariadb' |
+------------------------------------------------------------+
1 row in set (0.000 sec)

And it also works for me over TCP using PHP.

First, I create a simple test file:

tee pamtest.php <<EOF
<?php
 
echo "testing PAM" . PHP_EOL;
 
\$link = mysqli_connect("172.30.0.123", "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

And then I run it:

$ php -f pamtest.php
testing PAM
PHP Warning:  mysqli_connect(): Headers and client library minor version mismatch. Headers:50556 Library:100138 in /home/ec2-user/pamtest.php on line 5
Success: A proper connection to MySQL was made! The my_db database is great.
Host information: 172.30.0.123 via TCP/IP

Your problem may be caused by the version of the libmysqlclient library that your PHP installation is using. My RHEL 7 system where I performed this test has the MariaDB-compat package from 10.4.8 installed:

[ec2-user@ip-172-30-0-123 ~]$ rpm -qa | grep -i php
php-mysql-5.4.16-46.el7.x86_64
php-pdo-5.4.16-46.el7.x86_64
php-cli-5.4.16-46.el7.x86_64
php-common-5.4.16-46.el7.x86_64
[ec2-user@ip-172-30-0-123 ~]$ rpm -q MariaDB-compat
MariaDB-compat-10.4.8-1.el7.centos.x86_64

Comment by Laszlo Soos [ 2019-09-17 ]

Hi Geoff,

Interesting, let me test it further tomorrow / Friday and I'll give you more details on the setup, versions, etc...

Thanks for looking into it.

Comment by Laszlo Soos [ 2019-09-18 ]

Hi GeoffMontee,

I did the testing today.

Here is mariadb docker on alpine (docker image: wodby/mariadb:10.4-3.6.4)
Local auth works:

fa714d4c2cf6:/var/lib/mysql# mysql -u test_account --password=uGBXHxID3dJRALw2
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 37
Server version: 10.4.8-MariaDB MariaDB Server
 
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| test_account       |
+--------------------+
2 rows in set (0.001 sec)
 
fa714d4c2cf6:/var/lib/mysql# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:C0:A8:47:16
          inet addr:192.168.71.22  Bcast:192.168.71.255  Mask:255.255.255.0

Starting a php container:

dockerhost# docker run -it --rm --ip=192.168.71.42 php:7.3-fpm-alpine /bin/sh

Within the container, mysql cli tcp work via pam_auth_v2:

/var/www/html # apk add --no-cache mysql-client
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
(1/5) Installing mariadb-common (10.3.17-r0)
(2/5) Installing libgcc (8.3.0-r0)
(3/5) Installing libstdc++ (8.3.0-r0)
(4/5) Installing mariadb-client (10.3.17-r0)
(5/5) Installing mysql-client (10.3.17-r0)
Executing busybox-1.29.3-r10.trigger
OK: 49 MiB in 35 packages
/var/www/html # apk add --no-cache mariadb-connector-c
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz
(1/1) Installing mariadb-connector-c (3.0.8-r0)
OK: 50 MiB in 36 packages
/var/www/html # mysql -u test_account --password=uGBXHxID3dJRALw2 --host=192.168.71.22
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 87
Server version: 10.4.8-MariaDB MariaDB Server
 
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| test_account       |
+--------------------+
2 rows in set (0.001 sec)
 
MariaDB [(none)]> exit
Bye

Installing mysqli extension and creating test php file:

/var/www/html # docker-php-ext-install mysqli
 
/var/www/html # tee pamtest.php << EOF
<?php
\$link = mysqli_connect("192.168.71.22", "test_account", "uGBXHxID3dJRALw2", "test_account");
 
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

with v1:

/var/www/html # php -f pamtest.php
Success: A proper connection to MySQL was made! The my_db database is great.
Host information: 192.168.71.22 via TCP/IP

with v2:

/var/www/html # php -f pamtest.php
Warning: mysqli_connect(): (HY000/1045): Access denied for user 'test_account'@'5dcb9ddc6926' (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 'test_account'@'5dcb9ddc6926' (using password: NO)

PHP info:

/var/www/html # php -v
PHP 7.3.6 (cli) (built: May 31 2019 23:57:41) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.6, Copyright (c) 1998-2018 Zend Technologies

Thanks, regards,
László

Comment by Laszlo Soos [ 2019-10-07 ]

Hi GeoffMontee,

I even tried today with updated PHP. Now it's as:

/var/www/html # php -v
PHP 7.3.10 (cli) (built: Oct  4 2019 23:31:43) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.10, Copyright (c) 1998-2018 Zend Technologies

Another test with

$link = mysqli_connect("192.168.71.22", "test_account", "uGBXHxID3dJRALw2", "test_account");

FAILED.

Just to gather further proof on the theory, I tested with

$link = mysqli_connect("192.168.71.22", "test_account", "uGBXHxID3dJRALw2x", "test_account");

(an extra character after real password)
WORKS!

Thanks

Comment by Geoff Montee (Inactive) [ 2019-10-15 ]

Hi laca,

I was able to reproduce that. It seems to be the exact same problem as MDEV-19882.

I reproduced this by setting up a docker instance on my RHEL 7 system that runs the database:

sudo systemctl start docker
sudo docker run -it --rm --network=host --security-opt seccomp:unconfined php:7.3-fpm-alpine /bin/sh
apk add --no-cache strace
apk add --no-cache mysql-client
apk add --no-cache mariadb-connector-c
docker-php-ext-install mysqli
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

On the database server, I had to configure pam_use_cleartext_plugin:

$ sudo mysql -u root --execute="SHOW GLOBAL VARIABLES LIKE 'pam%'"
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| pam_use_cleartext_plugin | ON    |
| pam_winbind_workaround   | OFF   |
+--------------------------+-------+

Next, I attached strace to the mysqld process:

sudo mkdir strace/
cd strace/
sudo strace -o strace.out -f -ff -p $(pidof mysqld)

And then back on the docker instance, I ran the PHP script while also attaching strace to the process:

strace -o strace.out -f -ff php -f pamtest.php

The strace output from the PHP script shows that the password is being transmitted properly:

recvfrom(3, "\26\0\0\2\376mysql_clear_password\0", 32768, MSG_DONTWAIT, NULL, NULL) = 26
sendto(3, "\20\0\0\3uGBXHxID3dJRALw2", 20, MSG_DONTWAIT, NULL, 0) = 20

The strace output from mysqld shows that auth_pam_tool receives the correct password, but it truncates the password when it sends it to the PAM service:

read(0, "\0", 1)                        = 1
read(0, "\0\5", 2)                      = 2
read(0, "alice", 5)                     = 5
read(0, "\0\7", 2)                      = 2
read(0, "mariadb", 7)                   = 7
...
write(1, "C", 1)                        = 1
write(1, "\0\v", 2)                     = 2
write(1, "\4Password: ", 11)            = 11
read(0, "\0\20", 2)                     = 2
read(0, "uGBXHxID3dJRALw2", 16)         = 16
...
write(4, "uGBXHxID3dJRALw", 15)         = 15
write(4, "\0", 1)                       = 1

This is the same behavior reported in MDEV-19882.

When I compare these to the strace from a working client, I see that the password is NULL-terminated, and the length is one character longer:

read(0, "\0", 1)                        = 1
read(0, "\0\5", 2)                      = 2
read(0, "alice", 5)                     = 5
read(0, "\0\7", 2)                      = 2
read(0, "mariadb", 7)                   = 7
...
write(1, "C", 1)                        = 1
write(1, "\0\v", 2)                     = 2
write(1, "\4Password: ", 11)            = 11
read(0, "\0\21", 2)                     = 2
read(0, "uGBXHxID3dJRALw2\0", 17)       = 17
...
write(4, "uGBXHxID3dJRALw2", 16)        = 16
write(4, "\0", 1)                       = 1

Comment by Laszlo Soos [ 2019-10-16 ]

Hi GeoffMontee,

Sorry for not mentioning it earlier, yes I do indeed use cleartext_plugin on server side.

Thanks,
Laszlo

Comment by Geoff Montee (Inactive) [ 2019-10-17 ]

The relevant code in PHP's mysqlnd driver is in these locations:

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

It does look like this code is probably not considering the password's NULL terminator to be part of the auth data that would get sent over the wire.

Comment by Geoff Montee (Inactive) [ 2019-10-17 ]

I reported the following PHP bug about this:

https://bugs.php.net/bug.php?id=78680

Comment by Daniel Black [ 2021-02-12 ]

Upstream php-7.4+ fix approved - https://github.com/php/php-src/pull/6667

Generated at Thu Feb 08 09:00:30 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.