
[Commits] b59f297: MDEV-15473 Isolate/sandbox PAM modules, so that they can't crash the server.
by holyfoot@askmonty.org 02 Jul '18
by holyfoot@askmonty.org 02 Jul '18
02 Jul '18
revision-id: b59f297248ef699eb8c4881cb5c54763c81488af (mariadb-10.3.6-33-gb59f297)
parent(s): 4b0cedf82d8d8ba582648dcb4a2620c146862a43
committer: Alexey Botchkov
timestamp: 2018-07-02 16:41:27 +0400
message:
MDEV-15473 Isolate/sandbox PAM modules, so that they can't crash the server.
work
---
.gitignore | 1 +
debian/mariadb-server-10.3.install | 2 +
mysql-test/mysql-test-run.pl | 14 ++
mysql-test/suite/plugins/r/pam.result | 7 +
mysql-test/suite/plugins/suite.pm | 2 +
mysql-test/suite/plugins/t/pam.test | 12 ++
mysql-test/suite/plugins/t/pam_init_v1.inc | 14 ++
mysql-test/suite/plugins/t/pam_v1.test | 40 ++++
plugin/auth_pam/CMakeLists.txt | 6 +-
plugin/auth_pam/auth_pam.c | 287 ++++++++++++++---------------
plugin/auth_pam/auth_pam_base.c | 179 ++++++++++++++++++
plugin/auth_pam/auth_pam_common.c | 50 +++++
plugin/auth_pam/auth_pam_tool.c | 121 ++++++++++++
plugin/auth_pam/auth_pam_tool.h | 81 ++++++++
plugin/auth_pam/auth_pam_v1.c | 71 +++++++
plugin/auth_pam/testing/pam_mariadb_mtr.c | 10 +
16 files changed, 743 insertions(+), 154 deletions(-)
diff --git a/.gitignore b/.gitignore
index 932c666..798dbad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -100,6 +100,7 @@ pcre/pcre_chartables.c
pcre/pcregrep
pcre/pcretest
pcre/test*grep
+plugin/auth_pam/auth_pam_tool
plugin/aws_key_management/aws-sdk-cpp
plugin/aws_key_management/aws_sdk_cpp
plugin/aws_key_management/aws_sdk_cpp-prefix
diff --git a/debian/mariadb-server-10.3.install b/debian/mariadb-server-10.3.install
index a7d4d66..106b3a1 100644
--- a/debian/mariadb-server-10.3.install
+++ b/debian/mariadb-server-10.3.install
@@ -40,6 +40,8 @@ usr/bin/wsrep_sst_xtrabackup
usr/bin/wsrep_sst_xtrabackup-v2
usr/lib/mysql/plugin/auth_ed25519.so
usr/lib/mysql/plugin/auth_pam.so
+usr/lib/mysql/plugin/auth_pam_tool_dir/auth_pam_tool
+usr/lib/mysql/plugin/auth_pam_v1.so
usr/lib/mysql/plugin/auth_socket.so
usr/lib/mysql/plugin/disks.so
usr/lib/mysql/plugin/file_key_management.so
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index ddb79b9..dfb0cee 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -2603,8 +2603,22 @@ sub setup_vardir() {
unlink "$plugindir/symlink_test";
}
+ for (<$bindir/plugin/auth_pam/auth_pam_tool>)
+ {
+ mkpath("$plugindir/auth_pam_tool_dir");
+ if ($opt_use_copy)
+ {
+ copy rel2abs($_), "$plugindir/auth_pam_tool_dir/auth_pam_tool"
+ }
+ else
+ {
+ symlink rel2abs($_), "$plugindir/auth_pam_tool_dir/auth_pam_tool";
+ }
+ }
+
for (<$bindir/storage/*/*.so>,
<$bindir/plugin/*/*.so>,
+ <$bindir/plugin/*/auth_pam_tool_dir>,
<$bindir/libmariadb/plugins/*/*.so>,
<$bindir/libmariadb/*.so>,
<$bindir/sql/*.so>)
diff --git a/mysql-test/suite/plugins/r/pam.result b/mysql-test/suite/plugins/r/pam.result
index 8630320..a16cd7f 100644
--- a/mysql-test/suite/plugins/r/pam.result
+++ b/mysql-test/suite/plugins/r/pam.result
@@ -20,6 +20,13 @@ Challenge input first.
Enter: not very secret challenge
Now, the magic number!
PIN: ****
+#
+# athentication is unsuccessful
+#
+Challenge input first.
+Enter: crash pam module
+Now, the magic number!
+PIN: ***
drop user test_pam;
drop user pam_test;
uninstall plugin pam;
diff --git a/mysql-test/suite/plugins/suite.pm b/mysql-test/suite/plugins/suite.pm
index a2ac395..346f28c 100644
--- a/mysql-test/suite/plugins/suite.pm
+++ b/mysql-test/suite/plugins/suite.pm
@@ -14,6 +14,8 @@ sub skip_combinations {
my %skip;
$skip{'t/pam.test'} = 'No pam setup for mtr'
unless -e '/etc/pam.d/mariadb_mtr';
+ $skip{'t/pam_v1.test'} = 'No pam setup for mtr'
+ unless -e '/etc/pam.d/mariadb_mtr';
$skip{'t/cassandra.test'} = 'Cassandra is not running'
unless cassandra_running();
$skip{'t/cassandra_qcache.test'} = $skip{'t/cassandra.test'};
diff --git a/mysql-test/suite/plugins/t/pam.test b/mysql-test/suite/plugins/t/pam.test
index 8a95d6b..852f165 100644
--- a/mysql-test/suite/plugins/t/pam.test
+++ b/mysql-test/suite/plugins/t/pam.test
@@ -13,6 +13,12 @@ not very secret challenge
select user(), current_user(), database();
EOF
+--write_file $MYSQLTEST_VARDIR/tmp/pam_ugly.txt
+crash pam module
+666
+select user(), current_user(), database();
+EOF
+
--echo #
--echo # athentication is successful, challenge/pin are ok
--echo # note that current_user() differs from user()
@@ -25,6 +31,12 @@ EOF
--error 1
--exec $MYSQL_TEST -u test_pam --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_bad.txt
+--echo #
+--echo # athentication is unsuccessful
+--echo #
+--error 1
+--exec $MYSQL_TEST -u test_pam --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_ugly.txt
+
--remove_file $MYSQLTEST_VARDIR/tmp/pam_good.txt
--remove_file $MYSQLTEST_VARDIR/tmp/pam_bad.txt
drop user test_pam;
diff --git a/mysql-test/suite/plugins/t/pam_init_v1.inc b/mysql-test/suite/plugins/t/pam_init_v1.inc
new file mode 100644
index 0000000..4861d4f
--- /dev/null
+++ b/mysql-test/suite/plugins/t/pam_init_v1.inc
@@ -0,0 +1,14 @@
+
+--source include/not_embedded.inc
+
+if (!$AUTH_PAM_V1_SO) {
+ skip No pam auth plugin;
+}
+
+eval install plugin pam soname '$AUTH_PAM_V1_SO';
+create user test_pam identified via pam using 'mariadb_mtr';
+create user pam_test;
+grant proxy on pam_test to test_pam;
+
+let $plugindir=`SELECT @@global.plugin_dir`;
+
diff --git a/mysql-test/suite/plugins/t/pam_v1.test b/mysql-test/suite/plugins/t/pam_v1.test
new file mode 100644
index 0000000..f8a346e
--- /dev/null
+++ b/mysql-test/suite/plugins/t/pam_v1.test
@@ -0,0 +1,40 @@
+
+--source pam_init.inc
+
+--write_file $MYSQLTEST_VARDIR/tmp/pam_good.txt
+not very secret challenge
+9225
+select user(), current_user(), database();
+EOF
+
+--write_file $MYSQLTEST_VARDIR/tmp/pam_bad.txt
+not very secret challenge
+9224
+select user(), current_user(), database();
+EOF
+
+--echo #
+--echo # athentication is successful, challenge/pin are ok
+--echo # note that current_user() differs from user()
+--echo #
+--exec $MYSQL_TEST -u test_pam --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_good.txt
+
+--echo #
+--echo # athentication is unsuccessful
+--echo #
+--error 1
+--exec $MYSQL_TEST -u test_pam --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_bad.txt
+
+--echo #
+--echo # pam module crashes
+--echo #
+--error 1
+--exec $MYSQL_TEST -u crash_pam_tool --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_good.txt
+
+--remove_file $MYSQLTEST_VARDIR/tmp/pam_good.txt
+--remove_file $MYSQLTEST_VARDIR/tmp/pam_bad.txt
+drop user test_pam;
+drop user pam_test;
+let $count_sessions= 1;
+--source include/wait_until_count_sessions.inc
+uninstall plugin pam;
diff --git a/plugin/auth_pam/CMakeLists.txt b/plugin/auth_pam/CMakeLists.txt
index 5131752..4943d57 100644
--- a/plugin/auth_pam/CMakeLists.txt
+++ b/plugin/auth_pam/CMakeLists.txt
@@ -8,6 +8,10 @@ IF(HAVE_PAM_APPL_H)
IF(HAVE_STRNDUP)
ADD_DEFINITIONS(-DHAVE_STRNDUP)
ENDIF(HAVE_STRNDUP)
- MYSQL_ADD_PLUGIN(auth_pam auth_pam.c LINK_LIBRARIES pam MODULE_ONLY)
+ ADD_DEFINITIONS(-D_GNU_SOURCE)
+ MYSQL_ADD_PLUGIN(auth_pam_v1 auth_pam_v1.c LINK_LIBRARIES pam MODULE_ONLY)
+ MYSQL_ADD_PLUGIN(auth_pam auth_pam.c LINK_LIBRARIES pam dl MODULE_ONLY)
+ MYSQL_ADD_EXECUTABLE(auth_pam_tool auth_pam_tool.c DESTINATION ${INSTALL_PLUGINDIR}/auth_pam_tool_dir COMPONENT Server)
+ TARGET_LINK_LIBRARIES(auth_pam_tool pam)
ENDIF(HAVE_PAM_APPL_H)
diff --git a/plugin/auth_pam/auth_pam.c b/plugin/auth_pam/auth_pam.c
index ffc3d6f..1ffc328 100644
--- a/plugin/auth_pam/auth_pam.c
+++ b/plugin/auth_pam/auth_pam.c
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2011, 2012, Monty Program Ab
+ Copyright (c) 2011, 2018 MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,36 +14,12 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
-#define _GNU_SOURCE 1 /* for strndup */
-#include <mysql/plugin_auth.h>
-#include <stdio.h>
+#include <unistd.h>
#include <string.h>
-#include <security/pam_appl.h>
-#include <security/pam_modules.h>
-
-struct param {
- unsigned char buf[10240], *ptr;
- MYSQL_PLUGIN_VIO *vio;
-};
-
-/* It least solaris doesn't have strndup */
-
-#ifndef HAVE_STRNDUP
-char *strndup(const char *from, size_t length)
-{
- char *ptr;
- size_t max_length= strlen(from);
- if (length > max_length)
- length= max_length;
- if ((ptr= (char*) malloc(length+1)) != 0)
- {
- memcpy((char*) ptr, (char*) from, length);
- ptr[length]=0;
- }
- return ptr;
-}
-#endif
+#include <mysql/plugin_auth.h>
+#include "auth_pam_tool.h"
+#include <my_global.h>
#ifndef DBUG_OFF
static char pam_debug = 0;
@@ -52,158 +28,163 @@ static char pam_debug = 0;
#define PAM_DEBUG(X) /* no-op */
#endif
-static int conv(int n, const struct pam_message **msg,
- struct pam_response **resp, void *data)
+static char *opt_plugin_dir; /* To be dynamically linked. */
+static const char *tool_name= "auth_pam_tool_dir/auth_pam_tool";
+static const int tool_name_len= 31;
+
+static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{
- struct param *param = (struct param *)data;
- unsigned char *end = param->buf + sizeof(param->buf) - 1;
- int i;
+ int p_to_c[2], c_to_p[2]; /* Parent-to-child and child-to-parent pipes. */
+ pid_t proc_id;
+ int result= CR_ERROR;
+ unsigned char field;
- *resp = 0;
+ PAM_DEBUG((stderr, "PAM: opening pipes.\n"));
+ if (pipe(p_to_c) < 0 || pipe(c_to_p) < 0)
+ {
+ /* Error creating pipes. */
+ return CR_ERROR;
+ }
+ PAM_DEBUG((stderr, "PAM: forking.\n"));
+ if ((proc_id= fork()) < 0)
+ {
+ /* Error forking. */
+ close(p_to_c[0]);
+ close(c_to_p[1]);
+ goto error_ret;
+ }
- for (i = 0; i < n; i++)
+ if (proc_id == 0)
{
- /* if there's a message - append it to the buffer */
- if (msg[i]->msg)
+ /* The 'sandbox' process started. */
+ char toolpath[FN_REFLEN];
+ size_t plugin_dir_len= strlen(opt_plugin_dir);
+
+ PAM_DEBUG((stderr, "PAM: Child process prepares pipes.\n"));
+
+ if (close(p_to_c[1]) < 0 ||
+ close(c_to_p[0]) < 0 ||
+ dup2(p_to_c[0], 0) < 0 || /* Parent's pipe to STDIN. */
+ dup2(c_to_p[1], 1) < 0) /* Sandbox's pipe to STDOUT. */
{
- int len = strlen(msg[i]->msg);
- if (len > end - param->ptr)
- len = end - param->ptr;
- if (len > 0)
- {
- memcpy(param->ptr, msg[i]->msg, len);
- param->ptr+= len;
- *(param->ptr)++ = '\n';
- }
+ exit(-1);
}
- /* if the message style is *_PROMPT_*, meaning PAM asks a question,
- send the accumulated text to the client, read the reply */
- if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
- msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+
+ PAM_DEBUG((stderr, "PAM: check tool directory: %s, %s.\n",
+ opt_plugin_dir, tool_name));
+ if (plugin_dir_len + tool_name_len + 2 > sizeof(toolpath))
{
- int pkt_len;
- unsigned char *pkt;
+ /* Tool path too long. */
+ exit(-1);
+ }
- /* allocate the response array.
- freeing it is the responsibility of the caller */
- if (*resp == 0)
- {
- *resp = calloc(sizeof(struct pam_response), n);
- if (*resp == 0)
- return PAM_BUF_ERR;
- }
+ memcpy(toolpath, opt_plugin_dir, plugin_dir_len);
+ if (plugin_dir_len && toolpath[plugin_dir_len-1] != FN_LIBCHAR)
+ toolpath[plugin_dir_len++]= FN_LIBCHAR;
+ memcpy(toolpath+plugin_dir_len, tool_name, tool_name_len+1);
- /* dialog plugin interprets the first byte of the packet
- as the magic number.
- 2 means "read the input with the echo enabled"
- 4 means "password-like input, echo disabled"
- C'est la vie. */
- param->buf[0] = msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? 2 : 4;
- PAM_DEBUG((stderr, "PAM: conv: send(%.*s)\n", (int)(param->ptr - param->buf - 1), param->buf));
- if (param->vio->write_packet(param->vio, param->buf, param->ptr - param->buf - 1))
- return PAM_CONV_ERR;
-
- pkt_len = param->vio->read_packet(param->vio, &pkt);
- if (pkt_len < 0)
- {
- PAM_DEBUG((stderr, "PAM: conv: recv() ERROR\n"));
- return PAM_CONV_ERR;
- }
- PAM_DEBUG((stderr, "PAM: conv: recv(%.*s)\n", pkt_len, pkt));
- /* allocate and copy the reply to the response array */
- if (!((*resp)[i].resp= strndup((char*) pkt, pkt_len)))
- return PAM_CONV_ERR;
- param->ptr = param->buf + 1;
- }
+ PAM_DEBUG((stderr, "PAM: execute pam sandbox [%s].\n", toolpath));
+ (void) execl(toolpath, toolpath, NULL);
+ PAM_DEBUG((stderr, "PAM: exec() failed.\n"));
+ exit(-1);
}
- return PAM_SUCCESS;
-}
-#define DO(X) if ((status = (X)) != PAM_SUCCESS) goto end
+ /* Parent process continues. */
-#if defined(SOLARIS) || defined(__sun)
-typedef void** pam_get_item_3_arg;
-#else
-typedef const void** pam_get_item_3_arg;
-#endif
+ PAM_DEBUG((stderr, "PAM: parent continues.\n"));
+ if (close(p_to_c[0]) < 0 ||
+ close(c_to_p[1]) < 0)
+ goto error_ret;
-static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
-{
- pam_handle_t *pamh = NULL;
- int status;
- const char *new_username= NULL;
- struct param param;
- /* The following is written in such a way to make also solaris happy */
- struct pam_conv pam_start_arg = { &conv, (char*) ¶m };
- /*
- get the service name, as specified in
+ PAM_DEBUG((stderr, "PAM: parent sends user data [%s], [%s].\n",
+ info->user_name, info->auth_string));
- CREATE USER ... IDENTIFIED WITH pam AS "service"
- */
- const char *service = info->auth_string && info->auth_string[0]
- ? info->auth_string : "mysql";
+#ifndef DBUG_OFF
+ field= pam_debug;
+#else
+ field= 0;
+#endif
+ if (write(p_to_c[1], &field, 1) != 1 ||
+ write_string(p_to_c[1], (const uchar *) info->user_name,
+ info->user_name_length) ||
+ write_string(p_to_c[1], (const uchar *) info->auth_string,
+ info->auth_string_length))
+ goto error_ret;
+
+ for (;;)
+ {
+ PAM_DEBUG((stderr, "PAM: listening to the sandbox.\n"));
+ if (read(c_to_p[0], &field, 1) < 1)
+ {
+ PAM_DEBUG((stderr, "PAM: read failed.\n"));
+ goto error_ret;
+ }
- param.ptr = param.buf + 1;
- param.vio = vio;
+ if (field == AP_EOF)
+ {
+ PAM_DEBUG((stderr, "PAM: auth OK returned.\n"));
+ break;
+ }
- PAM_DEBUG((stderr, "PAM: pam_start(%s, %s)\n", service, info->user_name));
- DO( pam_start(service, info->user_name, &pam_start_arg, &pamh) );
+ switch (field)
+ {
+ case AP_AUTHENTICATED_AS:
+ PAM_DEBUG((stderr, "PAM: reading authenticated_as string.\n"));
+ if (read_string(c_to_p[0], info->authenticated_as,
+ sizeof(info->authenticated_as) - 1) < 0)
+ goto error_ret;
+ break;
+
+ case AP_CONV:
+ {
+ unsigned char buf[10240];
+ int buf_len;
+ unsigned char *pkt;
- PAM_DEBUG((stderr, "PAM: pam_authenticate(0)\n"));
- DO( pam_authenticate (pamh, 0) );
+ PAM_DEBUG((stderr, "PAM: getting CONV string.\n"));
+ if ((buf_len= read_string(c_to_p[0], (char *) buf, sizeof(buf))) < 0)
+ goto error_ret;
- PAM_DEBUG((stderr, "PAM: pam_acct_mgmt(0)\n"));
- DO( pam_acct_mgmt(pamh, 0) );
+ PAM_DEBUG((stderr, "PAM: sending CONV string.\n"));
+ if (vio->write_packet(vio, buf, buf_len))
+ goto error_ret;
- PAM_DEBUG((stderr, "PAM: pam_get_item(PAM_USER)\n"));
- DO( pam_get_item(pamh, PAM_USER, (pam_get_item_3_arg) &new_username) );
+ PAM_DEBUG((stderr, "PAM: reading CONV answer.\n"));
+ if ((buf_len= vio->read_packet(vio, &pkt)) < 0)
+ goto error_ret;
- if (new_username && strcmp(new_username, info->user_name))
- strncpy(info->authenticated_as, new_username,
- sizeof(info->authenticated_as));
- info->authenticated_as[sizeof(info->authenticated_as)-1]= 0;
+ PAM_DEBUG((stderr, "PAM: answering CONV.\n"));
+ if (write_string(p_to_c[1], pkt, buf_len))
+ goto error_ret;
+ }
+ break;
-end:
- pam_end(pamh, status);
- PAM_DEBUG((stderr, "PAM: status = %d user = %s\n", status, info->authenticated_as));
- return status == PAM_SUCCESS ? CR_OK : CR_ERROR;
-}
+ default:
+ PAM_DEBUG((stderr, "PAM: unknown sandbox field.\n"));
+ goto error_ret;
+ }
+ }
+ result= CR_OK;
-static struct st_mysql_auth info =
-{
- MYSQL_AUTHENTICATION_INTERFACE_VERSION,
- "dialog",
- pam_auth
-};
-
-static char use_cleartext_plugin;
-static MYSQL_SYSVAR_BOOL(use_cleartext_plugin, use_cleartext_plugin,
- PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
- "Use mysql_cleartext_plugin on the client side instead of the dialog "
- "plugin. This may be needed for compatibility reasons, but it only "
- "supports simple PAM policies that don't require anything besides "
- "a password", NULL, NULL, 0);
+error_ret:
+ close(p_to_c[1]);
+ close(c_to_p[0]);
-#ifndef DBUG_OFF
-static MYSQL_SYSVAR_BOOL(debug, pam_debug, PLUGIN_VAR_OPCMDARG,
- "Log all PAM activity", NULL, NULL, 0);
-#endif
+ PAM_DEBUG((stderr, "PAM: auth result %d.\n", result));
+ return result;
+}
-static struct st_mysql_sys_var* vars[] = {
- MYSQL_SYSVAR(use_cleartext_plugin),
-#ifndef DBUG_OFF
- MYSQL_SYSVAR(debug),
-#endif
- NULL
-};
+#include "auth_pam_common.c"
static int init(void *p __attribute__((unused)))
{
if (use_cleartext_plugin)
info.client_auth_plugin= "mysql_clear_password";
+ if (!(opt_plugin_dir= dlsym(RTLD_DEFAULT, "opt_plugin_dir")))
+ return 1;
return 0;
}
@@ -212,15 +193,15 @@ maria_declare_plugin(pam)
MYSQL_AUTHENTICATION_PLUGIN,
&info,
"pam",
- "Sergei Golubchik",
+ "MariaDB Corp",
"PAM based authentication",
PLUGIN_LICENSE_GPL,
init,
NULL,
- 0x0100,
+ 0x0200,
NULL,
vars,
- "1.0",
- MariaDB_PLUGIN_MATURITY_STABLE
+ "2.0",
+ MariaDB_PLUGIN_MATURITY_BETA
}
maria_declare_plugin_end;
diff --git a/plugin/auth_pam/auth_pam_base.c b/plugin/auth_pam/auth_pam_base.c
new file mode 100644
index 0000000..68be0e9
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_base.c
@@ -0,0 +1,179 @@
+/*
+ Copyright (c) 2011, 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+/*
+ This file contains code to interact with the PAM module.
+ To be included into auth_pam_tool.c and auth_pam_v2.c,
+
+ Before the #include these sould be defined:
+
+ struct param {
+ unsigned char buf[10240], *ptr;
+ MYSQL_PLUGIN_VIO *vio;
+ ... other arbitrary fields allowed.
+ };
+ static int write_packet(struct param *param, const unsigned char *buf,
+ int buf_len)
+ static int read_packet(struct param *param, unsigned char **pkt)
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+
+/* It least solaris doesn't have strndup */
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *from, size_t length)
+{
+ char *ptr;
+ size_t max_length= strlen(from);
+ if (length > max_length)
+ length= max_length;
+ if ((ptr= (char*) malloc(length+1)) != 0)
+ {
+ memcpy((char*) ptr, (char*) from, length);
+ ptr[length]=0;
+ }
+ return ptr;
+}
+#endif
+
+#ifndef DBUG_OFF
+static char pam_debug = 0;
+#define PAM_DEBUG(X) do { if (pam_debug) { fprintf X; } } while(0)
+#else
+#define PAM_DEBUG(X) /* no-op */
+#endif
+
+static int conv(int n, const struct pam_message **msg,
+ struct pam_response **resp, void *data)
+{
+ struct param *param = (struct param *)data;
+ unsigned char *end = param->buf + sizeof(param->buf) - 1;
+ int i;
+
+ *resp = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ /* if there's a message - append it to the buffer */
+ if (msg[i]->msg)
+ {
+ int len = strlen(msg[i]->msg);
+ if (len > end - param->ptr)
+ len = end - param->ptr;
+ if (len > 0)
+ {
+ memcpy(param->ptr, msg[i]->msg, len);
+ param->ptr+= len;
+ *(param->ptr)++ = '\n';
+ }
+ }
+ /* if the message style is *_PROMPT_*, meaning PAM asks a question,
+ send the accumulated text to the client, read the reply */
+ if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
+ msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+ {
+ int pkt_len;
+ unsigned char *pkt;
+
+ /* allocate the response array.
+ freeing it is the responsibility of the caller */
+ if (*resp == 0)
+ {
+ *resp = calloc(sizeof(struct pam_response), n);
+ if (*resp == 0)
+ return PAM_BUF_ERR;
+ }
+
+ /* dialog plugin interprets the first byte of the packet
+ as the magic number.
+ 2 means "read the input with the echo enabled"
+ 4 means "password-like input, echo disabled"
+ C'est la vie. */
+ param->buf[0] = msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? 2 : 4;
+ PAM_DEBUG((stderr, "PAM: conv: send(%.*s)\n",
+ (int)(param->ptr - param->buf - 1), param->buf));
+ if (write_packet(param, param->buf, param->ptr - param->buf - 1))
+ return PAM_CONV_ERR;
+
+ pkt_len = read_packet(param, &pkt);
+ if (pkt_len < 0)
+ {
+ PAM_DEBUG((stderr, "PAM: conv: recv() ERROR\n"));
+ return PAM_CONV_ERR;
+ }
+ PAM_DEBUG((stderr, "PAM: conv: recv(%.*s)\n", pkt_len, pkt));
+ /* allocate and copy the reply to the response array */
+ if (!((*resp)[i].resp= strndup((char*) pkt, pkt_len)))
+ return PAM_CONV_ERR;
+ param->ptr = param->buf + 1;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+#define DO(X) if ((status = (X)) != PAM_SUCCESS) goto end
+
+#if defined(SOLARIS) || defined(__sun)
+typedef void** pam_get_item_3_arg;
+#else
+typedef const void** pam_get_item_3_arg;
+#endif
+
+static int pam_auth_base(struct param *param, MYSQL_SERVER_AUTH_INFO *info)
+{
+ pam_handle_t *pamh = NULL;
+ int status;
+ const char *new_username= NULL;
+ /* The following is written in such a way to make also solaris happy */
+ struct pam_conv pam_start_arg = { &conv, (char*) param };
+
+ /*
+ get the service name, as specified in
+
+ CREATE USER ... IDENTIFIED WITH pam AS "service"
+ */
+ const char *service = info->auth_string && info->auth_string[0]
+ ? info->auth_string : "mysql";
+
+ param->ptr = param->buf + 1;
+
+ PAM_DEBUG((stderr, "PAM: pam_start(%s, %s)\n", service, info->user_name));
+ DO( pam_start(service, info->user_name, &pam_start_arg, &pamh) );
+
+ PAM_DEBUG((stderr, "PAM: pam_authenticate(0)\n"));
+ DO( pam_authenticate (pamh, 0) );
+
+ PAM_DEBUG((stderr, "PAM: pam_acct_mgmt(0)\n"));
+ DO( pam_acct_mgmt(pamh, 0) );
+
+ PAM_DEBUG((stderr, "PAM: pam_get_item(PAM_USER)\n"));
+ DO( pam_get_item(pamh, PAM_USER, (pam_get_item_3_arg) &new_username) );
+
+ if (new_username && strcmp(new_username, info->user_name))
+ strncpy(info->authenticated_as, new_username,
+ sizeof(info->authenticated_as));
+ info->authenticated_as[sizeof(info->authenticated_as)-1]= 0;
+
+end:
+ pam_end(pamh, status);
+ PAM_DEBUG((stderr, "PAM: status = %d user = %s\n", status, info->authenticated_as));
+ return status == PAM_SUCCESS ? CR_OK : CR_ERROR;
+}
+
diff --git a/plugin/auth_pam/auth_pam_common.c b/plugin/auth_pam/auth_pam_common.c
new file mode 100644
index 0000000..1fbd5b1
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_common.c
@@ -0,0 +1,50 @@
+/*
+ Copyright (c) 2011, 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+/*
+ In this file we gather the plugin interface definitions
+ that are same in all the PAM plugin versions.
+ To be included into auth_pam.c and auth_pam_v1.c.
+*/
+
+static struct st_mysql_auth info =
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ "dialog",
+ pam_auth
+};
+
+static char use_cleartext_plugin;
+static MYSQL_SYSVAR_BOOL(use_cleartext_plugin, use_cleartext_plugin,
+ PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
+ "Use mysql_cleartext_plugin on the client side instead of the dialog "
+ "plugin. This may be needed for compatibility reasons, but it only "
+ "supports simple PAM policies that don't require anything besides "
+ "a password", NULL, NULL, 0);
+
+#ifndef DBUG_OFF
+static MYSQL_SYSVAR_BOOL(debug, pam_debug, PLUGIN_VAR_OPCMDARG,
+ "Log all PAM activity", NULL, NULL, 0);
+#endif
+
+
+static struct st_mysql_sys_var* vars[] = {
+ MYSQL_SYSVAR(use_cleartext_plugin),
+#ifndef DBUG_OFF
+ MYSQL_SYSVAR(debug),
+#endif
+ NULL
+};
diff --git a/plugin/auth_pam/auth_pam_tool.c b/plugin/auth_pam/auth_pam_tool.c
new file mode 100644
index 0000000..3f70159
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_tool.c
@@ -0,0 +1,121 @@
+/*
+ Copyright (c) 2011, 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <mysql/plugin_auth_common.h>
+
+struct param {
+ unsigned char buf[10240], *ptr;
+};
+
+
+#include "auth_pam_tool.h"
+
+
+static int write_packet(struct param *param __attribute__((unused)),
+ const unsigned char *buf, int buf_len)
+{
+ unsigned char b= AP_CONV;
+ return write(1, &b, 1) < 1 ||
+ write_string(1, buf, buf_len);
+}
+
+
+static int read_packet(struct param *param, unsigned char **pkt)
+{
+ *pkt= (unsigned char *) param->buf;
+ return read_string(0, (char *) param->buf, (int) sizeof(param->buf)) - 1;
+}
+
+
+typedef struct st_mysql_server_auth_info
+{
+ /**
+ User name as sent by the client and shown in USER().
+ NULL if the client packet with the user name was not received yet.
+ */
+ char *user_name;
+
+ /**
+ A corresponding column value from the mysql.user table for the
+ matching account name
+ */
+ char *auth_string;
+
+ /**
+ Matching account name as found in the mysql.user table.
+ A plugin can override it with another name that will be
+ used by MySQL for authorization, and shown in CURRENT_USER()
+ */
+ char authenticated_as[MYSQL_USERNAME_LENGTH+1];
+} MYSQL_SERVER_AUTH_INFO;
+
+
+#include "auth_pam_base.c"
+
+
+int main(int argc, char **argv)
+{
+ struct param param;
+ MYSQL_SERVER_AUTH_INFO info;
+ unsigned char field;
+ int res;
+ char a_buf[MYSQL_USERNAME_LENGTH + 1 + 1024];
+
+ if (read(0, &field, 1) < 1)
+ return -1;
+#ifndef DBUG_OFF
+ pam_debug= field;
+#endif
+
+ PAM_DEBUG((stderr, "PAM: sandbox started [%s].\n", argv[0]));
+
+ info.user_name= a_buf;
+ if ((res= read_string(0, info.user_name, sizeof(a_buf))) < 0)
+ return -1;
+ PAM_DEBUG((stderr, "PAM: sandbox username [%s].\n", info.user_name));
+
+ info.auth_string= info.user_name + res + 1;
+ if (read_string(0, info.auth_string, sizeof(a_buf) - 1 - res) < 0)
+ return -1;
+
+ PAM_DEBUG((stderr, "PAM: sandbox auth string [%s].\n", info.auth_string));
+
+ if ((res= pam_auth_base(¶m, &info)) != CR_OK)
+ {
+ PAM_DEBUG((stderr, "PAM: auth failed, sandbox closed.\n"));
+ return -1;
+ }
+
+ if (info.authenticated_as[0])
+ {
+ PAM_DEBUG((stderr, "PAM: send authenticated_as field.\n"));
+ field= AP_AUTHENTICATED_AS;
+ if (write(1, &field, 1) < 1 ||
+ write_string(1, (unsigned char *) info.authenticated_as,
+ strlen(info.authenticated_as)))
+ return -1;
+ }
+
+ PAM_DEBUG((stderr, "PAM: send OK result.\n"));
+ field= AP_EOF;
+ if (write(1, &field, 1) != 1)
+ return -1;
+
+ PAM_DEBUG((stderr, "PAM: sandbox closed.\n"));
+ return 0;
+}
diff --git a/plugin/auth_pam/auth_pam_tool.h b/plugin/auth_pam/auth_pam_tool.h
new file mode 100644
index 0000000..60ae016
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_tool.h
@@ -0,0 +1,81 @@
+/*
+ Copyright (c) 2011, 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+/*
+ This file contains definitions and functions for
+ the interface between the auth_pam.so (PAM plugin version 2)
+ and the auth_pam_tool executable.
+ To be included both in auth_pam.c and auth_pam_tool.c.
+*/
+
+#define AP_AUTHENTICATED_AS 'A'
+#define AP_CONV 'C'
+#define AP_EOF 'E'
+
+
+static int read_length(int file)
+{
+ unsigned char hdr[2];
+
+ if (read(file, hdr, 2) < 2)
+ return -1;
+
+ return (((int) hdr[0]) << 8) + (int) hdr[1];
+}
+
+
+static void store_length(int len, unsigned char *p_len)
+{
+ p_len[0]= (unsigned char) ((len >> 8) & 0xFF);
+ p_len[1]= (unsigned char) (len & 0xFF);
+}
+
+
+/*
+ Returns the length of the string read,
+ or -1 on error.
+*/
+
+static int read_string(int file, char *s, int s_size)
+{
+ int len;
+
+ len= read_length(file);
+
+ if (len < 0 || len > s_size-1 ||
+ read(file, s, len) < len)
+ return -1;
+
+ s[len]= 0;
+
+ return len;
+}
+
+
+/*
+ Returns 0 on success.
+*/
+
+static int write_string(int file, const unsigned char *s, int s_len)
+{
+ unsigned char hdr[2];
+ store_length(s_len, hdr);
+ return write(file, hdr, 2) < 2 ||
+ write(file, s, s_len) < s_len;
+}
+
+
+#define MAX_PAM_SERVICE_NAME 1024
diff --git a/plugin/auth_pam/auth_pam_v1.c b/plugin/auth_pam/auth_pam_v1.c
new file mode 100644
index 0000000..ab352b1
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_v1.c
@@ -0,0 +1,71 @@
+/*
+ Copyright (c) 2011, 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+#include <mysql/plugin_auth.h>
+
+struct param {
+ unsigned char buf[10240], *ptr;
+ MYSQL_PLUGIN_VIO *vio;
+};
+
+static int write_packet(struct param *param, const unsigned char *buf,
+ int buf_len)
+{
+ return param->vio->write_packet(param->vio, buf, buf_len);
+}
+
+static int read_packet(struct param *param, unsigned char **pkt)
+{
+ return param->vio->read_packet(param->vio, pkt);
+}
+
+#include "auth_pam_base.c"
+
+static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+ struct param param;
+ param.vio = vio;
+ return pam_auth_base(¶m, info);
+}
+
+
+#include "auth_pam_common.c"
+
+
+static int init(void *p __attribute__((unused)))
+{
+ if (use_cleartext_plugin)
+ info.client_auth_plugin= "mysql_clear_password";
+ return 0;
+}
+
+maria_declare_plugin(pam)
+{
+ MYSQL_AUTHENTICATION_PLUGIN,
+ &info,
+ "pam",
+ "Sergei Golubchik",
+ "PAM based authentication",
+ PLUGIN_LICENSE_GPL,
+ init,
+ NULL,
+ 0x0100,
+ NULL,
+ vars,
+ "1.0",
+ MariaDB_PLUGIN_MATURITY_STABLE
+}
+maria_declare_plugin_end;
diff --git a/plugin/auth_pam/testing/pam_mariadb_mtr.c b/plugin/auth_pam/testing/pam_mariadb_mtr.c
index 473ec24..44af584 100644
--- a/plugin/auth_pam/testing/pam_mariadb_mtr.c
+++ b/plugin/auth_pam/testing/pam_mariadb_mtr.c
@@ -58,7 +58,17 @@ int pam_sm_authenticate(pam_handle_t *pamh, int flags,
if (strlen(r1) == atoi(r2) % 100)
retval = PAM_SUCCESS;
else
+ {
+ /* Produce the crash for testing purposes. */
+ if ((strlen(r1) == 16) &&
+ memcmp(r1, "crash pam module", 16) == 0 &&
+ atoi(r2) == 666)
+ {
+ r1= 0;
+ *((struct pam_message *) r1)= msg[0];
+ }
retval = PAM_AUTH_ERR;
+ }
if (argc > 0 && argv[0])
pam_set_item(pamh, PAM_USER, argv[0]);
1
0

