diff --git a/cmake/install.cmake b/cmake/install.cmake index e75ad943..cc65cf70 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake @@ -76,7 +76,7 @@ SET(BIN_INSTALL_DIR_DEFAULT "bin") SET(LIB_INSTALL_DIR_DEFAULT "lib") SET(INCLUDE_INSTALL_DIR_DEFAULT "include") SET(DOCS_INSTALL_DIR_DEFAULT "docs") -SET(LIB_INSTALL_PLUGIN_DIR_DEFAULT "lib/plugins") +SET(PLUGIN_INSTALL_DIR_DEFAULT "lib/plugin") # # RPM layout @@ -85,15 +85,15 @@ SET(SUFFIX_INSTALL_DIR_RPM "mariadb") SET(BIN_INSTALL_DIR_RPM "bin") IF(CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") SET(LIB_INSTALL_DIR_RPM "lib64") - SET(LIB_INSTALL_PLUGINDIR_RPM "lib64/plugins") + SET(PLUGIN_INSTALL_DIRDIR_RPM "lib64/plugin") ELSE() SET(LIB_INSTALL_DIR_RPM "lib") - SET(LIB_INSTALL_PLUGINDIR_RPM "lib/plugins") + SET(PLUGIN_INSTALL_DIRDIR_RPM "lib/plugin") ENDIF() SET(INCLUDE_INSTALL_DIR_RPM "include") SET(DOCS_INSTALL_DIR_RPM "docs") -SET(LIB_INSTALL_PLUGIN_DIR_RPM "lib/plugins") +SET(PLUGIN_INSTALL_DIR_RPM "lib/plugin") # # Overwrite defaults @@ -102,6 +102,10 @@ IF(LIB_INSTALL_DIR) SET(LIB_INSTALL_DIR_${INSTALL_LAYOUT} ${LIB_INSTALL_DIR}) ENDIF() +IF(PLUGIN_INSTALL_DIR) + SET(PLUGIN_INSTALL_DIR_${INSTALL_LAYOUT} ${PLUGIN_INSTALL_DIR}) +ENDIF() + IF(INCLUDE_INSTALL_DIR) SET(INCLUDE_INSTALL_DIR_${INSTALL_LAYOUT} ${INCLUDE_INSTALL_DIR}) ENDIF() @@ -118,7 +122,7 @@ IF(NOT SUFFIX_INSTALL_DIR) SET(SUFFIX_INSTALL_DIR_${INSTALL_LAYOUT} "mariadb") ENDIF() -FOREACH(dir "BIN" "LIB" "INCLUDE" "DOCS" "PREFIX" "SUFFIX") +FOREACH(dir "BIN" "LIB" "INCLUDE" "DOCS" "PREFIX" "SUFFIX" "PLUGIN") SET(${dir}_INSTALL_DIR ${${dir}_INSTALL_DIR_${INSTALL_LAYOUT}}) MARK_AS_ADVANCED(${dir}_INSTALL_DIR) ENDFOREACH() diff --git a/include/my_config.h.in b/include/my_config.h.in index 04d3b7ac..a1181d0b 100644 --- a/include/my_config.h.in +++ b/include/my_config.h.in @@ -274,5 +274,5 @@ #cmakedefine HAVE_THREADS 1 #cmakedefine SHAREDIR "@SHAREDIR@" #cmakedefine DEFAULT_CHARSET_HOME "@DEFAULT_CHARSET_HOME@" -#cmakedefine PLUGINDIR "@PLUGINDIR@" +#cmakedefine PLUGINDIR "@PREFIX_INSTALL_DIR@/@PLUGIN_INSTALL_DIR@" diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h index a95aa61c..1e706c2c 100644 --- a/include/mysql/client_plugin.h +++ b/include/mysql/client_plugin.h @@ -1,4 +1,5 @@ /* Copyright (C) 2010 - 2012 Sergei Golubchik and Monty Program Ab + 2014 MariaDB Corporation AB This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public diff --git a/include/mysql_com.h b/include/mysql_com.h index 5fee8189..d4495571 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -173,8 +173,9 @@ enum enum_server_command CLIENT_MULTI_STATEMENTS |\ CLIENT_MULTI_RESULTS |\ CLIENT_PROGRESS |\ - CLIENT_SSL_VERIFY_SERVER_CERT |\ + CLIENT_SSL_VERIFY_SERVER_CERT |\ CLIENT_REMEMBER_OPTIONS |\ + CLIENT_PLUGIN_AUTH |\ CLIENT_CONNECT_ATTRS) #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD |\ @@ -384,7 +385,6 @@ char *scramble_323(char *to,const char *message,const char *password); void my_scramble_41(const unsigned char *buffer, const char *scramble, const char *password); my_bool check_scramble(const char *, const char *message, unsigned long *salt,my_bool old_ver); -char *get_tty_password(char *opt_message); void hash_password(unsigned long *result, const char *password, size_t len); /* Some other useful functions */ diff --git a/libmariadb/client_plugin.c b/libmariadb/client_plugin.c index d0711e1e..376a827c 100644 --- a/libmariadb/client_plugin.c +++ b/libmariadb/client_plugin.c @@ -346,6 +346,7 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, char dlpath[FN_REFLEN+1]; void *sym, *dlhandle; struct st_mysql_client_plugin *plugin; + char *env_plugin_dir= getenv("MARIADB_PLUGIN_DIR"); if (is_not_initialized(mysql, name)) return NULL; @@ -362,7 +363,8 @@ mysql_load_plugin_v(MYSQL *mysql, const char *name, int type, /* Compile dll path */ strxnmov(dlpath, sizeof(dlpath) - 1, mysql->options.extension && mysql->options.extension->plugin_dir ? - mysql->options.extension->plugin_dir : PLUGINDIR, "/", + mysql->options.extension->plugin_dir : (env_plugin_dir) ? env_plugin_dir : + PLUGINDIR, "/", name, SO_EXT, NullS); /* Open new dll handle */ diff --git a/libmariadb/get_password.c b/libmariadb/get_password.c index 69d69063..783a730c 100644 --- a/libmariadb/get_password.c +++ b/libmariadb/get_password.c @@ -63,42 +63,82 @@ #endif #if defined( _WIN32) || defined(OS2) -/* were just going to fake it here and get input from the keyboard */ +/* {{{ statuc char* get_password */ +/* + reads password from tty/console -char *get_tty_password(char *opt_message) + SYNOPSIS + get_password() + buffer input buffer + length length of input buffer + + DESCRIPTION + reads a password from console (Windows) or tty without echoing + it's characters. Input buffer must be allocated by calling function. + + RETURNS + buffer pointer to input buffer +*/ +char* get_tty_password(char *prompt, char *buffer, int length) { - char to[80]; - char *pos=to,*end=to+sizeof(to)-1; - int i=0; - DBUG_ENTER("get_tty_password"); - fprintf(stdout,opt_message ? opt_message : "Enter password: "); - for (;;) - { - char tmp; - tmp=_getch(); - if (tmp == '\b' || (int) tmp == 127) - { - if (pos != to) - { - _cputs("\b \b"); - pos--; - continue; - } - } - if (tmp == '\n' || tmp == '\r' || tmp == 3) - break; - if (iscntrl(tmp) || pos == end) - continue; - _cputs("*"); - *(pos++) = tmp; - } - while (pos != to && isspace(pos[-1]) == ' ') - pos--; /* Allow dummy space at end */ - *pos=0; - _cputs("\n"); - DBUG_RETURN(my_strdup(to,MYF(MY_FAE))); -} +#ifdef _WIN32 + DWORD SaveState; + HANDLE Hdl; + int Offset= 0; + DWORD CharsProcessed= 0; + char inChar; + ZeroMemory(buffer, length); + + if (!(Hdl= CreateFile("CONIN$", + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, 0, NULL))) + { + /* todo: provide a graphical dialog */ + return buffer; + } + /* Save ConsoleMode and set ENABLE_PROCESSED_INPUT: + CTRL+C is processed by the system and is not placed in the input buffer */ + GetConsoleMode(Hdl, &SaveState); + SetConsoleMode(Hdl, ENABLE_PROCESSED_INPUT); + + do + { + if (!ReadConsole(Hdl, &inChar, 1, &CharsProcessed, NULL) || + !CharsProcessed) + break; + + switch(inChar) { + case '\b': /* backslash */ + if (Offset) + { + /* cursor is always at the end */ + Offset--; + buffer[Offset]= 0; + _cputs("\b \b"); + } + break; + case '\n': + case '\r': + break; + default: + buffer[Offset]= inChar; + if (Offset < length - 2) + Offset++; + _cputs("*"); + break; + } + } while (CharsProcessed && inChar != '\n' && inChar != '\r'); + SetConsoleMode(Hdl, SaveState); + CloseHandle(Hdl); + return buffer; + +#else +#endif +} +/* }}} */ #else @@ -150,14 +190,13 @@ static void get_password(char *to,uint length,int fd,bool echo) #endif /* ! HAVE_GETPASS */ -char *get_tty_password(char *opt_message) +char *get_tty_password(char *opt_message, char *buff, int bufflen) { #ifdef HAVE_GETPASS char *passbuff; #else /* ! HAVE_GETPASS */ TERMIO org,tmp; #endif /* HAVE_GETPASS */ - char buff[80]; DBUG_ENTER("get_tty_password"); @@ -165,7 +204,7 @@ char *get_tty_password(char *opt_message) passbuff = getpass(opt_message ? opt_message : "Enter password: "); /* copy the password to buff and clear original (static) buffer */ - strnmov(buff, passbuff, sizeof(buff) - 1); + strnmov(buff, passbuff, bufflen - 1); #ifdef _PASSWORD_LEN memset(passbuff, 0, _PASSWORD_LEN); #endif @@ -182,7 +221,7 @@ char *get_tty_password(char *opt_message) tmp.c_cc[VMIN] = 1; tmp.c_cc[VTIME] = 0; tcsetattr(fileno(stdin), TCSADRAIN, &tmp); - get_password(buff, sizeof(buff)-1, fileno(stdin), isatty(fileno(stdout))); + get_password(buff, bufflen-1, fileno(stdin), isatty(fileno(stdout))); tcsetattr(fileno(stdin), TCSADRAIN, &org); #elif defined(HAVE_TERMIO_H) ioctl(fileno(stdin), (int) TCGETA, &org); @@ -191,7 +230,7 @@ char *get_tty_password(char *opt_message) tmp.c_cc[VMIN] = 1; tmp.c_cc[VTIME]= 0; ioctl(fileno(stdin),(int) TCSETA, &tmp); - get_password(buff,sizeof(buff)-1,fileno(stdin),isatty(fileno(stdout))); + get_password(buff,bufflen-1,fileno(stdin),isatty(fileno(stdout))); ioctl(fileno(stdin),(int) TCSETA, &org); #else gtty(fileno(stdin), &org); @@ -199,13 +238,13 @@ char *get_tty_password(char *opt_message) tmp.sg_flags &= ~ECHO; tmp.sg_flags |= RAW; stty(fileno(stdin), &tmp); - get_password(buff,sizeof(buff)-1,fileno(stdin),isatty(fileno(stdout))); + get_password(buff,bufflen-1,fileno(stdin),isatty(fileno(stdout))); stty(fileno(stdin), &org); #endif if (isatty(fileno(stdout))) fputc('\n',stdout); #endif /* HAVE_GETPASS */ - DBUG_RETURN(my_strdup(buff,MYF(MY_FAE))); + DBUG_RETURN(buff); } #endif /*_WIN32*/ diff --git a/mariadb_config/mariadb_config.c.in b/mariadb_config/mariadb_config.c.in index 413a30e0..837139c1 100644 --- a/mariadb_config/mariadb_config.c.in +++ b/mariadb_config/mariadb_config.c.in @@ -8,6 +8,7 @@ "@extra_dynamic_LDFLAGS@" #define CFLAGS INCLUDE " @CMAKE_C_FLAGS@" #define VERSION "@MYSQL_CLIENT_VERSION@" +#define PLUGIN_DIR "@PREFIX_INSTALL_DIR@/@PLUGIN_INSTALL_DIR@" #define SOCKET "@MYSQL_UNIX_ADDR@" #define PORT "@MYSQL_PORT@" @@ -21,6 +22,7 @@ static struct option long_options[]= {"version", no_argument, 0, 'f'}, {"socket", no_argument, 0, 'g'}, {"port", no_argument, 0, 'h'}, + {"plugindir", no_argument, 0, 'p'}, {NULL, 0, 0, 0} }; @@ -33,7 +35,8 @@ static char *values[]= LIBS, VERSION, SOCKET, - PORT + PORT, + PLUGIN_DIR }; void usage(void) @@ -90,6 +93,9 @@ int main(int argc, char **argv) case 'h': puts(PORT); break; + case 'p': + puts(PLUGINDIR); + break; default: exit(0); } diff --git a/plugins/auth/CMakeLists.txt b/plugins/auth/CMakeLists.txt new file mode 100644 index 00000000..178c961e --- /dev/null +++ b/plugins/auth/CMakeLists.txt @@ -0,0 +1,13 @@ +# Dialog plugin +SET(DIALOG_SOURCES dialog.c ${CMAKE_SOURCE_DIR}/libmariadb/get_password.c) +IF(WIN32) + SET(DIALOG_SOURCES ${DIALOG_SOURCES} ${CMAKE_SOURCE_DIR}/plugins/plugin.def) +ENDIF() +ADD_LIBRARY(dialog SHARED ${DIALOG_SOURCES}) +SET_TARGET_PROPERTIES(dialog PROPERTIES PREFIX "") + +INSTALL(TARGETS + dialog + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" + LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" + ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}") diff --git a/plugins/auth/dialog.c b/plugins/auth/dialog.c new file mode 100644 index 00000000..53bbec80 --- /dev/null +++ b/plugins/auth/dialog.c @@ -0,0 +1,218 @@ +/************************************************************************************ + Copyright (C) 2014 MariaDB Corporation AB + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not see + or write to the Free Software Foundation, Inc., + 51 Franklin St., Fifth Floor, Boston, MA 02110, USA + + Part of this code includes code from the PHP project which + is freely available from http://www.php.net +*************************************************************************************/ +#ifndef _WIN32 +#define _GNU_SOURCE 1 +#endif + +#include +#include +#include +#include +#include + +#ifndef WIN32 +#include +#endif + + +/* function prototypes */ +extern char *get_tty_password(char *opt_message, char *buff, int bufflen); +static int auth_dialog_open(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); +static int auth_dialog_init(char *unused1, + size_t unused2, + int unused3, + va_list); + +mysql_authentication_dialog_ask_t auth_dialog_func; + +mysql_declare_client_plugin(AUTHENTICATION) + "dialog", + "Sergei Golubchik, Georg Richter", + "Dialog Client Authentication Plugin", + {0,1,0}, + auth_dialog_init, + NULL, + auth_dialog_open +mysql_end_client_plugin; + + +/* {{{ static char *auth_dialog_native_prompt */ +/* + Native dialog prompt via stdin + + SYNOPSIS + auth_dialog_native_prompt + mysql connection handle + type input type + prompt prompt + buffer Input buffer + buffer_len Input buffer length + + DESCRIPTION + + RETURNS + Input buffer +*/ +static char *auth_dialog_native_prompt(MYSQL *mysql, + int type, + const char *prompt, + char *buffer, + int buffer_len) +{ + /* display prompt */ + fprintf(stdout, "%s", prompt); + + memset(buffer, 0, buffer_len); + + /* for type 2 (password) don't display input */ + if (type != 2) + { + if (fgets(buffer, buffer_len - 1, stdin)) + { + /* remove trailing line break */ + size_t length= strlen(buffer); + if (length && buffer[length - 1] == '\n') + buffer[length - 1]= 0; + } + } + else + { + get_tty_password("", buffer, buffer_len - 1); + } + return buffer; +} +/* }}} */ + +/* {{{ static int auth_dialog_open */ +/* + opens dialog + + SYNOPSIS + vio Vio + mysql connection handle + + DESCRIPTION + reads prompt from server, waits for input and sends + input to server. + Note that first byte of prompt indicates if we have a + password which should not be echoed to stdout. + + RETURN + CR_ERROR if an error occurs + CR_OK + CR_OK_HANDSHAKE_COMPLETE +*/ +static int auth_dialog_open(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +{ + uchar *packet; + uchar type; + char dialog_buffer[1024]; + char *response; + size_t packet_length; + my_bool first_loop= TRUE; + + do { + if ((packet_length= vio->read_packet(vio, &packet)) < 0) + /* read error */ + return CR_ERROR; + + if (packet_length > 0) + { + type= *packet; + packet++; + + /* check for protocol packet */ + if (!type || type == 254) + return CR_OK_HANDSHAKE_COMPLETE; + + /* shift one bit */ + type= type >> 1; + + if (type == 2 && + first_loop && + mysql->passwd && mysql->passwd[0]) + response= mysql->passwd; + else + response= auth_dialog_func(mysql, type, + (const char *)packet, + dialog_buffer, 1024); + } + else + { + /* in case mysql_change_user was called the client needs + to send packet first */ + response= mysql->passwd; + } + if (!response || + vio->write_packet(vio, response, strlen(response) + 1)) + return CR_ERROR; + + first_loop= FALSE; + + } while(type != 2); + return CR_OK; +} +/* }}} */ + +/* {{{ static int auth_dialog_init */ +/* + Initialization routine + + SYNOPSIS + auth_dialog_init + unused1 + unused2 + unused3 + unused4 + + DESCRIPTION + Init function checks if the caller provides own dialog function. + The function name must be mariadb_auth_dialog or + mysql_authentication_dialog_ask. If the function cannot be found, + we will use owr own simple command line input. + + RETURN + 0 success +*/ +static int auth_dialog_init(char *unused1 __attribute__((unused)), + size_t unused2 __attribute__((unused)), + int unused3 __attribute__((unused)), + va_list unused4 __attribute__((unused))) +{ + void *func; +#ifdef WIN32 + if (!(func= GetProcAddress(GetModuleHandle(NULL), "mariadb_auth_dialog"))) + /* for MySQL users */ + func= GetProcAddress(GetModuleHandle(NULL), "mysql_authentication_dialog_ask"); +#else + if (!(func= dlsym(RTLD_DEFAULT, "mariadb_auth_dialog"))) + /* for MySQL users */ + func= dlsym(RTLD_DEFAULT, "mysql_authentication_dialog_ask"); +#endif + if (func) + auth_dialog_func= (mysql_authentication_dialog_ask_t)func; + else + auth_dialog_func= auth_dialog_native_prompt; + + return 0; +} +/* }}} */ diff --git a/plugins/plugin.def b/plugins/plugin.def new file mode 100644 index 00000000..70af9256 --- /dev/null +++ b/plugins/plugin.def @@ -0,0 +1,2 @@ +EXPORTS + _mysql_client_plugin_declaration_ DATA diff --git a/win/packaging/CMakeLists.txt b/win/packaging/CMakeLists.txt index ed1d1618..eee0ea48 100644 --- a/win/packaging/CMakeLists.txt +++ b/win/packaging/CMakeLists.txt @@ -38,8 +38,13 @@ IF (WITH_SIGNCODE) COMMAND signtool sign ${SIGN_OPTIONS} ${CLIENT_DBG_DIR}/libmariadb.lib) EXECUTE_PROCESS( COMMAND signtool sign ${SIGN_OPTIONS} ${CLIENT_DBG_DIR}/mariadbclient.lib) + EXECUTE_PROCESS( + COMMAND signtool sign ${SIGN_OPTIONS} ${CMAKE_BINARY_DIR}/plugins/auth/${CMAKE_BUILD_TYPE}/dialog.dll) ENDIF() +SET(MARIADB_PLUGINS "${MARIADB_PLUGINS} \n") + + FOREACH(src ${MARIADB_CLIENT_INCLUDES}) STRING(REPLACE "-" "_" src_id ${src}) SET(MARIADB_INCLUDE_FILES "${MARIADB_INCLUDE_FILES} \n") diff --git a/win/packaging/mariadb-connector-c.xml.in b/win/packaging/mariadb-connector-c.xml.in index 1834c939..dee8ec24 100644 --- a/win/packaging/mariadb-connector-c.xml.in +++ b/win/packaging/mariadb-connector-c.xml.in @@ -33,6 +33,7 @@ + @@ -58,6 +59,9 @@ + + @MARIADB_PLUGINS@ +