diff options
Diffstat (limited to 'plugin/auth_pam/auth_pam.c')
-rw-r--r-- | plugin/auth_pam/auth_pam.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/plugin/auth_pam/auth_pam.c b/plugin/auth_pam/auth_pam.c new file mode 100644 index 00000000..ffcfa019 --- /dev/null +++ b/plugin/auth_pam/auth_pam.c @@ -0,0 +1,241 @@ +/* + Copyright (c) 2011, 2020, 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 <my_global.h> +#include <config_auth_pam.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <spawn.h> +#include <mysql/plugin_auth.h> +#include "auth_pam_tool.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 winbind_hack = 0; + +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; + +/* + sleep_limit is now 5 meaning up to 1 second sleep. + each step means 10 times longer sleep, so 6 would mean 10 seconds. +*/ +static const unsigned int sleep_limit= 5; + +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, pkt_len= 0; + unsigned char field, *pkt; + unsigned int n_sleep= 0; + useconds_t sleep_time= 100; + posix_spawn_file_actions_t file_actions; + char toolpath[FN_REFLEN]; + size_t plugin_dir_len= strlen(opt_plugin_dir); + char *const argv[2]= {toolpath, 0}; + int res; + + PAM_DEBUG((stderr, "PAM: opening pipes.\n")); + if (pipe(p_to_c) < 0 || pipe(c_to_p) < 0) + { + my_printf_error(ENOEXEC, "pam: cannot create pipes (errno: %M)", + ME_ERROR_LOG_ONLY, errno); + return CR_ERROR; + } + + if (plugin_dir_len + tool_name_len + 2 > sizeof(toolpath)) + { + my_printf_error(ENOEXEC, "pam: too long path to <plugindir>/%s", + ME_ERROR_LOG_ONLY, tool_name); + return CR_ERROR; + } + + 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: forking %s\n", toolpath)); + res= posix_spawn_file_actions_init(&file_actions) || + posix_spawn_file_actions_addclose(&file_actions, p_to_c[1]) || + posix_spawn_file_actions_addclose(&file_actions, c_to_p[0]) || + posix_spawn_file_actions_adddup2(&file_actions, p_to_c[0], 0) || + posix_spawn_file_actions_adddup2(&file_actions, c_to_p[1], 1) || + posix_spawn(&proc_id, toolpath, &file_actions, NULL, argv, NULL); + + /* Parent process continues. */ + posix_spawn_file_actions_destroy(&file_actions); + close(p_to_c[0]); + close(c_to_p[1]); + + if (res) + { + my_printf_error(ENOEXEC, "pam: cannot exec %s (errno: %M)", + ME_ERROR_LOG_ONLY, toolpath, errno); + goto error_ret; + } + + /* no user name yet ? read the client handshake packet with the user name */ + if (info->user_name == 0) + { + if ((pkt_len= vio->read_packet(vio, &pkt)) < 0) + goto error_ret; + } + else + pkt= NULL; + + PAM_DEBUG((stderr, "PAM: parent sends user data [%s], [%s].\n", + info->user_name, info->auth_string)); + +#ifndef DBUG_OFF + field= pam_debug ? 1 : 0; +#else + field= 0; +#endif + field|= winbind_hack ? 2 : 0; + + 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; + + 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; + + if (!pkt || !*pkt || (buf[0] >> 1) != 2) + { + 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 ((pkt_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, pkt_len)) + goto error_ret; + + pkt= NULL; + } + 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]); + while (waitpid(proc_id, NULL, WNOHANG) != (int) proc_id) + { + if (n_sleep++ == sleep_limit) + { + /* + The auth_pam_tool application doesn't terminate. + Means something wrong happened there like pam_xxx.so hanged. + */ + kill(proc_id, SIGKILL); + sleep_time= 1000000; /* 1 second wait should be enough. */ + PAM_DEBUG((stderr, "PAM: auth_pam_tool doesn't terminate," + " have to kill it.\n")); + } + else if (n_sleep > sleep_limit) + break; + usleep(sleep_time); + sleep_time*= 10; + } + + PAM_DEBUG((stderr, "PAM: auth result %d.\n", result)); + return result; +} + + +#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; +} + +maria_declare_plugin(pam) +{ + MYSQL_AUTHENTICATION_PLUGIN, + &info, + "pam", + "MariaDB Corp", + "PAM based authentication", + PLUGIN_LICENSE_GPL, + init, + NULL, + 0x0200, + NULL, + vars, + "2.0", + MariaDB_PLUGIN_MATURITY_STABLE +} +maria_declare_plugin_end; |