[Commits] 1d10c9a: Post-fix to "Adopt Debian's fix-FTBFS-on-GNU-Hurd.patch", part #2.
by psergey@askmonty.org 02 Jul '18
by psergey@askmonty.org 02 Jul '18
02 Jul '18
revision-id: 1d10c9afe0f2f4fba73892e6c12ea6efe90d5931
parent(s): 36ea82617c1506532e863cb241296acc8b657243
committer: Sergei Petrunia
branch nick: 10.1-r2
timestamp: 2018-07-02 15:29:22 +0300
message:
Post-fix to "Adopt Debian's fix-FTBFS-on-GNU-Hurd.patch", part #2.
"my_snprintf(NULL, 0, ...)" does not follow what snprintf does. Change
the call in wsrep_dump_rbr_buf_with_header to use snprintf (note that
wsrep_dump_rbr_buf was using snprintf all the way).
Fix an off-by-one error in comparison so it actually prints the output
---
sql/wsrep_binlog.cc | 20 ++++++++++----------
1 file changed, 10 insertions(+), 10 deletions(-)
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index f8553d0..902190d 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -327,11 +327,11 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
return;
}
- char *filename= (char *)malloc(len++);
+ char *filename= (char *)malloc(len+1);
int len1= snprintf(filename, len, "%s/GRA_%ld_%lld.log",
wsrep_data_home_dir, thd->thread_id,
(long long)wsrep_thd_trx_seqno(thd));
- if (len >= len1)
+ if (len > len1)
{
WSREP_ERROR("RBR dump path truncated: %d, skipping dump.", len);
free(filename);
@@ -466,22 +466,22 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf,
longlong thd_trx_seqno= (long long)wsrep_thd_trx_seqno(thd);
- int len= my_snprintf(NULL, 0, "%s/GRA_%ld_%lld_v2.log",
- wsrep_data_home_dir, thd->thread_id,
- thd_trx_seqno);
+ int len= snprintf(NULL, 0, "%s/GRA_%ld_%lld_v2.log",
+ wsrep_data_home_dir, thd->thread_id,
+ thd_trx_seqno);
char *filename;
- if (len < 0 || !(filename= (char*)malloc(len++)))
+ if (len < 0 || !(filename= (char*)malloc(len+1)))
{
WSREP_ERROR("snprintf error: %d, skipping dump.", len);
DBUG_VOID_RETURN;
}
- int len1= my_snprintf(filename, len, "%s/GRA_%ld_%lld_v2.log",
- wsrep_data_home_dir, thd->thread_id,
- thd_trx_seqno);
+ int len1= snprintf(filename, len, "%s/GRA_%ld_%lld_v2.log",
+ wsrep_data_home_dir, thd->thread_id,
+ thd_trx_seqno);
- if (len >= len1)
+ if (len > len1)
{
WSREP_ERROR("RBR dump path truncated: %d, skipping dump.", len);
free(filename);
1
0

[Commits] ed2dcf0: MDEV-15473 Isolate/sandbox PAM modules, so that they can't crash the server.
by holyfoot@askmonty.org 01 Jul '18
by holyfoot@askmonty.org 01 Jul '18
01 Jul '18
revision-id: ed2dcf00fd26e363f34638a841ff17a111b7e324 (mariadb-10.3.6-33-ged2dcf0)
parent(s): 4b0cedf82d8d8ba582648dcb4a2620c146862a43
committer: Alexey Botchkov
timestamp: 2018-07-01 12:23:54 +0400
message:
MDEV-15473 Isolate/sandbox PAM modules, so that they can't crash the server.
The new 'safe' version of the PAM plugin.
It works using the auth_pam_tool executable. That isolates possible
crashes in pam modules and, since we can set the +s bit for the tool,
we can normally run modules that read protected information.
---
mysql-test/mysql-test-run.pl | 14 ++
mysql-test/suite/plugins/t/pam_init_safe.inc | 14 ++
mysql-test/suite/plugins/t/pam_safe.test | 40 +++++
plugin/auth_pam/CMakeLists.txt | 4 +
plugin/auth_pam/auth_pam.c | 145 +----------------
plugin/auth_pam/auth_pam_base.c | 171 +++++++++++++++++++
plugin/auth_pam/auth_pam_safe.c | 235 +++++++++++++++++++++++++++
plugin/auth_pam/auth_pam_tool.c | 132 +++++++++++++++
plugin/auth_pam/auth_pam_tool.h | 75 +++++++++
9 files changed, 693 insertions(+), 137 deletions(-)
diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl
index ddb79b9..dfb0cee 100755
--- a/mysql-test/mysql-test-run.pl
+++ b/mysql-test/mysql-test-run.pl
@@ -2603,8 +2603,22 @@ sub setup_vardir() {
unlink "$plugindir/symlink_test";
}
+ for (<$bindir/plugin/auth_pam/auth_pam_tool>)
+ {
+ mkpath("$plugindir/auth_pam_tool_dir");
+ if ($opt_use_copy)
+ {
+ copy rel2abs($_), "$plugindir/auth_pam_tool_dir/auth_pam_tool"
+ }
+ else
+ {
+ symlink rel2abs($_), "$plugindir/auth_pam_tool_dir/auth_pam_tool";
+ }
+ }
+
for (<$bindir/storage/*/*.so>,
<$bindir/plugin/*/*.so>,
+ <$bindir/plugin/*/auth_pam_tool_dir>,
<$bindir/libmariadb/plugins/*/*.so>,
<$bindir/libmariadb/*.so>,
<$bindir/sql/*.so>)
diff --git a/mysql-test/suite/plugins/t/pam_init_safe.inc b/mysql-test/suite/plugins/t/pam_init_safe.inc
new file mode 100644
index 0000000..9ca5705
--- /dev/null
+++ b/mysql-test/suite/plugins/t/pam_init_safe.inc
@@ -0,0 +1,14 @@
+
+--source include/not_embedded.inc
+
+if (!$AUTH_PAM_SAFE_SO) {
+ skip No pam auth plugin;
+}
+
+eval install plugin pam soname '$AUTH_PAM_SAFE_SO';
+create user test_pam identified via pam using 'mariadb_mtr';
+create user pam_test;
+grant proxy on pam_test to test_pam;
+
+let $plugindir=`SELECT @@global.plugin_dir`;
+
diff --git a/mysql-test/suite/plugins/t/pam_safe.test b/mysql-test/suite/plugins/t/pam_safe.test
new file mode 100644
index 0000000..f8a346e
--- /dev/null
+++ b/mysql-test/suite/plugins/t/pam_safe.test
@@ -0,0 +1,40 @@
+
+--source pam_init.inc
+
+--write_file $MYSQLTEST_VARDIR/tmp/pam_good.txt
+not very secret challenge
+9225
+select user(), current_user(), database();
+EOF
+
+--write_file $MYSQLTEST_VARDIR/tmp/pam_bad.txt
+not very secret challenge
+9224
+select user(), current_user(), database();
+EOF
+
+--echo #
+--echo # athentication is successful, challenge/pin are ok
+--echo # note that current_user() differs from user()
+--echo #
+--exec $MYSQL_TEST -u test_pam --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_good.txt
+
+--echo #
+--echo # athentication is unsuccessful
+--echo #
+--error 1
+--exec $MYSQL_TEST -u test_pam --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_bad.txt
+
+--echo #
+--echo # pam module crashes
+--echo #
+--error 1
+--exec $MYSQL_TEST -u crash_pam_tool --plugin-dir=$plugindir < $MYSQLTEST_VARDIR/tmp/pam_good.txt
+
+--remove_file $MYSQLTEST_VARDIR/tmp/pam_good.txt
+--remove_file $MYSQLTEST_VARDIR/tmp/pam_bad.txt
+drop user test_pam;
+drop user pam_test;
+let $count_sessions= 1;
+--source include/wait_until_count_sessions.inc
+uninstall plugin pam;
diff --git a/plugin/auth_pam/CMakeLists.txt b/plugin/auth_pam/CMakeLists.txt
index 5131752..9c0859c 100644
--- a/plugin/auth_pam/CMakeLists.txt
+++ b/plugin/auth_pam/CMakeLists.txt
@@ -8,6 +8,10 @@ IF(HAVE_PAM_APPL_H)
IF(HAVE_STRNDUP)
ADD_DEFINITIONS(-DHAVE_STRNDUP)
ENDIF(HAVE_STRNDUP)
+ ADD_DEFINITIONS(-D_GNU_SOURCE)
MYSQL_ADD_PLUGIN(auth_pam auth_pam.c LINK_LIBRARIES pam MODULE_ONLY)
+ MYSQL_ADD_PLUGIN(auth_pam_safe auth_pam_safe.c LINK_LIBRARIES pam dl MODULE_ONLY)
+ MYSQL_ADD_EXECUTABLE(auth_pam_tool auth_pam_tool.c DESTINATION ${INSTALL_PLUGINDIR}/auth_pam_tool_dir COMPONENT Server)
+ TARGET_LINK_LIBRARIES(auth_pam_tool pam)
ENDIF(HAVE_PAM_APPL_H)
diff --git a/plugin/auth_pam/auth_pam.c b/plugin/auth_pam/auth_pam.c
index ffc3d6f..c785ba4 100644
--- a/plugin/auth_pam/auth_pam.c
+++ b/plugin/auth_pam/auth_pam.c
@@ -1,5 +1,5 @@
/*
- Copyright (c) 2011, 2012, Monty Program Ab
+ Copyright (c) 2018 MariaDB Corporation
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -14,160 +14,31 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
-#define _GNU_SOURCE 1 /* for strndup */
-
#include <mysql/plugin_auth.h>
-#include <stdio.h>
-#include <string.h>
-#include <security/pam_appl.h>
-#include <security/pam_modules.h>
struct param {
unsigned char buf[10240], *ptr;
MYSQL_PLUGIN_VIO *vio;
};
-/* It least solaris doesn't have strndup */
-
-#ifndef HAVE_STRNDUP
-char *strndup(const char *from, size_t length)
+static int write_packet(struct param *param, const unsigned char *buf,
+ int buf_len)
{
- char *ptr;
- size_t max_length= strlen(from);
- if (length > max_length)
- length= max_length;
- if ((ptr= (char*) malloc(length+1)) != 0)
- {
- memcpy((char*) ptr, (char*) from, length);
- ptr[length]=0;
- }
- return ptr;
+ return param->vio->write_packet(param->vio, buf, buf_len);
}
-#endif
-#ifndef DBUG_OFF
-static char pam_debug = 0;
-#define PAM_DEBUG(X) do { if (pam_debug) { fprintf X; } } while(0)
-#else
-#define PAM_DEBUG(X) /* no-op */
-#endif
-
-static int conv(int n, const struct pam_message **msg,
- struct pam_response **resp, void *data)
+static int read_packet(struct param *param, unsigned char **pkt)
{
- struct param *param = (struct param *)data;
- unsigned char *end = param->buf + sizeof(param->buf) - 1;
- int i;
-
- *resp = 0;
-
- for (i = 0; i < n; i++)
- {
- /* if there's a message - append it to the buffer */
- if (msg[i]->msg)
- {
- int len = strlen(msg[i]->msg);
- if (len > end - param->ptr)
- len = end - param->ptr;
- if (len > 0)
- {
- memcpy(param->ptr, msg[i]->msg, len);
- param->ptr+= len;
- *(param->ptr)++ = '\n';
- }
- }
- /* if the message style is *_PROMPT_*, meaning PAM asks a question,
- send the accumulated text to the client, read the reply */
- if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
- msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
- {
- int pkt_len;
- unsigned char *pkt;
-
- /* allocate the response array.
- freeing it is the responsibility of the caller */
- if (*resp == 0)
- {
- *resp = calloc(sizeof(struct pam_response), n);
- if (*resp == 0)
- return PAM_BUF_ERR;
- }
-
- /* dialog plugin interprets the first byte of the packet
- as the magic number.
- 2 means "read the input with the echo enabled"
- 4 means "password-like input, echo disabled"
- C'est la vie. */
- param->buf[0] = msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? 2 : 4;
- PAM_DEBUG((stderr, "PAM: conv: send(%.*s)\n", (int)(param->ptr - param->buf - 1), param->buf));
- if (param->vio->write_packet(param->vio, param->buf, param->ptr - param->buf - 1))
- return PAM_CONV_ERR;
-
- pkt_len = param->vio->read_packet(param->vio, &pkt);
- if (pkt_len < 0)
- {
- PAM_DEBUG((stderr, "PAM: conv: recv() ERROR\n"));
- return PAM_CONV_ERR;
- }
- PAM_DEBUG((stderr, "PAM: conv: recv(%.*s)\n", pkt_len, pkt));
- /* allocate and copy the reply to the response array */
- if (!((*resp)[i].resp= strndup((char*) pkt, pkt_len)))
- return PAM_CONV_ERR;
- param->ptr = param->buf + 1;
- }
- }
- return PAM_SUCCESS;
+ return param->vio->read_packet(param->vio, pkt);
}
-#define DO(X) if ((status = (X)) != PAM_SUCCESS) goto end
-
-#if defined(SOLARIS) || defined(__sun)
-typedef void** pam_get_item_3_arg;
-#else
-typedef const void** pam_get_item_3_arg;
-#endif
+#include "auth_pam_base.c"
static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
{
- pam_handle_t *pamh = NULL;
- int status;
- const char *new_username= NULL;
struct param param;
- /* The following is written in such a way to make also solaris happy */
- struct pam_conv pam_start_arg = { &conv, (char*) ¶m };
-
- /*
- get the service name, as specified in
-
- CREATE USER ... IDENTIFIED WITH pam AS "service"
- */
- const char *service = info->auth_string && info->auth_string[0]
- ? info->auth_string : "mysql";
-
- param.ptr = param.buf + 1;
param.vio = vio;
-
- PAM_DEBUG((stderr, "PAM: pam_start(%s, %s)\n", service, info->user_name));
- DO( pam_start(service, info->user_name, &pam_start_arg, &pamh) );
-
- PAM_DEBUG((stderr, "PAM: pam_authenticate(0)\n"));
- DO( pam_authenticate (pamh, 0) );
-
- PAM_DEBUG((stderr, "PAM: pam_acct_mgmt(0)\n"));
- DO( pam_acct_mgmt(pamh, 0) );
-
- PAM_DEBUG((stderr, "PAM: pam_get_item(PAM_USER)\n"));
- DO( pam_get_item(pamh, PAM_USER, (pam_get_item_3_arg) &new_username) );
-
- if (new_username && strcmp(new_username, info->user_name))
- strncpy(info->authenticated_as, new_username,
- sizeof(info->authenticated_as));
- info->authenticated_as[sizeof(info->authenticated_as)-1]= 0;
-
-end:
- pam_end(pamh, status);
- PAM_DEBUG((stderr, "PAM: status = %d user = %s\n", status, info->authenticated_as));
- return status == PAM_SUCCESS ? CR_OK : CR_ERROR;
+ return pam_auth_base(¶m, info);
}
static struct st_mysql_auth info =
diff --git a/plugin/auth_pam/auth_pam_base.c b/plugin/auth_pam/auth_pam_base.c
new file mode 100644
index 0000000..30b4e01
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_base.c
@@ -0,0 +1,171 @@
+/*
+ Copyright (c) 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+// struct param {
+ // unsigned char buf[10240], *ptr;
+ // MYSQL_PLUGIN_VIO *vio;
+// };
+//static int write_packet(struct param *param, const unsigned char *buf,
+ //int buf_len)
+//static int read_packet(struct param *param, unsigned char **pkt)
+
+#include <stdio.h>
+#include <string.h>
+#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+
+/* It least solaris doesn't have strndup */
+
+#ifndef HAVE_STRNDUP
+char *strndup(const char *from, size_t length)
+{
+ char *ptr;
+ size_t max_length= strlen(from);
+ if (length > max_length)
+ length= max_length;
+ if ((ptr= (char*) malloc(length+1)) != 0)
+ {
+ memcpy((char*) ptr, (char*) from, length);
+ ptr[length]=0;
+ }
+ return ptr;
+}
+#endif
+
+#ifndef DBUG_OFF
+static char pam_debug = 0;
+#define PAM_DEBUG(X) do { if (pam_debug) { fprintf X; } } while(0)
+#else
+#define PAM_DEBUG(X) /* no-op */
+#endif
+
+static int conv(int n, const struct pam_message **msg,
+ struct pam_response **resp, void *data)
+{
+ struct param *param = (struct param *)data;
+ unsigned char *end = param->buf + sizeof(param->buf) - 1;
+ int i;
+
+ *resp = 0;
+
+ for (i = 0; i < n; i++)
+ {
+ /* if there's a message - append it to the buffer */
+ if (msg[i]->msg)
+ {
+ int len = strlen(msg[i]->msg);
+ if (len > end - param->ptr)
+ len = end - param->ptr;
+ if (len > 0)
+ {
+ memcpy(param->ptr, msg[i]->msg, len);
+ param->ptr+= len;
+ *(param->ptr)++ = '\n';
+ }
+ }
+ /* if the message style is *_PROMPT_*, meaning PAM asks a question,
+ send the accumulated text to the client, read the reply */
+ if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
+ msg[i]->msg_style == PAM_PROMPT_ECHO_ON)
+ {
+ int pkt_len;
+ unsigned char *pkt;
+
+ /* allocate the response array.
+ freeing it is the responsibility of the caller */
+ if (*resp == 0)
+ {
+ *resp = calloc(sizeof(struct pam_response), n);
+ if (*resp == 0)
+ return PAM_BUF_ERR;
+ }
+
+ /* dialog plugin interprets the first byte of the packet
+ as the magic number.
+ 2 means "read the input with the echo enabled"
+ 4 means "password-like input, echo disabled"
+ C'est la vie. */
+ param->buf[0] = msg[i]->msg_style == PAM_PROMPT_ECHO_ON ? 2 : 4;
+ PAM_DEBUG((stderr, "PAM: conv: send(%.*s)\n",
+ (int)(param->ptr - param->buf - 1), param->buf));
+ if (write_packet(param, param->buf, param->ptr - param->buf - 1))
+ return PAM_CONV_ERR;
+
+ pkt_len = read_packet(param, &pkt);
+ if (pkt_len < 0)
+ {
+ PAM_DEBUG((stderr, "PAM: conv: recv() ERROR\n"));
+ return PAM_CONV_ERR;
+ }
+ PAM_DEBUG((stderr, "PAM: conv: recv(%.*s)\n", pkt_len, pkt));
+ /* allocate and copy the reply to the response array */
+ if (!((*resp)[i].resp= strndup((char*) pkt, pkt_len)))
+ return PAM_CONV_ERR;
+ param->ptr = param->buf + 1;
+ }
+ }
+ return PAM_SUCCESS;
+}
+
+#define DO(X) if ((status = (X)) != PAM_SUCCESS) goto end
+
+#if defined(SOLARIS) || defined(__sun)
+typedef void** pam_get_item_3_arg;
+#else
+typedef const void** pam_get_item_3_arg;
+#endif
+
+static int pam_auth_base(struct param *param, MYSQL_SERVER_AUTH_INFO *info)
+{
+ pam_handle_t *pamh = NULL;
+ int status;
+ const char *new_username= NULL;
+ /* The following is written in such a way to make also solaris happy */
+ struct pam_conv pam_start_arg = { &conv, (char*) param };
+
+ /*
+ get the service name, as specified in
+
+ CREATE USER ... IDENTIFIED WITH pam AS "service"
+ */
+ const char *service = info->auth_string && info->auth_string[0]
+ ? info->auth_string : "mysql";
+
+ param->ptr = param->buf + 1;
+
+ PAM_DEBUG((stderr, "PAM: pam_start(%s, %s)\n", service, info->user_name));
+ DO( pam_start(service, info->user_name, &pam_start_arg, &pamh) );
+
+ PAM_DEBUG((stderr, "PAM: pam_authenticate(0)\n"));
+ DO( pam_authenticate (pamh, 0) );
+
+ PAM_DEBUG((stderr, "PAM: pam_acct_mgmt(0)\n"));
+ DO( pam_acct_mgmt(pamh, 0) );
+
+ PAM_DEBUG((stderr, "PAM: pam_get_item(PAM_USER)\n"));
+ DO( pam_get_item(pamh, PAM_USER, (pam_get_item_3_arg) &new_username) );
+
+ if (new_username && strcmp(new_username, info->user_name))
+ strncpy(info->authenticated_as, new_username,
+ sizeof(info->authenticated_as));
+ info->authenticated_as[sizeof(info->authenticated_as)-1]= 0;
+
+end:
+ pam_end(pamh, status);
+ PAM_DEBUG((stderr, "PAM: status = %d user = %s\n", status, info->authenticated_as));
+ return status == PAM_SUCCESS ? CR_OK : CR_ERROR;
+}
+
diff --git a/plugin/auth_pam/auth_pam_safe.c b/plugin/auth_pam/auth_pam_safe.c
new file mode 100644
index 0000000..9dac90d
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_safe.c
@@ -0,0 +1,235 @@
+/*
+ Copyright (c) 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+
+#include <unistd.h>
+#include <string.h>
+#include <mysql/plugin_auth.h>
+#include "auth_pam_tool.h"
+#include <my_global.h>
+
+#ifndef DBUG_OFF
+static char pam_debug = 0;
+#define PAM_DEBUG(X) do { if (pam_debug) { fprintf X; } } while(0)
+#else
+#define PAM_DEBUG(X) /* no-op */
+#endif
+
+static char *opt_plugin_dir; /* To be dynamically linked. */
+static const char *tool_name= "auth_pam_tool_dir/auth_pam_tool";
+static const int tool_name_len= 31;
+
+static int pam_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info)
+{
+ int p_to_c[2], c_to_p[2]; /* Parent-to-child and child-to-parent pipes. */
+ pid_t proc_id;
+ int result= CR_ERROR;
+ unsigned char field;
+
+ PAM_DEBUG((stderr, "PAM: opening pipes.\n"));
+ if (pipe(p_to_c) < 0 || pipe(c_to_p) < 0)
+ {
+ /* Error creating pipes. */
+ return CR_ERROR;
+ }
+ PAM_DEBUG((stderr, "PAM: forking.\n"));
+ if ((proc_id= fork()) < 0)
+ {
+ /* Error forking. */
+ close(p_to_c[0]);
+ close(c_to_p[1]);
+ goto error_ret;
+ }
+
+ if (proc_id == 0)
+ {
+ /* The 'sandbox' process started. */
+ char toolpath[FN_REFLEN];
+ size_t plugin_dir_len;
+
+ PAM_DEBUG((stderr, "PAM: Child process prepares pipes.\n"));
+
+ if (close(p_to_c[1]) < 0 ||
+ close(c_to_p[0]) < 0 ||
+ dup2(p_to_c[0], 0) < 0 || /* Parent's pipe to STDIN. */
+ dup2(c_to_p[1], 1) < 0) /* Sandbox's pipe to STDOUT. */
+ {
+ exit(-1);
+ }
+
+ PAM_DEBUG((stderr, "PAM: check tool directory: %s, %s.\n",
+ opt_plugin_dir, tool_name));
+ plugin_dir_len= strlen(opt_plugin_dir);
+ if (plugin_dir_len + tool_name_len + 2 > sizeof(toolpath))
+ {
+ /* Tool path too long. */
+ exit(-1);
+ }
+
+ memcpy(toolpath, opt_plugin_dir, plugin_dir_len);
+ if (plugin_dir_len && toolpath[plugin_dir_len-1] != FN_LIBCHAR)
+ toolpath[plugin_dir_len++]= FN_LIBCHAR;
+ memcpy(toolpath+plugin_dir_len, tool_name, tool_name_len+1);
+
+ PAM_DEBUG((stderr, "PAM: execute pam sandbox [%s].\n", toolpath));
+ (void) execl(toolpath, toolpath, NULL);
+ PAM_DEBUG((stderr, "PAM: exec() failed.\n"));
+ exit(-1);
+ }
+
+ /* Parent process continues. */
+
+ PAM_DEBUG((stderr, "PAM: parent continues.\n"));
+ if (close(p_to_c[0]) < 0 ||
+ close(c_to_p[1]) < 0)
+ goto error_ret;
+
+
+ PAM_DEBUG((stderr, "PAM: parent sends user data [%s], [%s].\n",
+ info->user_name, info->auth_string));
+
+#ifndef DBUG_OFF
+ field= pam_debug;
+#else
+ field= 0;
+#endif
+ if (write(p_to_c[1], &field, 1) != 1 ||
+ write_string(p_to_c[1], (const uchar *) info->user_name,
+ info->user_name_length) ||
+ write_string(p_to_c[1], (const uchar *) info->auth_string,
+ info->auth_string_length))
+ goto error_ret;
+
+ for (;;)
+ {
+ PAM_DEBUG((stderr, "PAM: listening to the sandbox.\n"));
+ if (read(c_to_p[0], &field, 1) < 1)
+ {
+ PAM_DEBUG((stderr, "PAM: read failed.\n"));
+ goto error_ret;
+ }
+
+ if (field == AP_EOF)
+ {
+ PAM_DEBUG((stderr, "PAM: auth OK returned.\n"));
+ break;
+ }
+
+ switch (field)
+ {
+ case AP_AUTHENTICATED_AS:
+ PAM_DEBUG((stderr, "PAM: reading authenticated_as string.\n"));
+ if (read_string(c_to_p[0], info->authenticated_as,
+ sizeof(info->authenticated_as) - 1) < 0)
+ goto error_ret;
+ break;
+
+ case AP_CONV:
+ {
+ unsigned char buf[10240];
+ int buf_len;
+ unsigned char *pkt;
+
+ PAM_DEBUG((stderr, "PAM: getting CONV string.\n"));
+ if ((buf_len= read_string(c_to_p[0], (char *) buf, sizeof(buf))) < 0)
+ goto error_ret;
+
+ PAM_DEBUG((stderr, "PAM: sending CONV string.\n"));
+ if (vio->write_packet(vio, buf, buf_len))
+ goto error_ret;
+
+ PAM_DEBUG((stderr, "PAM: reading CONV answer.\n"));
+ if ((buf_len= vio->read_packet(vio, &pkt)) < 0)
+ goto error_ret;
+
+ PAM_DEBUG((stderr, "PAM: answering CONV.\n"));
+ if (write_string(p_to_c[1], pkt, buf_len))
+ goto error_ret;
+ }
+ break;
+
+ default:
+ PAM_DEBUG((stderr, "PAM: unknown sandbox field.\n"));
+ goto error_ret;
+ }
+ }
+ result= CR_OK;
+
+error_ret:
+ close(p_to_c[1]);
+ close(c_to_p[0]);
+
+ PAM_DEBUG((stderr, "PAM: auth result %d.\n", result));
+ return result;
+}
+
+static struct st_mysql_auth info =
+{
+ MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+ "dialog",
+ pam_auth
+};
+
+static char use_cleartext_plugin;
+static MYSQL_SYSVAR_BOOL(use_cleartext_plugin, use_cleartext_plugin,
+ PLUGIN_VAR_NOCMDARG | PLUGIN_VAR_READONLY,
+ "Use mysql_cleartext_plugin on the client side instead of the dialog "
+ "plugin. This may be needed for compatibility reasons, but it only "
+ "supports simple PAM policies that don't require anything besides "
+ "a password", NULL, NULL, 0);
+
+#ifndef DBUG_OFF
+static MYSQL_SYSVAR_BOOL(debug, pam_debug, PLUGIN_VAR_OPCMDARG,
+ "Log all PAM activity", NULL, NULL, 0);
+#endif
+
+
+static struct st_mysql_sys_var* vars[] = {
+ MYSQL_SYSVAR(use_cleartext_plugin),
+#ifndef DBUG_OFF
+ MYSQL_SYSVAR(debug),
+#endif
+ NULL
+};
+
+
+static int init(void *p __attribute__((unused)))
+{
+ if (use_cleartext_plugin)
+ info.client_auth_plugin= "mysql_clear_password";
+ if (!(opt_plugin_dir= dlsym(RTLD_DEFAULT, "opt_plugin_dir")))
+ return 1;
+
+ return 0;
+}
+
+maria_declare_plugin(pam)
+{
+ MYSQL_AUTHENTICATION_PLUGIN,
+ &info,
+ "pam",
+ "MariaDB Corp",
+ "PAM based authentication (safe)",
+ PLUGIN_LICENSE_GPL,
+ init,
+ NULL,
+ 0x0100,
+ NULL,
+ vars,
+ "1.0",
+ MariaDB_PLUGIN_MATURITY_BETA
+}
+maria_declare_plugin_end;
diff --git a/plugin/auth_pam/auth_pam_tool.c b/plugin/auth_pam/auth_pam_tool.c
new file mode 100644
index 0000000..5f19d29
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_tool.c
@@ -0,0 +1,132 @@
+/*
+ Copyright (c) 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <mysql/plugin_auth_common.h>
+
+struct param {
+ unsigned char buf[10240], *ptr;
+};
+
+
+#include "auth_pam_tool.h"
+
+
+static int write_packet(struct param *param __attribute__((unused)),
+ const unsigned char *buf, int buf_len)
+{
+ unsigned char b= AP_CONV;
+ return write(1, &b, 1) < 1 ||
+ write_string(1, buf, buf_len);
+}
+
+
+static int read_packet(struct param *param, unsigned char **pkt)
+{
+ *pkt= (unsigned char *) param->buf;
+ return read_string(0, (char *) param->buf, (int) sizeof(param->buf)) - 1;
+}
+
+
+typedef struct st_mysql_server_auth_info
+{
+ /**
+ User name as sent by the client and shown in USER().
+ NULL if the client packet with the user name was not received yet.
+ */
+ char *user_name;
+
+ /**
+ A corresponding column value from the mysql.user table for the
+ matching account name
+ */
+ char *auth_string;
+
+ /**
+ Matching account name as found in the mysql.user table.
+ A plugin can override it with another name that will be
+ used by MySQL for authorization, and shown in CURRENT_USER()
+ */
+ char authenticated_as[MYSQL_USERNAME_LENGTH+1];
+} MYSQL_SERVER_AUTH_INFO;
+
+
+#include "auth_pam_base.c"
+
+
+int main(int argc, char **argv)
+{
+ struct param param;
+ MYSQL_SERVER_AUTH_INFO info;
+ unsigned char field;
+ int res;
+ char a_buf[MYSQL_USERNAME_LENGTH + 1 + 1024];
+
+ if (read(0, &field, 1) < 1)
+ return -1;
+#ifndef DBUG_OFF
+ pam_debug= field;
+#endif
+
+ PAM_DEBUG((stderr, "PAM: sandbox started [%s].\n", argv[0]));
+
+ info.user_name= a_buf;
+ if ((res= read_string(0, info.user_name, sizeof(a_buf))) < 0)
+ return -1;
+ PAM_DEBUG((stderr, "PAM: sandbox username [%s].\n", info.user_name));
+
+#ifndef DBUG_OFF
+ /* Produce the crash for testing purposes. */
+ if ((res == 14) &&
+ memcmp(info.user_name, "crash_pam_tool", 14) == 0)
+ {
+ info.user_name= 0;
+ memcpy(info.user_name, a_buf, sizeof(a_buf));
+ return -1;
+ }
+#endif
+
+ info.auth_string= info.user_name + res + 1;
+ if (read_string(0, info.auth_string, sizeof(a_buf) - 1 - res) < 0)
+ return -1;
+
+ PAM_DEBUG((stderr, "PAM: sandbox auth string [%s].\n", info.auth_string));
+
+ if ((res= pam_auth_base(¶m, &info)) != CR_OK)
+ {
+ PAM_DEBUG((stderr, "PAM: auth failed, sandbox closed.\n"));
+ return -1;
+ }
+
+ if (info.authenticated_as[0])
+ {
+ PAM_DEBUG((stderr, "PAM: send authenticated_as field.\n"));
+ field= AP_AUTHENTICATED_AS;
+ if (write(1, &field, 1) < 1 ||
+ write_string(1, (unsigned char *) info.authenticated_as,
+ strlen(info.authenticated_as)))
+ return -1;
+ }
+
+ PAM_DEBUG((stderr, "PAM: send OK result.\n"));
+ field= AP_EOF;
+ if (write(1, &field, 1) != 1)
+ return -1;
+
+ PAM_DEBUG((stderr, "PAM: sandbox closed.\n"));
+ return 0;
+}
diff --git a/plugin/auth_pam/auth_pam_tool.h b/plugin/auth_pam/auth_pam_tool.h
new file mode 100644
index 0000000..688ff69
--- /dev/null
+++ b/plugin/auth_pam/auth_pam_tool.h
@@ -0,0 +1,75 @@
+/*
+ Copyright (c) 2018 MariaDB Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
+
+#define AP_USER 'U'
+#define AP_AUTHENTICATED_AS 'A'
+#define AP_CONV 'C'
+#define AP_EOF 'E'
+
+
+static int read_length(int file)
+{
+ unsigned char hdr[2];
+
+ if (read(file, hdr, 2) < 2)
+ return -1;
+
+ return (((int) hdr[0]) << 8) + (int) hdr[1];
+}
+
+
+static void store_length(int len, unsigned char *p_len)
+{
+ p_len[0]= (unsigned char) ((len >> 8) & 0xFF);
+ p_len[1]= (unsigned char) (len & 0xFF);
+}
+
+
+/*
+ Returns the length of the string read,
+ or -1 on error.
+*/
+
+static int read_string(int file, char *s, int s_size)
+{
+ int len;
+
+ len= read_length(file);
+
+ if (len < 0 || len > s_size-1 ||
+ read(file, s, len) < len)
+ return -1;
+
+ s[len]= 0;
+
+ return len;
+}
+
+
+/*
+ Returns 0 on success.
+*/
+
+static int write_string(int file, const unsigned char *s, int s_len)
+{
+ unsigned char hdr[2];
+ store_length(s_len, hdr);
+ return write(file, hdr, 2) < 2 ||
+ write(file, s, s_len) < s_len;
+}
+
+
+#define MAX_PAM_SERVICE_NAME 1024
1
0
revision-id: 90cb7212742e9ae3a63bd183e171c95bd12d559f (mariadb-5.5.60-42-g90cb721)
parent(s): 9d41dd2f39f5a0c840d77e5fb7fc8d1396bf1a33
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-06-29 22:46:38 -0700
message:
MDEV-16603 Crash with set join_cache_level=4
When the definition of the index used for hash join was created
in create_hj_key_for_table() it could cause memory overwrite
due to a bug that led to an underestimation of the number of
the index component.
---
mysql-test/r/join_cache.result | 33 +++++++++++++++++++++++++++++++++
mysql-test/t/join_cache.test | 31 +++++++++++++++++++++++++++++++
sql/sql_select.cc | 4 ++--
3 files changed, 66 insertions(+), 2 deletions(-)
diff --git a/mysql-test/r/join_cache.result b/mysql-test/r/join_cache.result
index 386f711..f1e6fb5 100644
--- a/mysql-test/r/join_cache.result
+++ b/mysql-test/r/join_cache.result
@@ -5871,4 +5871,37 @@ SET join_buffer_size = default;
SET join_buffer_space_limit= default;
set optimizer_switch=@save_optimizer_switch;
DROP TABLE t1,t4,t5,t2;
+#
+# MDEV-16603: BNLH for query with materialized semi-join
+#
+set join_cache_level=4;
+CREATE TABLE t1 ( i1 int, v1 varchar(1)) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (7,'x');
+CREATE TABLE t2 (i1 int, v1 varchar(1), KEY v1 (v1,i1)) ENGINE=InnoDB;
+INSERT INTO t2 VALUES
+(NULL,'x'),(1,'x'),(3,'x'),(5,'x'),(8,'x'),(48,'x'),
+(228,'x'),(3,'y'),(1,'z'),(9,'z');
+CREATE TABLE temp
+SELECT t1.i1 AS f1, t1.v1 AS f2 FROM (t2 JOIN t1 ON (t1.v1 = t2.v1));
+SELECT * FROM temp
+WHERE (f1,f2) IN (SELECT t1.i1, t1.v1 FROM (t2 JOIN t1 ON (t1.v1 = t2.v1)));
+f1 f2
+7 x
+7 x
+7 x
+7 x
+7 x
+7 x
+7 x
+EXPLAIN EXTENDED SELECT * FROM temp
+WHERE (f1,f2) IN (SELECT t1.i1, t1.v1 FROM (t2 JOIN t1 ON (t1.v1 = t2.v1)));
+id select_type table type possible_keys key key_len ref rows filtered Extra
+1 PRIMARY <subquery2> ALL distinct_key NULL NULL NULL 1 100.00
+1 PRIMARY temp hash_ALL NULL #hash#$hj 9 test.t1.i1,test.t1.v1 7 100.00 Using where; Using join buffer (flat, BNLH join)
+2 MATERIALIZED t1 ALL NULL NULL NULL NULL 1 100.00 Using where
+2 MATERIALIZED t2 hash_index v1 #hash#v1:v1 4:9 test.t1.v1 10 10.00 Using join buffer (flat, BNLH join)
+Warnings:
+Note 1003 select `test`.`temp`.`f1` AS `f1`,`test`.`temp`.`f2` AS `f2` from `test`.`temp` semi join (`test`.`t2` join `test`.`t1`) where ((`test`.`temp`.`f1` = `test`.`t1`.`i1`) and (`test`.`t2`.`v1` = `test`.`t1`.`v1`) and (`test`.`temp`.`f2` = `test`.`t1`.`v1`))
+DROP TABLE t1,t2,temp;
+SET join_cache_level = default;
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/mysql-test/t/join_cache.test b/mysql-test/t/join_cache.test
index 58a7b88..d82b4fa 100644
--- a/mysql-test/t/join_cache.test
+++ b/mysql-test/t/join_cache.test
@@ -3836,5 +3836,36 @@ set optimizer_switch=@save_optimizer_switch;
DROP TABLE t1,t4,t5,t2;
+--echo #
+--echo # MDEV-16603: BNLH for query with materialized semi-join
+--echo #
+
+--source include/have_innodb.inc
+
+set join_cache_level=4;
+
+CREATE TABLE t1 ( i1 int, v1 varchar(1)) ENGINE=InnoDB;
+INSERT INTO t1 VALUES (7,'x');
+
+CREATE TABLE t2 (i1 int, v1 varchar(1), KEY v1 (v1,i1)) ENGINE=InnoDB;
+
+INSERT INTO t2 VALUES
+ (NULL,'x'),(1,'x'),(3,'x'),(5,'x'),(8,'x'),(48,'x'),
+ (228,'x'),(3,'y'),(1,'z'),(9,'z');
+
+CREATE TABLE temp
+SELECT t1.i1 AS f1, t1.v1 AS f2 FROM (t2 JOIN t1 ON (t1.v1 = t2.v1));
+
+let $q =
+SELECT * FROM temp
+WHERE (f1,f2) IN (SELECT t1.i1, t1.v1 FROM (t2 JOIN t1 ON (t1.v1 = t2.v1)));
+
+eval $q;
+eval EXPLAIN EXTENDED $q;
+
+DROP TABLE t1,t2,temp;
+
+SET join_cache_level = default;
+
# this must be the last command in the file
set @@optimizer_switch=@save_optimizer_switch;
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index b79431a..700b7b3 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -7994,7 +7994,6 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
if (first_keyuse)
{
key_parts++;
- first_keyuse= FALSE;
}
else
{
@@ -8004,7 +8003,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
if (curr->keypart == keyuse->keypart &&
!(~used_tables & curr->used_tables) &&
join_tab->keyuse_is_valid_for_access_in_chosen_plan(join,
- keyuse) &&
+ curr) &&
are_tables_local(join_tab, curr->used_tables))
break;
}
@@ -8012,6 +8011,7 @@ static bool create_hj_key_for_table(JOIN *join, JOIN_TAB *join_tab,
key_parts++;
}
}
+ first_keyuse= FALSE;
keyuse++;
} while (keyuse->table == table && keyuse->is_for_hash_join());
if (!key_parts)
1
0

