diff options
author | Daniel Black <daniel@mariadb.org> | 2021-02-05 16:54:08 +1100 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2021-02-15 11:32:07 +0100 |
commit | 3646604203d80bc0f6a124aa2ac5c448229327ea (patch) | |
tree | f2ac7583d557f471ad3eca02b5768abd3fc25a70 | |
parent | 44a80b64b918ff64a4e5bbcef43f00dfe0fcde27 (diff) | |
download | php-git-3646604203d80bc0f6a124aa2ac5c448229327ea.tar.gz |
Fix #78680: mysqlnd pam plugin missing terminating null
The PAM service requires the terminating null to be part
of the communication.
Tested with MariaDB-10.4(pam) and Percona Server 5.7.32(auth_pam_compat).
Also changed MySQL Enterprise test to the server side plugin, authentication_pam
as opposed to the client plugin mysql_clear_password.
Add additional check for pamtest user and pam service file as
all are required for the test.
More importantly, test result should actually succeed.
Thanks Geoff Montee for bug report.
Closes GH-78680.
-rw-r--r-- | NEWS | 4 | ||||
-rw-r--r-- | ext/mysqli/tests/mysqli_auth_pam.phpt | 52 | ||||
-rw-r--r-- | ext/mysqlnd/mysqlnd_auth.c | 6 |
3 files changed, 55 insertions, 7 deletions
@@ -5,6 +5,10 @@ PHP NEWS - Core: . Fixed #80706 (mail(): Headers after Bcc headers may be ignored). (cmb) +- MySQLnd: + . Fixed bug #78680 (mysqlnd's mysql_clear_password does not transmit + null-terminated password). (Daniel Black) + - MySQLi: . Fixed bug #74779 (x() and y() truncating floats to integers). (cmb) diff --git a/ext/mysqli/tests/mysqli_auth_pam.phpt b/ext/mysqli/tests/mysqli_auth_pam.phpt index aa2c6a818b..587320a628 100644 --- a/ext/mysqli/tests/mysqli_auth_pam.phpt +++ b/ext/mysqli/tests/mysqli_auth_pam.phpt @@ -19,8 +19,8 @@ if (!$res = $link->query("SHOW PLUGINS")) $have_pam = false; while ($row = $res->fetch_assoc()) { - if (isset($row['Name']) && ('mysql_clear_password' == $row['Name'])) { - $have_pam = true; + if (isset($row['Name']) && in_array($row['Name'], array('pam', 'authentication_pam', 'auth_pam_compat'))) { + $have_pam = $row['Name']; break; } } @@ -29,12 +29,54 @@ $res->close(); if (!$have_pam) die("SKIP Server PAM plugin not installed"); +if ($have_pam == 'pam') { + /* MariaDB - needs system variable pam_use_cleartext_plugin=ON to be set */ + if (!$res = mysqli_query($link, 'SHOW GLOBAL VARIABLES LIKE "pam_use_cleartext_plugin"')) + die(sprintf("SKIP MariaDB probe of GLOBAL VARIABLES failed [%d] %s\n", + mysqli_errno($link), mysqli_error($link))); + $pam_use_cleartext_plugin = mysqli_fetch_row($res); + mysqli_free_result($res); + if (!$pam_use_cleartext_plugin or $pam_use_cleartext_plugin[1]!='ON') + die("SKIP Server setting pam_use_cleartext_plugin!=ON"); + + $pam_service = file_get_contents('/etc/pam.d/mysql'); +} elseif ($have_pam == 'authentication_pam') { + /* + required MySQL syntax: + https://dev.mysql.com/doc/refman/8.0/en/pam-pluggable-authentication.html#pam-pluggable-authentication-usage + */ + $have_pam .= " AS 'mysql-unix'"; + $pam_service = file_get_contents('/etc/pam.d/mysql-unix'); +} else { + $pam_service = file_get_contents('/etc/pam.d/mysql'); +} +$auth = 0; +$account = 0; +foreach (explode("\n", $pam_service) as $line) +{ + if (preg_match('/^auth/', $line)) { + $auth = 1; + } elseif (preg_match('/^account/', $line)) { + $account = 1; + } +} +if (!$auth) { + die("SKIP pam service file missing 'auth' directive"); +} +if (!$account) { + die("SKIP pam service file missing 'account' directive"); +} + +if (!posix_getpwnam('pamtest')) { + die("SKIP no pamtest user"); +} +/* Password of user 'pamtest' should be set to 'pamtest' */ mysqli_query($link, 'DROP USER pamtest'); mysqli_query($link, 'DROP USER pamtest@localhost'); -if (!mysqli_query($link, 'CREATE USER pamtest@"%" IDENTIFIED WITH mysql_clear_password') || - !mysqli_query($link, 'CREATE USER pamtest@"localhost" IDENTIFIED WITH mysql_clear_password')) { +if (!mysqli_query($link, "CREATE USER pamtest@'%' IDENTIFIED WITH $have_pam") || + !mysqli_query($link, "CREATE USER pamtest@'localhost' IDENTIFIED WITH $have_pam")) { printf("skip Cannot create second DB user [%d] %s", mysqli_errno($link), mysqli_error($link)); mysqli_close($link); die("skip CREATE USER failed"); @@ -88,6 +130,4 @@ max_execution_time=240 mysqli_query($link, 'DROP USER pamtest@localhost'); ?> --EXPECTF-- -Warning: mysqli_real_connect(): (28000/1045): Access denied for user %s -[001] Cannot connect to the server using host=%s done! diff --git a/ext/mysqlnd/mysqlnd_auth.c b/ext/mysqlnd/mysqlnd_auth.c index 915b735553..7a33fe6d58 100644 --- a/ext/mysqlnd/mysqlnd_auth.c +++ b/ext/mysqlnd/mysqlnd_auth.c @@ -652,7 +652,11 @@ mysqlnd_pam_auth_get_auth_data(struct st_mysqlnd_authentication_plugin * self, if (passwd && passwd_len) { ret = (zend_uchar*) zend_strndup(passwd, passwd_len); } - *auth_data_len = passwd_len; + /* + Trailing null required. bug#78680 + https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_authentication_methods_clear_text_password.html + */ + *auth_data_len = passwd_len + 1; return ret; } |