summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Black <daniel@mariadb.org>2021-02-05 16:54:08 +1100
committerNikita Popov <nikita.ppv@gmail.com>2021-02-15 11:32:07 +0100
commit3646604203d80bc0f6a124aa2ac5c448229327ea (patch)
treef2ac7583d557f471ad3eca02b5768abd3fc25a70
parent44a80b64b918ff64a4e5bbcef43f00dfe0fcde27 (diff)
downloadphp-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--NEWS4
-rw-r--r--ext/mysqli/tests/mysqli_auth_pam.phpt52
-rw-r--r--ext/mysqlnd/mysqlnd_auth.c6
3 files changed, 55 insertions, 7 deletions
diff --git a/NEWS b/NEWS
index 8f8e2954b1..3bd66a0231 100644
--- a/NEWS
+++ b/NEWS
@@ -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;
}