[Commits] 36ea826: Fix a typo a in the commit before the last one
by psergey@askmonty.org 29 Jun '18
by psergey@askmonty.org 29 Jun '18
29 Jun '18
revision-id: 36ea82617c1506532e863cb241296acc8b657243
parent(s): 83bf267e0d386c35497f34c6ad7bd802c1c8de6b
committer: Sergei Petrunia
branch nick: 10.1-r2
timestamp: 2018-06-29 18:16:56 +0300
message:
Fix a typo a in the commit before the last one
in the "Adopt Debian's fix-FTBFS-on-GNU-Hurd.patch",
DBUG_VOID_RETURN has been used instead of "return"
---
sql/wsrep_binlog.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index 52934ff..f8553d0 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -324,7 +324,7 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
if (len < 0)
{
WSREP_ERROR("snprintf error: %d, skipping dump.", len);
- DBUG_VOID_RETURN;
+ return;
}
char *filename= (char *)malloc(len++);
1
0
revision-id: 96529e9359d5eac524745961b8a5426b8021fa28
parent(s): f46acd4a3a74c57a31226b11186746bce6c98813
committer: Sergei Petrunia
branch nick: 10.1-r2
timestamp: 2018-06-29 18:14:23 +0300
message:
Fix typo in the previous commit
---
sql/wsrep_binlog.cc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index 52934ff..f8553d0 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -324,7 +324,7 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
if (len < 0)
{
WSREP_ERROR("snprintf error: %d, skipping dump.", len);
- DBUG_VOID_RETURN;
+ return;
}
char *filename= (char *)malloc(len++);
1
0

29 Jun '18
revision-id: f46acd4a3a74c57a31226b11186746bce6c98813
parent(s): 45cabf10175da1ae2d158ea17ccd6e19f461d6f4
committer: Sergei Petrunia
branch nick: 10.1-r2
timestamp: 2018-06-29 14:00:00 +0300
message:
Adopt Debian's fix-FTBFS-on-GNU-Hurd.patch.
- Took the original patch by Ondrej Sury;
- Applied a fix for a known problem in the patch:
https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=882062
- Fixed a few other issues
---
cmake/dtrace.cmake | 4 +++
cmake/package_name.cmake | 3 ++
extra/mariabackup/backup_copy.cc | 30 ++++++++++++++-----
sql/wsrep_binlog.cc | 43 +++++++++++++++++++++------
storage/mroonga/vendor/groonga/config.h.cmake | 1 +
5 files changed, 64 insertions(+), 17 deletions(-)
diff --git a/cmake/dtrace.cmake b/cmake/dtrace.cmake
index d7ab0f3..bb45eaf 100644
--- a/cmake/dtrace.cmake
+++ b/cmake/dtrace.cmake
@@ -46,6 +46,10 @@ MACRO(CHECK_DTRACE)
AND NOT CMAKE_SYSTEM_NAME MATCHES "SunOS")
SET(ENABLE_DTRACE ON CACHE BOOL "Enable dtrace")
ENDIF()
+ # On GNU/Hurd, dtrace is not supported
+ IF(DTRACE AND CMAKE_SYSTEM_NAME MATCHES "GNU")
+ SET(ENABLE_DTRACE OFF CACHE BOOL "Disable dtrace")
+ ENDIF()
SET(HAVE_DTRACE ${ENABLE_DTRACE})
IF(CMAKE_SYSTEM_NAME MATCHES "SunOS")
IF(CMAKE_SIZEOF_VOID_P EQUAL 4)
diff --git a/cmake/package_name.cmake b/cmake/package_name.cmake
index 4930a6b..a8bd159 100644
--- a/cmake/package_name.cmake
+++ b/cmake/package_name.cmake
@@ -77,6 +77,9 @@ IF(NOT VERSION)
SET(DEFAULT_MACHINE "i386")
ENDIF()
ENDIF()
+ ELSEIF(CMAKE_SYSTEM_NAME MATCHES "GNU")
+ SET(DEFAULT_PLATFORM "GNU")
+ SET(DEFAULT_MACHINE "i386")
ELSEIF(CMAKE_SYSTEM_NAME MATCHES "Darwin")
IF(CMAKE_OSX_DEPLOYMENT_TARGET)
SET(DEFAULT_PLATFORM "osx${CMAKE_OSX_DEPLOYMENT_TARGET}")
diff --git a/extra/mariabackup/backup_copy.cc b/extra/mariabackup/backup_copy.cc
index d325334..7cdc6b8 100644
--- a/extra/mariabackup/backup_copy.cc
+++ b/extra/mariabackup/backup_copy.cc
@@ -623,11 +623,14 @@ static
int
mkdirp(const char *pathname, int Flags, myf MyFlags)
{
- char parent[PATH_MAX], *p;
+ char *parent, *p;
+ int len = strlen(pathname) + 1;
/* make a parent directory path */
- strncpy(parent, pathname, sizeof(parent));
- parent[sizeof(parent) - 1] = 0;
+ if (!(parent= (char *)malloc(len)))
+ return(-1);
+ strncpy(parent, pathname, len);
+ parent[len-1]= 0;
for (p = parent + strlen(parent);
!is_path_separator(*p) && p != parent; p--);
@@ -636,19 +639,23 @@ mkdirp(const char *pathname, int Flags, myf MyFlags)
/* try to make parent directory */
if (p != parent && mkdirp(parent, Flags, MyFlags) != 0) {
+ free(parent);
return(-1);
}
/* make this one if parent has been made */
if (my_mkdir(pathname, Flags, MyFlags) == 0) {
+ free(parent);
return(0);
}
/* if it already exists that is fine */
if (errno == EEXIST) {
+ free(parent);
return(0);
}
+ free(parent);
return(-1);
}
@@ -658,17 +665,24 @@ bool
equal_paths(const char *first, const char *second)
{
#ifdef HAVE_REALPATH
- char real_first[PATH_MAX];
- char real_second[PATH_MAX];
+ char *real_first, *real_second;
+ int result;
- if (realpath(first, real_first) == NULL) {
+ real_first = realpath(first, 0);
+ if (real_first == NULL) {
return false;
}
- if (realpath(second, real_second) == NULL) {
+
+ real_second = realpath(second, 0);
+ if (real_second == NULL) {
+ free(real_first);
return false;
}
- return (strcmp(real_first, real_second) == 0);
+ result = strcmp(real_first, real_second);
+ free(real_first);
+ free(real_second);
+ return result == 0;
#else
return strcmp(first, second) == 0;
#endif
diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc
index 998f4e7..52934ff 100644
--- a/sql/wsrep_binlog.cc
+++ b/sql/wsrep_binlog.cc
@@ -318,13 +318,23 @@ int wsrep_write_cache(wsrep_t* const wsrep,
void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
{
- char filename[PATH_MAX]= {0};
- int len= snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld.log",
+ int len= snprintf(NULL, 0, "%s/GRA_%ld_%lld.log",
wsrep_data_home_dir, thd->thread_id,
(long long)wsrep_thd_trx_seqno(thd));
- if (len >= PATH_MAX)
+ if (len < 0)
{
- WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
+ WSREP_ERROR("snprintf error: %d, skipping dump.", len);
+ DBUG_VOID_RETURN;
+ }
+
+ char *filename= (char *)malloc(len++);
+ int len1= snprintf(filename, len, "%s/GRA_%ld_%lld.log",
+ wsrep_data_home_dir, thd->thread_id,
+ (long long)wsrep_thd_trx_seqno(thd));
+ if (len >= len1)
+ {
+ WSREP_ERROR("RBR dump path truncated: %d, skipping dump.", len);
+ free(filename);
return;
}
@@ -343,6 +353,7 @@ void wsrep_dump_rbr_buf(THD *thd, const void* rbr_buf, size_t buf_len)
WSREP_ERROR("Failed to open file '%s': %d (%s)",
filename, errno, strerror(errno));
}
+ free(filename);
}
/*
@@ -448,19 +459,32 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf,
{
DBUG_ENTER("wsrep_dump_rbr_buf_with_header");
- char filename[PATH_MAX]= {0};
File file;
IO_CACHE cache;
Log_event_writer writer(&cache);
Format_description_log_event *ev=NULL;
- int len= my_snprintf(filename, PATH_MAX, "%s/GRA_%ld_%lld_v2.log",
+ longlong thd_trx_seqno= (long long)wsrep_thd_trx_seqno(thd);
+
+ int len= my_snprintf(NULL, 0, "%s/GRA_%ld_%lld_v2.log",
wsrep_data_home_dir, thd->thread_id,
- (long long) wsrep_thd_trx_seqno(thd));
+ thd_trx_seqno);
- if (len >= PATH_MAX)
+ char *filename;
+ if (len < 0 || !(filename= (char*)malloc(len++)))
{
- WSREP_ERROR("RBR dump path too long: %d, skipping dump.", len);
+ WSREP_ERROR("snprintf error: %d, skipping dump.", len);
+ DBUG_VOID_RETURN;
+ }
+
+ int len1= my_snprintf(filename, len, "%s/GRA_%ld_%lld_v2.log",
+ wsrep_data_home_dir, thd->thread_id,
+ thd_trx_seqno);
+
+ if (len >= len1)
+ {
+ WSREP_ERROR("RBR dump path truncated: %d, skipping dump.", len);
+ free(filename);
DBUG_VOID_RETURN;
}
@@ -501,6 +525,7 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf,
end_io_cache(&cache);
cleanup1:
+ free(filename);
mysql_file_close(file, MYF(MY_WME));
if (!thd->wsrep_applier) delete ev;
diff --git a/storage/mroonga/vendor/groonga/config.h.cmake b/storage/mroonga/vendor/groonga/config.h.cmake
index bfd0cbd..ec67c5e 100644
--- a/storage/mroonga/vendor/groonga/config.h.cmake
+++ b/storage/mroonga/vendor/groonga/config.h.cmake
@@ -107,6 +107,7 @@
#cmakedefine HAVE_SIGNAL_H
#cmakedefine HAVE_SYS_MMAN_H
#cmakedefine HAVE_SYS_PARAM_H
+#cmakedefine HAVE_SYS_POLL_H
#cmakedefine HAVE_SYS_RESOURCE_H
#cmakedefine HAVE_SYS_SELECT_H
#cmakedefine HAVE_SYS_SOCKET_H
1
0
revision-id: a9f62503cff0988072eb07e5d70cbd8fcab64143 (mariadb-10.2.16-10-ga9f6250)
parent(s): 6d377a523c78b1e442d69ae6be548bccddd54416
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-06-28 00:33:44 -0700
message:
This is another attempt to fix mdev-16473.
The previous correction of the patch for mdev-16473 did not work
correctly for the databases whose names started with '*'.
Added a test case with a database named "*".
---
mysql-test/r/cte_nonrecursive.result | 11 +++++++++++
mysql-test/t/cte_nonrecursive.test | 7 +++++++
sql/sql_base.cc | 2 +-
sql/sql_class.h | 5 ++++-
sql/sql_parse.cc | 3 ++-
sql/table.h | 3 ++-
6 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/mysql-test/r/cte_nonrecursive.result b/mysql-test/r/cte_nonrecursive.result
index f6b8015..32104c5 100644
--- a/mysql-test/r/cte_nonrecursive.result
+++ b/mysql-test/r/cte_nonrecursive.result
@@ -1511,4 +1511,15 @@ a a
3 3
1 1
drop database db_mdev_16473;
+create database `*` ;
+create table `*`.t1 (a int);
+insert into `*`.t1 values (2), (7), (3), (1);
+use `*`;
+select * from t1;
+a
+2
+7
+3
+1
+drop database `*`;
use test;
diff --git a/mysql-test/t/cte_nonrecursive.test b/mysql-test/t/cte_nonrecursive.test
index 11c864b..22bb292 100644
--- a/mysql-test/t/cte_nonrecursive.test
+++ b/mysql-test/t/cte_nonrecursive.test
@@ -1056,4 +1056,11 @@ select * from cte, db_mdev_16473.t1 as t where cte.a=t.a;
drop database db_mdev_16473;
+create database `*` ;
+create table `*`.t1 (a int);
+insert into `*`.t1 values (2), (7), (3), (1);
+use `*`;
+select * from t1;
+drop database `*`;
+
use test;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 093f7cf..9905400 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3331,7 +3331,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
Not a placeholder: must be a base/temporary table or a view. Let us open it.
*/
- if (tables->db[0] == no_db[0])
+ if (tables->no_default_db && !tables->is_fqtn)
{
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
error= TRUE;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e79fde0..88d3af0 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3959,7 +3959,10 @@ class THD :public Statement,
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
return TRUE;
}
- /* This will allow to throw an error later for non-CTE references */
+ /*
+ It does not matter what database name is set in this case
+ because it will never be used after parser stage
+ */
*p_db_length= strlen(no_db);
*p_db= strmake(no_db, *p_db_length);
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3631c8e..6bbc33d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -139,7 +139,7 @@ static bool execute_show_status(THD *, TABLE_LIST *);
static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
const char *any_db="*any*"; // Special symbol for check_access
-const char *no_db="*none*"; // Used when no default db is set
+const char *no_db="*"; // Used when no default db is set
const LEX_STRING command_name[257]={
{ C_STRING_WITH_LEN("Sleep") }, //0
@@ -8188,6 +8188,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
DBUG_RETURN(0);
else
ptr->is_fqtn= FALSE;
+ ptr->no_default_db= !thd->db && !(lex->sphead && lex->sphead->m_name.str);
ptr->alias= alias_str;
ptr->is_alias= alias ? TRUE : FALSE;
diff --git a/sql/table.h b/sql/table.h
index c0cca10..f5d504e 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2095,7 +2095,8 @@ struct TABLE_LIST
qualified name (<db_name>.<table_name>).
*/
bool is_fqtn;
-
+ /** TRUE if no default database is defined for the table name */
+ bool no_default_db;
/* TRUE <=> derived table should be filled right after optimization. */
bool fill_me;
/* TRUE <=> view/DT is merged. */
1
0
revision-id: 92e90c63bdfa87a35c77ba5bee9dde0176f2fb2d (mariadb-10.2.16-10-g92e90c6)
parent(s): 6d377a523c78b1e442d69ae6be548bccddd54416
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-06-28 00:30:44 -0700
message:
This is another attempt to fix mdev-16473.
The previous correction of the patch for mdev-16473 did not work
correctly for the databases whose names started with '*'.
Added a test case with a database named "*none*".
---
mysql-test/r/cte_nonrecursive.result | 11 +++++++++++
mysql-test/t/cte_nonrecursive.test | 7 +++++++
sql/sql_base.cc | 2 +-
sql/sql_class.h | 5 ++++-
sql/sql_parse.cc | 3 ++-
sql/table.h | 3 ++-
6 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/mysql-test/r/cte_nonrecursive.result b/mysql-test/r/cte_nonrecursive.result
index f6b8015..32104c5 100644
--- a/mysql-test/r/cte_nonrecursive.result
+++ b/mysql-test/r/cte_nonrecursive.result
@@ -1511,4 +1511,15 @@ a a
3 3
1 1
drop database db_mdev_16473;
+create database `*` ;
+create table `*`.t1 (a int);
+insert into `*`.t1 values (2), (7), (3), (1);
+use `*`;
+select * from t1;
+a
+2
+7
+3
+1
+drop database `*`;
use test;
diff --git a/mysql-test/t/cte_nonrecursive.test b/mysql-test/t/cte_nonrecursive.test
index 11c864b..22bb292 100644
--- a/mysql-test/t/cte_nonrecursive.test
+++ b/mysql-test/t/cte_nonrecursive.test
@@ -1056,4 +1056,11 @@ select * from cte, db_mdev_16473.t1 as t where cte.a=t.a;
drop database db_mdev_16473;
+create database `*` ;
+create table `*`.t1 (a int);
+insert into `*`.t1 values (2), (7), (3), (1);
+use `*`;
+select * from t1;
+drop database `*`;
+
use test;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 093f7cf..9905400 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3331,7 +3331,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
Not a placeholder: must be a base/temporary table or a view. Let us open it.
*/
- if (tables->db[0] == no_db[0])
+ if (tables->no_default_db && !tables->is_fqtn)
{
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
error= TRUE;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e79fde0..88d3af0 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3959,7 +3959,10 @@ class THD :public Statement,
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
return TRUE;
}
- /* This will allow to throw an error later for non-CTE references */
+ /*
+ It does not matter what database name is set in this case
+ because it will never be used after parser stage
+ */
*p_db_length= strlen(no_db);
*p_db= strmake(no_db, *p_db_length);
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3631c8e..6bbc33d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -139,7 +139,7 @@ static bool execute_show_status(THD *, TABLE_LIST *);
static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
const char *any_db="*any*"; // Special symbol for check_access
-const char *no_db="*none*"; // Used when no default db is set
+const char *no_db="*"; // Used when no default db is set
const LEX_STRING command_name[257]={
{ C_STRING_WITH_LEN("Sleep") }, //0
@@ -8188,6 +8188,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
DBUG_RETURN(0);
else
ptr->is_fqtn= FALSE;
+ ptr->no_default_db= !thd->db && !(lex->sphead && lex->sphead->m_name.str);
ptr->alias= alias_str;
ptr->is_alias= alias ? TRUE : FALSE;
diff --git a/sql/table.h b/sql/table.h
index c0cca10..f5d504e 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2095,7 +2095,8 @@ struct TABLE_LIST
qualified name (<db_name>.<table_name>).
*/
bool is_fqtn;
-
+ /** TRUE if no default database is defined for the table name */
+ bool no_default_db;
/* TRUE <=> derived table should be filled right after optimization. */
bool fill_me;
/* TRUE <=> view/DT is merged. */
1
0
revision-id: e7d3aa6662289cc41f495ba565ea8331e9c33bc1 (mariadb-10.2.16-10-ge7d3aa6)
parent(s): 6d377a523c78b1e442d69ae6be548bccddd54416
author: Igor Babaev
committer: Igor Babaev
timestamp: 2018-06-28 00:13:55 -0700
message:
This is another attempt to fix mdev-16473.
The previous correction of the patch for mdev-16473 did not work
correctly for the databases whose names started with '*'.
Added a test case with a database named "*none*".
---
mysql-test/r/cte_nonrecursive.result | 11 +++++++++++
mysql-test/t/cte_nonrecursive.test | 7 +++++++
sql/sql_base.cc | 2 +-
sql/sql_class.h | 5 ++++-
sql/sql_parse.cc | 3 ++-
sql/table.h | 3 ++-
6 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/mysql-test/r/cte_nonrecursive.result b/mysql-test/r/cte_nonrecursive.result
index f6b8015..b1bf786 100644
--- a/mysql-test/r/cte_nonrecursive.result
+++ b/mysql-test/r/cte_nonrecursive.result
@@ -1511,4 +1511,15 @@ a a
3 3
1 1
drop database db_mdev_16473;
+create database `*none*` ;
+create table `*none*`.t1 (a int);
+insert into `*none*`.t1 values (2), (7), (3), (1);
+use `*none*`;
+select * from t1;
+a
+2
+7
+3
+1
+drop database `*none*`;
use test;
diff --git a/mysql-test/t/cte_nonrecursive.test b/mysql-test/t/cte_nonrecursive.test
index 11c864b..d800903 100644
--- a/mysql-test/t/cte_nonrecursive.test
+++ b/mysql-test/t/cte_nonrecursive.test
@@ -1056,4 +1056,11 @@ select * from cte, db_mdev_16473.t1 as t where cte.a=t.a;
drop database db_mdev_16473;
+create database `*none*` ;
+create table `*none*`.t1 (a int);
+insert into `*none*`.t1 values (2), (7), (3), (1);
+use `*none*`;
+select * from t1;
+drop database `*none*`;
+
use test;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 093f7cf..9905400 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -3331,7 +3331,7 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
Not a placeholder: must be a base/temporary table or a view. Let us open it.
*/
- if (tables->db[0] == no_db[0])
+ if (tables->no_default_db && !tables->is_fqtn)
{
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
error= TRUE;
diff --git a/sql/sql_class.h b/sql/sql_class.h
index e79fde0..88d3af0 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3959,7 +3959,10 @@ class THD :public Statement,
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0));
return TRUE;
}
- /* This will allow to throw an error later for non-CTE references */
+ /*
+ It does not matter what database name is set in this case
+ because it will never be used after parser stage
+ */
*p_db_length= strlen(no_db);
*p_db= strmake(no_db, *p_db_length);
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 3631c8e..6bbc33d 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -139,7 +139,7 @@ static bool execute_show_status(THD *, TABLE_LIST *);
static bool check_rename_table(THD *, TABLE_LIST *, TABLE_LIST *);
const char *any_db="*any*"; // Special symbol for check_access
-const char *no_db="*none*"; // Used when no default db is set
+const char *no_db="*"; // Used when no default db is set
const LEX_STRING command_name[257]={
{ C_STRING_WITH_LEN("Sleep") }, //0
@@ -8188,6 +8188,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
DBUG_RETURN(0);
else
ptr->is_fqtn= FALSE;
+ ptr->no_default_db= !thd->db && !(lex->sphead && lex->sphead->m_name.str);
ptr->alias= alias_str;
ptr->is_alias= alias ? TRUE : FALSE;
diff --git a/sql/table.h b/sql/table.h
index c0cca10..f5d504e 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -2095,7 +2095,8 @@ struct TABLE_LIST
qualified name (<db_name>.<table_name>).
*/
bool is_fqtn;
-
+ /** TRUE if no default database is defined for the table name */
+ bool no_default_db;
/* TRUE <=> derived table should be filled right after optimization. */
bool fill_me;
/* TRUE <=> view/DT is merged. */
1
0