diff options
author | Sergei Golubchik <sergii@pisem.net> | 2011-07-02 22:08:51 +0200 |
---|---|---|
committer | Sergei Golubchik <sergii@pisem.net> | 2011-07-02 22:08:51 +0200 |
commit | 9809f05199aeb0b67991fac41bd86f38730768dc (patch) | |
tree | fa2792ff86d0da014b535d743759810612338042 /libmysql | |
parent | 0accbd0364e0333e0b119aa9ce93e34ded9df6cb (diff) | |
parent | 5a0e7394a5ae0c7b6a1ea35b7ea3a8985325987a (diff) | |
download | mariadb-git-9809f05199aeb0b67991fac41bd86f38730768dc.tar.gz |
5.5-merge
Diffstat (limited to 'libmysql')
-rw-r--r-- | libmysql/CMakeLists.txt | 19 | ||||
-rw-r--r-- | libmysql/Makefile.am | 115 | ||||
-rw-r--r-- | libmysql/Makefile.shared | 120 | ||||
-rw-r--r-- | libmysql/acinclude.m4 | 91 | ||||
-rw-r--r-- | libmysql/authentication_win/CMakeLists.txt | 33 | ||||
-rw-r--r-- | libmysql/authentication_win/common.cc | 492 | ||||
-rw-r--r-- | libmysql/authentication_win/common.h | 324 | ||||
-rw-r--r-- | libmysql/authentication_win/handshake.cc | 289 | ||||
-rw-r--r-- | libmysql/authentication_win/handshake.h | 181 | ||||
-rw-r--r-- | libmysql/authentication_win/handshake_client.cc | 378 | ||||
-rw-r--r-- | libmysql/authentication_win/log_client.cc | 55 | ||||
-rw-r--r-- | libmysql/authentication_win/plugin_client.cc | 68 | ||||
-rw-r--r-- | libmysql/client_settings.h | 15 | ||||
-rw-r--r-- | libmysql/conf_to_src.c | 3 | ||||
-rw-r--r-- | libmysql/errmsg.c | 5 | ||||
-rw-r--r-- | libmysql/get_password.c | 3 | ||||
-rw-r--r-- | libmysql/libmysql.c | 92 |
17 files changed, 1895 insertions, 388 deletions
diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 84dd3fa4b43..816977d9b8e 100644 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. # # 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 @@ -134,12 +134,19 @@ CACHE INTERNAL "Functions exported by client API" ) +IF(WIN32) + ADD_SUBDIRECTORY(authentication_win) + SET(WITH_AUTHENTICATION_WIN 1) + ADD_DEFINITIONS(-DAUTHENTICATION_WIN) +ENDIF(WIN32) + SET(CLIENT_SOURCES get_password.c libmysql.c errmsg.c ../sql-common/client.c ../sql-common/my_time.c + ../sql-common/client_plugin.c ../sql/net_serv.cc ../sql-common/client_plugin.c ../sql-common/pack.c @@ -151,6 +158,10 @@ ADD_DEPENDENCIES(clientlib GenError) SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL}) +IF(WITH_AUTHENTICATION_WIN) + LIST(APPEND LIBS auth_win_client) +ENDIF(WITH_AUTHENTICATION_WIN) + # Merge several convenience libraries into one big mysqlclient # and link them together into shared library. MERGE_LIBRARIES(mysqlclient STATIC ${LIBS} COMPONENT Development) @@ -172,7 +183,7 @@ IF(UNIX) SET(${OUTNAME} ${LIBNAME}${EXTENSION}${DOT_VERSION}) ENDIF() ENDMACRO() - INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r.a mysqlclient ${INSTALL_LIBDIR} COMPONENT SharedLibraries) + INSTALL_SYMLINK(${CMAKE_STATIC_LIBRARY_PREFIX}mysqlclient_r.a mysqlclient ${INSTALL_LIBDIR} Development) ENDIF() IF(NOT DISABLE_SHARED) @@ -210,7 +221,7 @@ IF(NOT DISABLE_SHARED) "${CMAKE_SHARED_LIBRARY_SUFFIX}" "" linkname) - INSTALL_SYMLINK(${linkname} libmysql ${INSTALL_LIBDIR} COMPONENT SharedLibraries) + INSTALL_SYMLINK(${linkname} libmysql ${INSTALL_LIBDIR} SharedLibraries) SET(OS_SHARED_LIB_SYMLINKS "${SHARED_LIB_MAJOR_VERSION}" "${OS_SHARED_LIB_VERSION}") LIST(REMOVE_DUPLICATES OS_SHARED_LIB_SYMLINKS) FOREACH(ver ${OS_SHARED_LIB_SYMLINKS}) @@ -219,7 +230,7 @@ IF(NOT DISABLE_SHARED) "${CMAKE_SHARED_LIBRARY_SUFFIX}" "${ver}" linkname) - INSTALL_SYMLINK(${linkname} libmysql ${INSTALL_LIBDIR} COMPONENT SharedLibraries) + INSTALL_SYMLINK(${linkname} libmysql ${INSTALL_LIBDIR} SharedLibraries) ENDFOREACH() ENDIF() ENDIF() diff --git a/libmysql/Makefile.am b/libmysql/Makefile.am deleted file mode 100644 index 07f66c28ddb..00000000000 --- a/libmysql/Makefile.am +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright (C) 2000-2004 MySQL AB, 2008-2009 Sun Microsystems, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of version 2 GNU General Public License as -# published by the Free Software Foundation. -# -# There are special exceptions to the terms and conditions of the GPL as it -# is applied to this software. View the full text of the exception in file -# EXCEPTIONS-CLIENT in the directory of this software distribution. -# -# 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, write to the Free -# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA -# -# This file is public domain and comes with NO WARRANTY of any kind - -target = libmysqlclient.la -target_defs = -DMYSQL_CLIENT_NO_THREADS -DDISABLE_MYSQL_THREAD_H \ - @LIB_EXTRA_CCFLAGS@ - -LIBS = @CLIENT_LIBS@ -INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ - $(openssl_includes) @ZLIB_INCLUDES@ - -include $(srcdir)/Makefile.shared - -libmysqlclient_la_SOURCES = $(target_sources) -libmysqlclient_la_LIBADD = $(target_libadd) $(yassl_las) -libmysqlclient_la_LDFLAGS = $(target_ldflags) -EXTRA_DIST = Makefile.shared libmysql.def CMakeLists.txt -noinst_HEADERS = client_settings.h - -link_sources: - set -x; \ - ss=`echo $(mystringsobjects) | sed "s;\.lo;.c;g"`; \ - ds=`echo $(dbugobjects) | sed "s;\.lo;.c;g"`; \ - ms=`echo $(mysysobjects) | sed "s;\.lo;.c;g"`; \ - vs=`echo $(vio_objects) | sed "s;\.lo;.c;g"`; \ - scs=`echo $(sql_cmn_objects) | sed "s;\.lo;.c;g"`; \ - for f in $$ss; do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \ - done; \ - for f in $$vs $(vioheaders); do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/vio/$$f $$f; \ - done; \ - for f in $$scs; do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/sql-common/$$f $$f; \ - done; \ - for f in $(mystringsextra); do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/strings/$$f $$f; \ - done; \ - for f in $$ds; do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/dbug/$$f $$f; \ - done; \ - for f in $$ms $(mysysheaders); do \ - rm -f $$f; \ - @LN_CP_F@ $(top_srcdir)/mysys/$$f $$f; \ - done; \ - rm -f net.c; \ - @LN_CP_F@ $(top_srcdir)/sql/net_serv.cc net.c ; \ - rm -f password.c; \ - @LN_CP_F@ $(top_srcdir)/sql/password.c password.c - echo timestamp > link_sources - -# This part requires GNUmake -# -# This makes a distribution file with only the files needed to compile -# a minimal MySQL client library -# -# For a really minimal distribution (without debugging code) we could -# keep only the stubs for debug.c -# -# A list of needed headers collected from the deps information 000213 -nh = my_global.h dbug.h errmsg.h \ - m_ctype.h m_string.h password.h \ - my_alarm.h my_config.h my_dir.h my_list.h my_net.h my_sys.h \ - mysql.h mysql_com.h mysql_version.h mysqld_error.h \ - mysys_err.h my_pthread.h thr_alarm.h violite.h hash.h \ - sql_common.h ../libmysql/client_settings.h -# Get a list of the needed objects -lobjs = $(mysysobjects1) $(dbugobjects) $(mystringsobjects) $(sqlobjects) - -do-lib-dist: - dir=libmysql-$(MYSQL_NO_DASH_VERSION); \ - srcs1=`echo $(lobjs) | sed "s;\.lo;.c;g"`; \ - srcs2=$(target_sources); \ - srcs="$$srcs1 $$srcs2"; \ - objs1=`echo $(lobjs) | sed "s;\.lo;.o;g"`; \ - objs2=`echo $(target_sources) | sed "s;\.c;.o;g"`; \ - objs="$$objs1 $$objs2"; \ - rm -rf $$dir; \ - mkdir $$dir; \ - $(INSTALL_DATA) $$srcs $(mysysheaders) $$dir; \ - for i in $(nh); do $(INSTALL_DATA) ../include/$$i $$dir; done; \ - echo "# A very minimal Makefile to compile" > $$dir/Makefile; \ - echo "# the minimized libmysql library" >> $$dir/Makefile; \ - echo "# This file is autogenerated from Makefile.am" >> $$dir/Makefile; \ - echo 'CFLAGS= -I. -DMYSQL_CLIENT_NO_THREADS' >>$$dir/Makefile; \ - echo "obj=$$objs" >>$$dir/Makefile; \ - echo 'all: libmysql.a' >>$$dir/Makefile; \ - echo 'libmysql.a: $$(obj)' >>$$dir/Makefile; \ - echo ' $$(AR) r $$@ $$?' >>$$dir/Makefile; \ - gtar cvzf $$dir.tar.gz $$dir; \ - cd $$dir; gmake diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared deleted file mode 100644 index 09f597444f4..00000000000 --- a/libmysql/Makefile.shared +++ /dev/null @@ -1,120 +0,0 @@ -# Copyright (C) 2000-2004 MySQL AB, 2008-2009 Sun Microsystems, Inc. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of version 2 GNU General Public License as -# published by the Free Software Foundation. -# -# There are special exceptions to the terms and conditions of the GPL as it -# is applied to this software. View the full text of the exception in file -# EXCEPTIONS-CLIENT in the directory of this software distribution. -# -# 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, write to the Free -# Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, -# MA 02111-1307, USA -# -# This file is public domain and comes with NO WARRANTY of any kind - -MYSQLDATAdir = $(localstatedir) -MYSQLSHAREdir = $(pkgdatadir) -MYSQLBASEdir= $(prefix) -pkgplugindir = $(pkglibdir)/plugin -## We'll use CLIENT_EXTRA_LDFLAGS for threaded and non-threaded -## until someone complains that they need separate options. -LDADD = @CLIENT_EXTRA_LDFLAGS@ $(target) -pkglib_LTLIBRARIES = $(target) - -noinst_PROGRAMS = conf_to_src - - -target_sources = libmysql.c password.c \ - get_password.c errmsg.c - -mystringsobjects = strmov.lo strxmov.lo strxnmov.lo strnmov.lo \ - strmake.lo strend.lo \ - strnlen.lo strfill.lo is_prefix.lo \ - int2str.lo str2int.lo strcont.lo \ - strcend.lo ctype-latin1.lo \ - bchange.lo bmove.lo bmove_upp.lo longlong2str.lo \ - strtoull.lo strtoll.lo llstr.lo my_vsnprintf.lo \ - ctype.lo ctype-simple.lo ctype-bin.lo ctype-mb.lo \ - ctype-big5.lo ctype-czech.lo ctype-cp932.lo ctype-eucjpms.lo ctype-euc_kr.lo \ - ctype-win1250ch.lo ctype-utf8.lo ctype-extra.lo \ - ctype-ucs2.lo ctype-gb2312.lo ctype-gbk.lo \ - ctype-sjis.lo ctype-tis620.lo ctype-ujis.lo \ - ctype-uca.lo xml.lo my_strtoll10.lo str_alloc.lo dtoa.lo \ - strmov_overlapp.lo - -mystringsextra= strto.c -dbugobjects = dbug.lo -mysysheaders = mysys_priv.h my_static.h -vioheaders = vio_priv.h -mysysobjects1 = my_init.lo my_static.lo my_malloc.lo \ - my_create.lo my_delete.lo mf_tempfile.lo my_open.lo \ - my_file.lo my_read.lo my_write.lo errors.lo \ - my_error.lo my_getwd.lo my_div.lo \ - mf_pack.lo my_mess.lo mf_dirname.lo mf_fn_ext.lo\ - mf_wcomp.lo typelib.lo my_alloc.lo \ - mf_format.lo mf_path.lo mf_unixpath.lo my_fopen.lo \ - my_symlink.lo my_fstream.lo mf_arr_appstr.lo \ - mf_loadpath.lo my_pthread.lo my_thr_init.lo \ - thr_mutex.lo mulalloc.lo string.lo default.lo \ - my_compress.lo array.lo my_once.lo list.lo \ - charset.lo charset-def.lo hash.lo mf_iocache.lo \ - mf_iocache2.lo my_seek.lo my_sleep.lo \ - my_pread.lo mf_cache.lo md5.lo sha1.lo my_rnd.lo \ - my_getopt.lo my_gethostbyname.lo my_port.lo \ - my_rename.lo my_chsize.lo my_sync.lo \ - my_getsystime.lo my_symlink2.lo mf_same.lo -sqlobjects = net.lo -sql_cmn_objects = pack.lo client.lo my_time.lo client_plugin.lo - -# Not needed in the minimum library -mysysobjects2 = my_lib.lo mf_qsort.lo -mysysobjects = $(mysysobjects1) $(mysysobjects2) -target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) \ - $(sql_cmn_objects) $(vio_objects) $(sqlobjects) -target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@ @LIBDL@ -vio_objects= vio.lo viosocket.lo viossl.lo viosslfactories.lo - -BUILT_SOURCES = link_sources - -CLEANFILES = $(target_libadd) $(SHLIBOBJS) \ - $(target) $(BUILT_SOURCES) -DEFS = -DDEFAULT_CHARSET_HOME='"$(MYSQLBASEdir)"' \ - -DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \ - -DDEFAULT_HOME_ENV=MYSQL_HOME \ - -DPLUGINDIR='"$(pkgplugindir)"' \ - -DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \ - -DDEFAULT_SYSCONFDIR='"$(sysconfdir)"' \ - -DSHAREDIR='"$(MYSQLSHAREdir)"' -DDISABLE_DTRACE \ - $(target_defs) - -if HAVE_YASSL -yassl_las = $(top_builddir)/extra/yassl/src/libyassl.la \ - $(top_builddir)/extra/yassl/taocrypt/src/libtaocrypt.la -endif - -# The automatic dependencies miss this -#bmove_upp.lo: $(LTCHARSET_OBJS) - -clean-local: - rm -f `echo $(mystringsobjects) | sed "s;\.lo;.c;g"` \ - `echo $(dbugobjects) | sed "s;\.lo;.c;g"` \ - `echo $(mysysobjects) | sed "s;\.lo;.c;g"` \ - `echo $(vio_objects) | sed "s;\.lo;.c;g"` \ - `echo $(sql_cmn_objects) | sed "s;\.lo;.c;g"` \ - $(CHARSET_SRCS) $(CHARSET_OBJS) \ - $(mystringsextra) $(mysysheaders) $(vioheaders) \ - net.c - -conf_to_src_SOURCES = conf_to_src.c -conf_to_src_LDADD= -#force static linking of conf_to_src - essential when linking against -#custom installation of libc -conf_to_src_LDFLAGS=@NOINST_LDFLAGS@ diff --git a/libmysql/acinclude.m4 b/libmysql/acinclude.m4 deleted file mode 100644 index c0211f3759f..00000000000 --- a/libmysql/acinclude.m4 +++ /dev/null @@ -1,91 +0,0 @@ -# Local macros for automake & autoconf - -AC_DEFUN(MYSQL_TYPE_ACCEPT, -[ac_save_CXXFLAGS="$CXXFLAGS" -AC_CACHE_CHECK([base type of last arg to accept], mysql_cv_btype_last_arg_accept, -AC_LANG_SAVE -AC_LANG_CPLUSPLUS -if test "$ac_cv_prog_gxx" = "yes" -then - CXXFLAGS="$CXXFLAGS -Werror" -fi -mysql_cv_btype_last_arg_accept=none -[AC_TRY_COMPILE([#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -], -[int a = accept(1, (struct sockaddr *) 0, (socklen_t *) 0);], -mysql_cv_btype_last_arg_accept=socklen_t)] -if test $mysql_cv_btype_last_arg_accept = none; then -[AC_TRY_COMPILE([#include <stdlib.h> -#include <sys/types.h> -#include <sys/socket.h> -], -[int a = accept(1, (struct sockaddr *) 0, (size_t *) 0);], -mysql_cv_btype_last_arg_accept=size_t)] -fi -if test $mysql_cv_btype_last_arg_accept = none; then -mysql_cv_btype_last_arg_accept=int -fi) -AC_LANG_RESTORE -AC_DEFINE_UNQUOTED(SOCKET_SIZE_TYPE, $mysql_cv_btype_last_arg_accept) -CXXFLAGS="$ac_save_CXXFLAGS" -]) - - -#---START: Used in for client configure -AC_DEFUN(MYSQL_CHECK_ULONG, -[AC_MSG_CHECKING(for type ulong) -AC_CACHE_VAL(ac_cv_ulong, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - ulong foo; - foo++; - exit(0); -}], ac_cv_ulong=yes, ac_cv_ulong=no, ac_cv_ulong=no)]) -AC_MSG_RESULT($ac_cv_ulong) -if test "$ac_cv_ulong" = "yes" -then - AC_DEFINE(HAVE_ULONG) -fi -]) - -AC_DEFUN(MYSQL_CHECK_UCHAR, -[AC_MSG_CHECKING(for type uchar) -AC_CACHE_VAL(ac_cv_uchar, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - uchar foo; - foo++; - exit(0); -}], ac_cv_uchar=yes, ac_cv_uchar=no, ac_cv_uchar=no)]) -AC_MSG_RESULT($ac_cv_uchar) -if test "$ac_cv_uchar" = "yes" -then - AC_DEFINE(HAVE_UCHAR) -fi -]) - -AC_DEFUN(MYSQL_CHECK_UINT, -[AC_MSG_CHECKING(for type uint) -AC_CACHE_VAL(ac_cv_uint, -[AC_TRY_RUN([#include <stdio.h> -#include <sys/types.h> -main() -{ - uint foo; - foo++; - exit(0); -}], ac_cv_uint=yes, ac_cv_uint=no, ac_cv_uint=no)]) -AC_MSG_RESULT($ac_cv_uint) -if test "$ac_cv_uint" = "yes" -then - AC_DEFINE(HAVE_UINT) -fi -]) - -#---END: diff --git a/libmysql/authentication_win/CMakeLists.txt b/libmysql/authentication_win/CMakeLists.txt new file mode 100644 index 00000000000..80cd14780e6 --- /dev/null +++ b/libmysql/authentication_win/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. +# +# 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 St, Fifth Floor, Boston, MA 02110-1301 USA + +# +# Configuration for building Windows Authentication Plugin (client-side) +# + +ADD_DEFINITIONS(-DSECURITY_WIN32) +ADD_DEFINITIONS(-DDEBUG_ERRROR_LOG) # no error logging in production builds +ADD_DEFINITIONS(-DWINAUTH_USE_DBUG_LIB) # it is OK to use dbug library in statically + # linked plugin + +SET(HEADERS common.h handshake.h) +SET(PLUGIN_SOURCES plugin_client.cc handshake_client.cc log_client.cc common.cc handshake.cc) + +ADD_CONVENIENCE_LIBRARY(auth_win_client ${PLUGIN_SOURCES} ${HEADERS}) +TARGET_LINK_LIBRARIES(auth_win_client Secur32) + +# In IDE, group headers in a separate folder. + +SOURCE_GROUP(Headers REGULAR_EXPRESSION ".*h$") diff --git a/libmysql/authentication_win/common.cc b/libmysql/authentication_win/common.cc new file mode 100644 index 00000000000..1d1f2938969 --- /dev/null +++ b/libmysql/authentication_win/common.cc @@ -0,0 +1,492 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "common.h" +#include <sddl.h> // for ConvertSidToStringSid() +#include <secext.h> // for GetUserNameEx() + + +template <> void error_log_print<error_log_level::INFO>(const char *fmt, ...); +template <> void error_log_print<error_log_level::WARNING>(const char *fmt, ...); +template <> void error_log_print<error_log_level::ERROR>(const char *fmt, ...); + + +/** Connection class **************************************************/ + +/** + Create connection out of an active MYSQL_PLUGIN_VIO object. + + @param[in] vio pointer to a @c MYSQL_PLUGIN_VIO object used for + connection - it can not be NULL +*/ + +Connection::Connection(MYSQL_PLUGIN_VIO *vio): m_vio(vio), m_error(0) +{ + DBUG_ASSERT(vio); +} + + +/** + Write data to the connection. + + @param[in] blob data to be written + + @return 0 on success, VIO error code on failure. + + @note In case of error, VIO error code is stored in the connection object + and can be obtained with @c error() method. +*/ + +int Connection::write(const Blob &blob) +{ + m_error= m_vio->write_packet(m_vio, blob.ptr(), blob.len()); + +#ifndef DBUG_OFF + if (m_error) + DBUG_PRINT("error", ("vio write error %d", m_error)); +#endif + + return m_error; +} + + +/** + Read data from connection. + + @return A Blob containing read packet or null Blob in case of error. + + @note In case of error, VIO error code is stored in the connection object + and can be obtained with @c error() method. +*/ + +Blob Connection::read() +{ + unsigned char *ptr; + int len= m_vio->read_packet(m_vio, &ptr); + + if (len < 0) + { + m_error= true; + return Blob(); + } + + return Blob(ptr, len); +} + + +/** Sid class *****************************************************/ + + +/** + Create Sid object corresponding to a given account name. + + @param[in] account_name name of a Windows account + + The account name can be in any form accepted by @c LookupAccountName() + function. + + @note In case of errors created object is invalid and its @c is_valid() + method returns @c false. +*/ + +Sid::Sid(const wchar_t *account_name): m_data(NULL) +#ifndef DBUG_OFF +, m_as_string(NULL) +#endif +{ + DWORD sid_size= 0, domain_size= 0; + bool success; + + // Determine required buffer sizes + + success= LookupAccountNameW(NULL, account_name, NULL, &sid_size, + NULL, &domain_size, &m_type); + + if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID buffer size, " + "LookupAccountName() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + return; + } + + // Query for SID (domain is ignored) + + wchar_t *domain= new wchar_t[domain_size]; + m_data= (TOKEN_USER*) new BYTE[sid_size + sizeof(TOKEN_USER)]; + m_data->User.Sid= (BYTE*)m_data + sizeof(TOKEN_USER); + + success= LookupAccountNameW(NULL, account_name, + m_data->User.Sid, &sid_size, + domain, &domain_size, + &m_type); + + if (!success || !is_valid()) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID of '%S', " + "LookupAccountName() failed with error %X (%s)", + account_name, GetLastError(), + get_last_error_message(error_buf))); +#endif + goto fail; + } + + goto end; + +fail: + if (m_data) + delete [] m_data; + m_data= NULL; + +end: + if (domain) + delete [] domain; +} + + +/** + Create Sid object corresponding to a given security token. + + @param[in] token security token of a Windows account + + @note In case of errors created object is invalid and its @c is_valid() + method returns @c false. +*/ + +Sid::Sid(HANDLE token): m_data(NULL) +#ifndef DBUG_OFF +, m_as_string(NULL) +#endif +{ + DWORD req_size= 0; + bool success; + + // Determine required buffer size + + success= GetTokenInformation(token, TokenUser, NULL, 0, &req_size); + if (!success && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not determine SID buffer size, " + "GetTokenInformation() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + return; + } + + m_data= (TOKEN_USER*) new BYTE[req_size]; + success= GetTokenInformation(token, TokenUser, m_data, req_size, &req_size); + + if (!success || !is_valid()) + { + delete [] m_data; + m_data= NULL; +#ifndef DBUG_OFF + if (!success) + { + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not read SID from security token, " + "GetTokenInformation() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); + } +#endif + } +} + + +Sid::~Sid() +{ + if (m_data) + delete [] m_data; +#ifndef DBUG_OFF + if (m_as_string) + LocalFree(m_as_string); +#endif +} + +/// Check if Sid object is valid. +bool Sid::is_valid(void) const +{ + return m_data && m_data->User.Sid && IsValidSid(m_data->User.Sid); +} + + +#ifndef DBUG_OFF + +/** + Produces string representation of the SID. + + @return String representation of the SID or NULL in case of errors. + + @note Memory allocated for the string is automatically freed in Sid's + destructor. +*/ + +const char* Sid::as_string() +{ + if (!m_data) + return NULL; + + if (!m_as_string) + { + bool success= ConvertSidToStringSid(m_data->User.Sid, &m_as_string); + + if (!success) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not get textual representation of a SID, " + "ConvertSidToStringSid() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + m_as_string= NULL; + return NULL; + } + } + + return m_as_string; +} + +#endif + + +bool Sid::operator ==(const Sid &other) +{ + if (!is_valid() || !other.is_valid()) + return false; + + return EqualSid(m_data->User.Sid, other.m_data->User.Sid); +} + + +/** Generating User Principal Name *************************/ + +/** + Call Windows API functions to get UPN of the current user and store it + in internal buffer. +*/ + +UPN::UPN(): m_buf(NULL) +{ + wchar_t buf1[MAX_SERVICE_NAME_LENGTH]; + + // First we try to use GetUserNameEx. + + m_len= sizeof(buf1)/sizeof(wchar_t); + + if (!GetUserNameExW(NameUserPrincipal, buf1, (PULONG)&m_len)) + { + if (GetLastError()) + { +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("note", ("When determining UPN" + ", GetUserNameEx() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + if (ERROR_MORE_DATA == GetLastError()) + ERROR_LOG(INFO, ("Buffer overrun when determining UPN:" + " need %ul characters but have %ul", + m_len, sizeof(buf1)/sizeof(WCHAR))); + } + + m_len= 0; // m_len == 0 indicates invalid UPN + return; + } + + /* + UPN is stored in buf1 in wide-char format - convert it to utf8 + for sending over network. + */ + + m_buf= wchar_to_utf8(buf1, &m_len); + + if(!m_buf) + ERROR_LOG(ERROR, ("Failed to convert UPN to utf8")); + + // Note: possible error would be indicated by the fact that m_buf is NULL. + return; +} + + +UPN::~UPN() +{ + if (m_buf) + free(m_buf); +} + + +/** + Convert a wide-char string to utf8 representation. + + @param[in] string null-terminated wide-char string to be converted + @param[in,out] len length of the string to be converted or 0; on + return length (in bytes, excluding terminating + null character) of the converted string + + If len is 0 then the length of the string will be computed by this function. + + @return Pointer to a buffer containing utf8 representation or NULL in + case of error. + + @note The returned buffer must be freed with @c free() call. +*/ + +char* wchar_to_utf8(const wchar_t *string, size_t *len) +{ + char *buf= NULL; + size_t str_len= len && *len ? *len : wcslen(string); + + /* + A conversion from utf8 to wchar_t will never take more than 3 bytes per + character, so a buffer of length 3 * str_len schould be sufficient. + We check that assumption with an assertion later. + */ + + size_t buf_len= 3 * str_len; + + buf= (char*)malloc(buf_len + 1); + if (!buf) + { + DBUG_PRINT("error",("Out of memory when converting string '%S' to utf8", + string)); + return NULL; + } + + int res= WideCharToMultiByte(CP_UTF8, // convert to UTF-8 + 0, // conversion flags + string, // input buffer + str_len, // its length + buf, buf_len, // output buffer and its size + NULL, NULL); // default character (not used) + + if (res) + { + buf[res]= '\0'; + if (len) + *len= res; + return buf; + } + + // res is 0 which indicates error + +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not convert string '%S' to utf8" + ", WideCharToMultiByte() failed with error %X (%s)", + string, GetLastError(), + get_last_error_message(error_buf))); +#endif + + // Let's check our assumption about sufficient buffer size + DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); + + return NULL; +} + + +/** + Convert an utf8 string to a wide-char string. + + @param[in] string null-terminated utf8 string to be converted + @param[in,out] len length of the string to be converted or 0; on + return length (in chars) of the converted string + + If len is 0 then the length of the string will be computed by this function. + + @return Pointer to a buffer containing wide-char representation or NULL in + case of error. + + @note The returned buffer must be freed with @c free() call. +*/ + +wchar_t* utf8_to_wchar(const char *string, size_t *len) +{ + size_t buf_len; + + /* + Note: length (in bytes) of an utf8 string is always bigger than the + number of characters in this string. Hence a buffer of size len will + be sufficient. We add 1 for the terminating null character. + */ + + buf_len= len && *len ? *len : strlen(string); + wchar_t *buf= (wchar_t*)malloc((buf_len+1)*sizeof(wchar_t)); + + if (!buf) + { + DBUG_PRINT("error",("Out of memory when converting utf8 string '%s'" + " to wide-char representation", string)); + return NULL; + } + + size_t res; + res= MultiByteToWideChar(CP_UTF8, // convert from UTF-8 + 0, // conversion flags + string, // input buffer + buf_len, // its size + buf, buf_len); // output buffer and its size + if (res) + { + buf[res]= '\0'; + if (len) + *len= res; + return buf; + } + + // error in MultiByteToWideChar() + +#ifndef DBUG_OFF + Error_message_buf error_buf; + DBUG_PRINT("error", ("Could not convert UPN from UTF-8" + ", MultiByteToWideChar() failed with error %X (%s)", + GetLastError(), get_last_error_message(error_buf))); +#endif + + // Let's check our assumption about sufficient buffer size + DBUG_ASSERT(ERROR_INSUFFICIENT_BUFFER != GetLastError()); + + return NULL; +} + + +/** Error handling ****************************************************/ + + +/** + Returns error message corresponding to the last Windows error given + by GetLastError(). + + @note Error message is overwritten by next call to + @c get_last_error_message(). +*/ + +const char* get_last_error_message(Error_message_buf buf) +{ + int error= GetLastError(); + + buf[0]= '\0'; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)buf, sizeof(buf), NULL ); + + return buf; +} diff --git a/libmysql/authentication_win/common.h b/libmysql/authentication_win/common.h new file mode 100644 index 00000000000..ff0f7153664 --- /dev/null +++ b/libmysql/authentication_win/common.h @@ -0,0 +1,324 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef COMMON_H +#define COMMON_H + +#include <my_global.h> +#include <windows.h> +#include <sspi.h> // for CtxtHandle +#include <mysql/plugin_auth.h> // for MYSQL_PLUGIN_VIO + +/// Maximum length of the target service name. +#define MAX_SERVICE_NAME_LENGTH 1024 + + +/** Debugging and error reporting infrastructure ***************************/ + +/* + Note: We use plugin local logging and error reporting mechanisms until + WL#2940 (plugin service: error reporting) is available. +*/ + +#undef INFO +#undef WARNING +#undef ERROR + +struct error_log_level +{ + typedef enum {INFO, WARNING, ERROR} type; +}; + + +/* + If DEBUG_ERROR_LOG is defined then error logging happens only + in debug-copiled code. Otherwise ERROR_LOG() expands to + error_log_print() even in production code. Note that in client + plugin, error_log_print() will print nothing if opt_auth_win_clinet_log + is 0. + + Note: Macro ERROR_LOG() can use printf-like format string like this: + + ERROR_LOG(Level, ("format string", args)); + + The implementation should handle it correctly. Currently it is passed + to fprintf() (see error_log_vprint() function). +*/ + +extern "C" int opt_auth_win_client_log; + +#if defined(DEBUG_ERROR_LOG) && defined(DBUG_OFF) +#define ERROR_LOG(Level, Msg) do {} while (0) +#else +#define ERROR_LOG(Level, Msg) error_log_print< error_log_level::Level > Msg +#endif + + +void error_log_vprint(error_log_level::type level, + const char *fmt, va_list args); + +template <error_log_level::type Level> +void error_log_print(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + error_log_vprint(Level, fmt, args); + va_end(args); +} + +typedef char Error_message_buf[1024]; +const char* get_last_error_message(Error_message_buf); + + +/* + Internal implementation of debug message printing which does not use + dbug library. This is invoked via macro: + + DBUG_PRINT_DO(Keyword, ("format string", args)); + + This is supposed to be used as an implementation of DBUG_PRINT() macro, + unless the dbug library implementation is used or debug messages are disabled. +*/ + +#ifndef DBUG_OFF + +#define DBUG_PRINT_DO(Keyword, Msg) \ + do { \ + if (2 > opt_auth_win_client_log) break; \ + fprintf(stderr, "winauth: %s: ", Keyword); \ + debug_msg Msg; \ + } while (0) + +inline +void debug_msg(const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); + va_end(args); +} + +#else +#define DBUG_PRINT_DO(K, M) do {} while (0) +#endif + + +#ifndef WINAUTH_USE_DBUG_LIB + +#undef DBUG_PRINT +#define DBUG_PRINT(Keyword, Msg) DBUG_PRINT_DO(Keyword, Msg) + +/* + Redefine few more debug macros to make sure that no symbols from + dbug library are used. +*/ + +#undef DBUG_ENTER +#define DBUG_ENTER(X) do {} while (0) + +#undef DBUG_RETURN +#define DBUG_RETURN(X) return (X) + +#undef DBUG_ASSERT +#ifndef DBUG_OFF +#define DBUG_ASSERT(X) assert (X) +#else +#define DBUG_ASSERT(X) do {} while (0) +#endif + +#undef DBUG_DUMP +#define DBUG_DUMP(A,B,C) do {} while (0) + +#endif + + +/** Blob class *************************************************************/ + +typedef unsigned char byte; + +/** + Class representing a region of memory (e.g., a string or binary buffer). + + @note This class does not allocate memory. It merely describes a region + of memory which must be allocated externally (if it is dynamic memory). +*/ + +class Blob +{ + byte *m_ptr; ///< Pointer to the first byte of the memory region. + size_t m_len; ///< Length of the memory region. + +public: + + Blob(): m_ptr(NULL), m_len(0) + {} + + Blob(const byte *ptr, const size_t len) + : m_ptr(const_cast<byte*>(ptr)), m_len(len) + {} + + Blob(const char *str): m_ptr((byte*)str) + { + m_len= strlen(str); + } + + byte* ptr() const + { + return m_ptr; + } + + size_t len() const + { + return m_len; + } + + byte& operator[](unsigned pos) const + { + static byte out_of_range= 0; // alas, no exceptions... + return pos < len() ? m_ptr[pos] : out_of_range; + } + + bool is_null() const + { + return m_ptr == NULL; + } + + void trim(size_t l) + { + m_len= l; + } +}; + + +/** Connection class *******************************************************/ + +/** + Convenience wrapper around MYSQL_PLUGIN_VIO object providing basic + read/write operations. +*/ + +class Connection +{ + MYSQL_PLUGIN_VIO *m_vio; ///< Pointer to @c MYSQL_PLUGIN_VIO structure. + + /** + If non-zero, indicates that connection is broken. If this has happened + because of failed operation, stores non-zero error code from that failure. + */ + int m_error; + +public: + + Connection(MYSQL_PLUGIN_VIO *vio); + int write(const Blob&); + Blob read(); + + int error() const + { + return m_error; + } +}; + + +/** Sid class **************************************************************/ + +/** + Class for storing and manipulating Windows security identifiers (SIDs). +*/ + +class Sid +{ + TOKEN_USER *m_data; ///< Pointer to structure holding identifier's data. + SID_NAME_USE m_type; ///< Type of identified entity. + +public: + + Sid(const wchar_t*); + Sid(HANDLE sec_token); + ~Sid(); + + bool is_valid(void) const; + + bool is_group(void) const + { + return m_type == SidTypeGroup + || m_type == SidTypeWellKnownGroup + || m_type == SidTypeAlias; + } + + bool is_user(void) const + { + return m_type == SidTypeUser; + } + + bool operator==(const Sid&); + + operator PSID() const + { + return (PSID)m_data->User.Sid; + } + +#ifndef DBUG_OFF + +private: + char *m_as_string; ///< Cached string representation of the SID. +public: + const char* as_string(); + +#endif +}; + + +/** UPN class **************************************************************/ + +/** + An object of this class obtains and stores User Principal Name of the + account under which current process is running. +*/ + +class UPN +{ + char *m_buf; ///< Pointer to UPN in utf8 representation. + size_t m_len; ///< Length of the name. + +public: + + UPN(); + ~UPN(); + + bool is_valid() const + { + return m_len > 0; + } + + const Blob as_blob() const + { + return m_len ? Blob((byte*)m_buf, m_len) : Blob(); + } + + const char* as_string() const + { + return (const char*)m_buf; + } + +}; + + +char* wchar_to_utf8(const wchar_t*, size_t*); +wchar_t* utf8_to_wchar(const char*, size_t*); + +#endif diff --git a/libmysql/authentication_win/handshake.cc b/libmysql/authentication_win/handshake.cc new file mode 100644 index 00000000000..ec665af9ef9 --- /dev/null +++ b/libmysql/authentication_win/handshake.cc @@ -0,0 +1,289 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "handshake.h" + + +/** Handshake class implementation **********************************/ + +/** + Create common part of handshake context. + + @param[in] ssp name of the SSP (Security Service Provider) to + be used for authentication + @param[in] side is this handshake object used for server- or + client-side handshake + + Prepare for handshake using the @c ssp security module. We use + "Negotiate" which picks best available module. Parameter @c side + tells if this is preparing for server or client side authentication + and is used to prepare appropriate credentials. +*/ + +Handshake::Handshake(const char *ssp, side_t side) +: m_atts(0L), m_error(0), m_complete(FALSE), + m_have_credentials(false), m_have_sec_context(false) +#ifndef DBUG_OFF + , m_ssp_info(NULL) +#endif +{ + SECURITY_STATUS ret; + + // Obtain credentials for the authentication handshake. + + ret= AcquireCredentialsHandle(NULL, (SEC_CHAR*)ssp, + side == SERVER ? SECPKG_CRED_INBOUND : SECPKG_CRED_OUTBOUND, + NULL, NULL, NULL, NULL, &m_cred, &m_expire); + + if (ret != SEC_E_OK) + { + DBUG_PRINT("error", ("AcqireCredentialsHandle() failed" + " with error %X", ret)); + ERROR_LOG(ERROR, ("Could not obtain local credentials" + " required for authentication")); + m_error= ret; + } + + m_have_credentials= true; +} + + +Handshake::~Handshake() +{ + if (m_have_credentials) + FreeCredentialsHandle(&m_cred); + if (m_have_sec_context) + DeleteSecurityContext(&m_sctx); + m_output.free(); + +#ifndef DBUG_OFF + if (m_ssp_info) + FreeContextBuffer(m_ssp_info); +#endif +} + + +/** + Read and process data packets from the other end of a connection. + + @param[IN] con a connection to read packets from + + Packets are read and processed until authentication handshake is + complete. It is assumed that the peer will send at least one packet. + Packets are processed with @c process_data() method. If new data is + generated during packet processing, this data is sent to the peer and + another round of packet exchange starts. + + @return 0 on success. + + @note In case of error, appropriate error message is logged. +*/ +int Handshake::packet_processing_loop() +{ + m_round= 0; + + do { + ++m_round; + // Read packet send by the peer + + DBUG_PRINT("info", ("Waiting for packet")); + Blob packet= read_packet(); + if (error()) + { + ERROR_LOG(ERROR, ("Error reading packet in round %d", m_round)); + return 1; + } + DBUG_PRINT("info", ("Got packet of length %d", packet.len())); + + /* + Process received data, possibly generating new data to be sent. + */ + + Blob new_data= process_data(packet); + + if (error()) + { + ERROR_LOG(ERROR, ("Error processing packet in round %d", m_round)); + return 1; + } + + /* + If new data has been generated, send it to the peer. Otherwise + handshake must be completed. + */ + + if (!new_data.is_null()) + { + DBUG_PRINT("info", ("Round %d started", m_round)); + + DBUG_PRINT("info", ("Sending packet of length %d", new_data.len())); + int ret= write_packet(new_data); + if (ret) + { + ERROR_LOG(ERROR, ("Error writing packet in round %d", m_round)); + return 1; + } + DBUG_PRINT("info", ("Data sent")); + } + else if (!is_complete()) + { + ERROR_LOG(ERROR, ("No data to send in round %d" + " but handshake is not complete", m_round)); + return 1; + } + + /* + To protect against malicious clients, break handshake exchange if + too many rounds. + */ + + if (m_round > MAX_HANDSHAKE_ROUNDS) + { + ERROR_LOG(ERROR, ("Authentication handshake could not be completed" + " after %d rounds", m_round)); + return 1; + } + + } while(!is_complete()); + + ERROR_LOG(INFO, ("Handshake completed after %d rounds", m_round)); + return 0; +} + + +#ifndef DBUG_OFF + +/** + Get name of the security package which was used in authentication. + + This method should be called only after handshake was completed. It is + available only in debug builds. + + @return Name of security package or NULL if it can not be obtained. +*/ + +const char* Handshake::ssp_name() +{ + if (!m_ssp_info && m_complete) + { + SecPkgContext_PackageInfo pinfo; + + int ret= QueryContextAttributes(&m_sctx, SECPKG_ATTR_PACKAGE_INFO, &pinfo); + + if (SEC_E_OK == ret) + { + m_ssp_info= pinfo.PackageInfo; + } + else + DBUG_PRINT("error", + ("Could not obtain SSP info from authentication context" + ", QueryContextAttributes() failed with error %X", ret)); + } + + return m_ssp_info ? m_ssp_info->Name : NULL; +} + +#endif + + +/** + Process result of @c {Initialize,Accept}SecurityContext() function. + + @param[in] ret return code from @c {Initialize,Accept}SecurityContext() + function + + This function analyses return value of Windows + @c {Initialize,Accept}SecurityContext() function. A call to + @c CompleteAuthToken() is done if requested. If authentication is complete, + this fact is marked in the internal state of the Handshake object. + If errors are detected the object is moved to error state. + + @return True if error has been detected. +*/ + +bool Handshake::process_result(int ret) +{ + /* + First check for errors and set the m_complete flag if the result + indicates that handshake is complete. + */ + + switch (ret) + { + case SEC_E_OK: + case SEC_I_COMPLETE_NEEDED: + // Handshake completed + m_complete= true; + break; + + case SEC_I_CONTINUE_NEEDED: + case SEC_I_COMPLETE_AND_CONTINUE: + break; + + default: + m_error= ret; + return true; + } + + m_have_sec_context= true; + + /* + If the result indicates a need for this, complete the authentication + token. + */ + + switch (ret) + { + case SEC_I_COMPLETE_NEEDED: + case SEC_I_COMPLETE_AND_CONTINUE: + ret= CompleteAuthToken(&m_sctx, &m_output); + if (ret != 0) + { + DBUG_PRINT("error", ("CompleteAuthToken() failed with error %X", ret)); + m_error= ret; + return true; + } + default: + break; + } + + return false; +} + + +/** Security_buffer class implementation **********************************/ + + +Security_buffer::Security_buffer(const Blob &blob): m_allocated(false) +{ + init(blob.ptr(), blob.len()); +} + + +Security_buffer::Security_buffer(): m_allocated(true) +{ + init(NULL, 0); +} + + +void Security_buffer::free(void) +{ + if (!m_allocated) + return; + if (!ptr()) + return; + FreeContextBuffer(ptr()); + m_allocated= false; +} diff --git a/libmysql/authentication_win/handshake.h b/libmysql/authentication_win/handshake.h new file mode 100644 index 00000000000..5292948b583 --- /dev/null +++ b/libmysql/authentication_win/handshake.h @@ -0,0 +1,181 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef HANDSHAKE_H +#define HANDSHAKE_H + +#include "common.h" + +/** + Name of the SSP (Security Support Provider) to be used for authentication. + + We use "Negotiate" which will find the most secure SSP which can be used + and redirect to that SSP. +*/ +#define SSP_NAME "Negotiate" + +/** + Maximal number of rounds in authentication handshake. + + Server will interrupt authentication handshake with error if client's + identity can not be determined within this many rounds. +*/ +#define MAX_HANDSHAKE_ROUNDS 50 + + +/// Convenience wrapper around @c SecBufferDesc. + +class Security_buffer: public SecBufferDesc +{ + SecBuffer m_buf; ///< A @c SecBuffer instance. + + void init(byte *ptr, size_t len) + { + ulVersion= 0; + cBuffers= 1; + pBuffers= &m_buf; + + m_buf.BufferType= SECBUFFER_TOKEN; + m_buf.pvBuffer= ptr; + m_buf.cbBuffer= len; + } + + /// If @c false, no deallocation will be done in the destructor. + bool m_allocated; + + public: + + Security_buffer(const Blob&); + Security_buffer(); + + ~Security_buffer() + { + free(); + } + + byte* ptr() const + { + return (byte*)m_buf.pvBuffer; + } + + size_t len() const + { + return m_buf.cbBuffer; + } + + bool is_valid() const + { + return ptr() != NULL; + } + + const Blob as_blob() const + { + return Blob(ptr(), len()); + } + + void free(void); +}; + + +/// Common base for Handshake_{server,client}. + +class Handshake +{ +public: + + typedef enum {CLIENT, SERVER} side_t; + + Handshake(const char *ssp, side_t side); + virtual ~Handshake(); + + int Handshake::packet_processing_loop(); + + bool virtual is_complete() const + { + return m_complete; + } + + int error() const + { + return m_error; + } + +protected: + + /// Security context object created during the handshake. + CtxtHandle m_sctx; + + /// Credentials of the principal performing this handshake. + CredHandle m_cred; + + /// Stores expiry date of the created security context. + TimeStamp m_expire; + + /// Stores attributes of the created security context. + ULONG m_atts; + + /** + Round of the handshake (starting from round 1). One round + consist of reading packet from the other side, processing it and + optionally sending a reply (see @c packet_processing_loop()). + */ + unsigned int m_round; + + /// If non-zero, stores error code of the last failed operation. + int m_error; + + /// @c true when handshake is complete. + bool m_complete; + + /// @c true when the principal credentials has been determined. + bool m_have_credentials; + + /// @c true when the security context has been created. + bool m_have_sec_context; + + /// Buffer for data to be send to the other side. + Security_buffer m_output; + + bool process_result(int); + + /** + This method is used inside @c packet_processing_loop to process + data packets received from the other end. + + @param[IN] data data to be processed + + @return A blob with data to be sent to the other end or null blob if + no more data needs to be exchanged. + */ + virtual Blob process_data(const Blob &data) =0; + + /// Read packet from the other end. + virtual Blob read_packet() =0; + + /// Write packet to the other end. + virtual int write_packet(Blob &data) =0; + +#ifndef DBUG_OFF + +private: + SecPkgInfo *m_ssp_info; +public: + const char* ssp_name(); + +#endif +}; + + +#endif diff --git a/libmysql/authentication_win/handshake_client.cc b/libmysql/authentication_win/handshake_client.cc new file mode 100644 index 00000000000..7e89fc92ae7 --- /dev/null +++ b/libmysql/authentication_win/handshake_client.cc @@ -0,0 +1,378 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "handshake.h" + +#include <mysql.h> // for MYSQL structure + + +/// Client-side context for authentication handshake + +class Handshake_client: public Handshake +{ + /** + Name of the server's service for which we authenticate. + + The service name is sent by server in the initial packet. If no + service name is used, this member is @c NULL. + */ + SEC_WCHAR *m_service_name; + + /// Buffer for storing service name obtained from server. + SEC_WCHAR m_service_name_buf[MAX_SERVICE_NAME_LENGTH]; + + Connection &m_con; + +public: + + Handshake_client(Connection &con, const char *target, size_t len); + ~Handshake_client(); + + Blob first_packet(); + Blob process_data(const Blob&); + + Blob read_packet(); + int write_packet(Blob &data); +}; + + +/** + Create authentication handshake context for client. + + @param con connection for communication with the peer + @param target name of the target service with which we will authenticate + (can be NULL if not used) + + Some security packages (like Kerberos) require providing explicit name + of the service with which a client wants to authenticate. The server-side + authentication plugin sends this name in the greeting packet + (see @c win_auth_handshake_{server,client}() functions). +*/ + +Handshake_client::Handshake_client(Connection &con, + const char *target, size_t len) +: Handshake(SSP_NAME, CLIENT), m_service_name(NULL), m_con(con) +{ + if (!target || 0 == len) + return; + + // Convert received UPN to internal WCHAR representation. + + m_service_name= utf8_to_wchar(target, &len); + + if (m_service_name) + DBUG_PRINT("info", ("Using target service: %S\n", m_service_name)); + else + { + /* + Note: we ignore errors here - m_target will be NULL, the target name + will not be used and system will fall-back to NTLM authentication. But + we leave trace in error log. + */ + ERROR_LOG(WARNING, ("Could not decode UPN sent by the server" + "; target service name will not be used" + " and Kerberos authentication will not work")); + } +} + + +Handshake_client::~Handshake_client() +{ + if (m_service_name) + free(m_service_name); +} + + +Blob Handshake_client::read_packet() +{ + /* + We do a fake read in the first round because first + packet from the server containing UPN must be read + before the handshake context is created and the packet + processing loop starts. We return an empty blob here + and process_data() function will ignore it. + */ + if (m_round == 1) + return Blob(); + + // Otherwise we read packet from the connection. + + Blob packet= m_con.read(); + m_error= m_con.error(); + if (!m_error && packet.is_null()) + m_error= true; // (no specific error code assigned) + + if (m_error) + return Blob(); + + DBUG_PRINT("dump", ("Got the following bytes")); + DBUG_DUMP("dump", packet.ptr(), packet.len()); + return packet; +} + + + +int Handshake_client::write_packet(Blob &data) +{ + /* + Length of the first data payload send by client authentication plugin is + limited to 255 bytes (because it is wrapped inside client authentication + packet and is length-encoded with 1 byte for the length). + + If the data payload is longer than 254 bytes, then it is sent in two parts: + first part of length 255 will be embedded in the authentication packet, + second part will be sent in the following packet. Byte 255 of the first + part contains information about the total length of the payload. It is a + number of blocks of size 512 bytes which is sufficient to store the + combined packets. + + Server's logic for reading first client's payload is as follows + (see Handshake_server::read_packet()): + 1. Read data from the authentication packet, if it is shorter than 255 bytes + then that is all data sent by client. + 2. If there is 255 bytes of data in the authentication packet, read another + packet and append it to the data, skipping byte 255 of the first packet + which can be used to allocate buffer of appropriate size. + */ + + size_t len2= 0; // length of the second part of first data payload + byte saved_byte; // for saving byte 255 in which data length is stored + + if (m_round == 1 && data.len() > 254) + { + len2= data.len() - 254; + DBUG_PRINT("info", ("Splitting first packet of length %lu" + ", %lu bytes will be sent in a second part", + data.len(), len2)); + /* + Store in byte 255 the number of 512b blocks that are needed to + keep all the data. + */ + unsigned block_count= data.len()/512 + ((data.len() % 512) ? 1 : 0); + DBUG_ASSERT(block_count < (unsigned)0x100); + saved_byte= data[254]; + data[254] = block_count; + + data.trim(255); + } + + DBUG_PRINT("dump", ("Sending the following data")); + DBUG_DUMP("dump", data.ptr(), data.len()); + int ret= m_con.write(data); + + if (ret) + return ret; + + // Write second part if it is present. + if (len2) + { + data[254]= saved_byte; + Blob data2(data.ptr() + 254, len2); + DBUG_PRINT("info", ("Sending second part of data")); + DBUG_DUMP("info", data2.ptr(), data2.len()); + ret= m_con.write(data2); + } + + return ret; +} + + +/** + Process data sent by server. + + @param[in] data blob with data from server + + This method analyses data sent by server during authentication handshake. + If client should continue packet exchange, this method returns data to + be sent to the server next. If no more data needs to be exchanged, an + empty blob is returned and @c is_complete() is @c true. In case of error + an empty blob is returned and @c error() gives non-zero error code. + + When invoked for the first time (in the first round of the handshake) + there is no data from the server (data blob is null) and the intial + packet is generated without an input. + + @return Data to be sent to the server next or null blob if no more data + needs to be exchanged or in case of error. +*/ + +Blob Handshake_client::process_data(const Blob &data) +{ +#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB) + /* + Code for testing the logic for sending the first client payload. + + A fake data of length given by environment variable TEST_PACKET_LENGTH + (or default 255 bytes) is sent to the server. First 2 bytes of the + payload contain its total length (LSB first). The length of test data + is limited to 2048 bytes. + + Upon receiving test data, server will check that data is correct and + refuse connection. If server detects data errors it will crash on + assertion. + + This code is executed if debug flag "winauth_first_packet_test" is + set, e.g. using client option: + + --debug="d,winauth_first_packet_test" + + The same debug flag must be enabled in the server, e.g. using + statement: + + SET GLOBAL debug= '+d,winauth_first_packet_test'; + */ + + static byte test_buf[2048]; + + if (m_round == 1 + && DBUG_EVALUATE_IF("winauth_first_packet_test", true, false)) + { + const char *env= getenv("TEST_PACKET_LENGTH"); + size_t len= env ? atoi(env) : 0; + if (!len) + len= 255; + if (len > sizeof(test_buf)) + len= sizeof(test_buf); + + // Store data length in first 2 bytes. + byte *ptr= test_buf; + *ptr++= len & 0xFF; + *ptr++= len >> 8; + + // Fill remaining bytes with known values. + for (byte b= 0; ptr < test_buf + len; ++ptr, ++b) + *ptr= b; + + return Blob(test_buf, len); + }; + +#endif + + Security_buffer input(data); + SECURITY_STATUS ret; + + m_output.free(); + + ret= InitializeSecurityContextW( + &m_cred, + m_round == 1 ? NULL : &m_sctx, // partial context + m_service_name, // service name + ASC_REQ_ALLOCATE_MEMORY, // requested attributes + 0, // reserved + SECURITY_NETWORK_DREP, // data representation + m_round == 1 ? NULL : &input, // input data + 0, // reserved + &m_sctx, // context + &m_output, // output data + &m_atts, // attributes + &m_expire); // expire date + + if (process_result(ret)) + { + DBUG_PRINT("error", + ("InitializeSecurityContext() failed with error %X", ret)); + return Blob(); + } + + return m_output.as_blob(); +} + + +/**********************************************************************/ + + +/** + Perform authentication handshake from client side. + + @param[in] vio pointer to @c MYSQL_PLUGIN_VIO instance to be used + for communication with the server + @param[in] mysql pointer to a MySQL connection for which we authenticate + + After reading the initial packet from server, containing its UPN to be + used as service name, client starts packet exchange by sending the first + packet in this exchange. While handshake is not yet completed, client + reads packets sent by the server and process them, possibly generating new + data to be sent to the server. + + This function reports errors. + + @return 0 on success. +*/ + +int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql) +{ + DBUG_ENTER("win_auth_handshake_client"); + + /* + Check if we should enable logging. + */ + { + const char *opt= getenv("AUTHENTICATION_WIN_LOG"); + int opt_val= opt ? atoi(opt) : 0; + if (opt && !opt_val) + { + if (!strncasecmp("on", opt, 2)) opt_val= 1; + if (!strncasecmp("yes", opt, 3)) opt_val= 1; + if (!strncasecmp("true", opt, 4)) opt_val= 1; + if (!strncasecmp("debug", opt, 5)) opt_val= 2; + if (!strncasecmp("dbug", opt, 4)) opt_val= 2; + } + opt_auth_win_client_log= opt_val; + } + + ERROR_LOG(INFO, ("Authentication handshake for account %s", mysql->user)); + + // Create connection object. + + Connection con(vio); + DBUG_ASSERT(!con.error()); + + // Read initial packet from server containing service name. + + Blob service_name= con.read(); + + if (con.error() || service_name.is_null()) + { + ERROR_LOG(ERROR, ("Error reading initial packet")); + DBUG_RETURN(CR_ERROR); + } + DBUG_PRINT("info", ("Got initial packet of length %d", service_name.len())); + + // Create authentication handshake context using the given service name. + + Handshake_client hndshk(con, + service_name[0] ? (char *)service_name.ptr() : NULL, + service_name.len()); + if (hndshk.error()) + { + ERROR_LOG(ERROR, ("Could not create authentication handshake context")); + DBUG_RETURN(CR_ERROR); + } + + DBUG_ASSERT(!hndshk.error()); + + /* + Read and process packets from server until handshake is complete. + Note that the first read from server is dummy + (see Handshake_client::read_packet()) as we already have read the + first packet to establish service name. + */ + if (hndshk.packet_processing_loop()) + DBUG_RETURN(CR_ERROR); + + DBUG_ASSERT(!hndshk.error() && hndshk.is_complete()); + + DBUG_RETURN(CR_OK); +} diff --git a/libmysql/authentication_win/log_client.cc b/libmysql/authentication_win/log_client.cc new file mode 100644 index 00000000000..df4ce4f9c2a --- /dev/null +++ b/libmysql/authentication_win/log_client.cc @@ -0,0 +1,55 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include "common.h" + +/** + This option is set in win_auth_handshake_client() function + in handshake_client.cc. + + Values: + 0 - no logging + 1 - log error/warning/info messages + 2 - also log debug messages + + Note: No error or debug messages are logged in production code + (see logging macros in common.h). +*/ +int opt_auth_win_client_log= 0; + + +// Client-side logging function + +void error_log_vprint(error_log_level::type level, + const char *fmt, va_list args) +{ + if (0 == opt_auth_win_client_log) + return; + + const char *level_string= ""; + + switch (level) + { + case error_log_level::INFO: level_string= "Note"; break; + case error_log_level::WARNING: level_string= "Warning"; break; + case error_log_level::ERROR: level_string= "ERROR"; break; + } + + fprintf(stderr, "Windows Authentication Plugin %s: ", level_string); + vfprintf(stderr, fmt, args); + fputc('\n', stderr); + fflush(stderr); +} diff --git a/libmysql/authentication_win/plugin_client.cc b/libmysql/authentication_win/plugin_client.cc new file mode 100644 index 00000000000..30dc5b1493d --- /dev/null +++ b/libmysql/authentication_win/plugin_client.cc @@ -0,0 +1,68 @@ +/* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include <my_global.h> +#include <mysql.h> +#include <mysql/plugin_auth.h> +#include <mysql/client_plugin.h> + +#include "common.h" + +/* + The following MS C++ specific pragma embeds a comment in the resulting + object file. A "lib" comment tells the linker to use the specified + library, thus the dependency is handled automagically. +*/ + +#ifdef _MSC_VER +#pragma comment(lib, "Secur32") +#endif + +static int win_auth_client_plugin_init(char*, size_t, int, va_list) +{ + return 0; +} + + +static int win_auth_client_plugin_deinit() +{ + return 0; +} + + +int win_auth_handshake_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql); + + +/* + Client plugin declaration. This is added to mysql_client_builtins[] + in sql-common/client.c +*/ + +extern "C" +st_mysql_client_plugin_AUTHENTICATION win_auth_client_plugin= +{ + MYSQL_CLIENT_AUTHENTICATION_PLUGIN, + MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION, + "authentication_windows_client", + "Rafal Somla", + "Windows Authentication Plugin - client side", + {0,1,0}, + "GPL", + NULL, + win_auth_client_plugin_init, + win_auth_client_plugin_deinit, + NULL, // option handling + win_auth_handshake_client +}; diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index 5204d03e5af..c5a08ce1fcc 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -35,21 +35,6 @@ sig_handler my_pipe_sig_handler(int sig); void read_user_name(char *name); my_bool handle_local_infile(MYSQL *mysql, const char *net_filename); -/* - Let the user specify that we don't want SIGPIPE; This doesn't however work - with threaded applications as we can have multiple read in progress. -*/ - -#if !defined(__WIN__) && defined(SIGPIPE) && !defined(THREAD) -#define init_sigpipe_variables sig_return old_signal_handler=(sig_return) 0; -#define set_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) old_signal_handler=signal(SIGPIPE, my_pipe_sig_handler) -#define reset_sigpipe(mysql) if ((mysql)->client_flag & CLIENT_IGNORE_SIGPIPE) signal(SIGPIPE,old_signal_handler); -#else -#define init_sigpipe_variables -#define set_sigpipe(mysql) -#define reset_sigpipe(mysql) -#endif - void mysql_read_default_options(struct st_mysql_options *options, const char *filename,const char *group); void mysql_detach_stmt_list(LIST **stmt_list, const char *func_name); diff --git a/libmysql/conf_to_src.c b/libmysql/conf_to_src.c index 785e3cad4c1..f39a2e1856f 100644 --- a/libmysql/conf_to_src.c +++ b/libmysql/conf_to_src.c @@ -5,8 +5,7 @@ the Free Software Foundation. There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. View the full text of the exception in file - EXCEPTIONS-CLIENT in the directory of this software distribution. + is applied to this software. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 823f83026c9..a2d066b1eba 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -5,8 +5,7 @@ the Free Software Foundation. There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. View the full text of the exception in file - EXCEPTIONS-CLIENT in the directory of this software distribution. + is applied to this software. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -52,7 +51,7 @@ const char *client_errors[]= "Error on SHOW SLAVE HOSTS:", "Error connecting to slave:", "Error connecting to master:", - "SSL connection error", + "SSL connection error: %-.100s", "Malformed packet", "This client library is licensed only for use with MySQL servers having '%s' license", "Invalid use of null pointer", diff --git a/libmysql/get_password.c b/libmysql/get_password.c index 56514a8d864..455b4b3c8f1 100644 --- a/libmysql/get_password.c +++ b/libmysql/get_password.c @@ -5,8 +5,7 @@ the Free Software Foundation. There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. View the full text of the exception in file - EXCEPTIONS-CLIENT in the directory of this software distribution. + is applied to this software. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index f5286b8fa50..1af15c75d7f 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -5,8 +5,7 @@ the Free Software Foundation. There are special exceptions to the terms and conditions of the GPL as it - is applied to this software. View the full text of the exception in file - EXCEPTIONS-CLIENT in the directory of this software distribution. + is applied to this software. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -52,7 +51,7 @@ #ifdef HAVE_SYS_UN_H #include <sys/un.h> #endif -#if defined(THREAD) && !defined(__WIN__) +#if !defined(__WIN__) #include <my_pthread.h> /* because of signal() */ #endif #ifndef INADDR_NONE @@ -95,6 +94,11 @@ sig_handler my_pipe_sig_handler(int sig); static my_bool mysql_client_init= 0; static my_bool org_my_init_done= 0; +typedef struct st_mysql_stmt_extension +{ + MEM_ROOT fields_mem_root; +} MYSQL_STMT_EXT; + /* Initialize the MySQL client library @@ -173,10 +177,8 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)), result= init_embedded_server(argc, argv, groups); #endif } -#ifdef THREAD else result= (int)my_thread_init(); /* Init if new thread */ -#endif return result; } @@ -230,18 +232,12 @@ MYSQL_PARAMETERS *STDCALL mysql_get_parameters(void) my_bool STDCALL mysql_thread_init() { -#ifdef THREAD return my_thread_init(); -#else - return 0; -#endif } void STDCALL mysql_thread_end() { -#ifdef THREAD my_thread_end(); -#endif } @@ -1106,11 +1102,7 @@ void STDCALL mysql_get_character_set_info(MYSQL *mysql, MY_CHARSET_INFO *csinfo) uint STDCALL mysql_thread_safe(void) { -#ifdef THREAD return 1; -#else - return 0; -#endif } @@ -1506,11 +1498,16 @@ mysql_stmt_init(MYSQL *mysql) MYSQL_STMT *stmt; DBUG_ENTER("mysql_stmt_init"); - if (!(stmt= (MYSQL_STMT *) my_malloc(sizeof(MYSQL_STMT), + if (!(stmt= + (MYSQL_STMT *) my_malloc(sizeof (MYSQL_STMT), + MYF(MY_WME | MY_ZEROFILL))) || + !(stmt->extension= + (MYSQL_STMT_EXT *) my_malloc(sizeof (MYSQL_STMT_EXT), MYF(MY_WME | MY_ZEROFILL)))) { set_mysql_error(mysql, CR_OUT_OF_MEMORY, unknown_sqlstate); - DBUG_RETURN(0); + my_free(stmt); + DBUG_RETURN(NULL); } init_alloc_root(&stmt->mem_root, 2048, 2048); @@ -1525,6 +1522,8 @@ mysql_stmt_init(MYSQL *mysql) strmov(stmt->sqlstate, not_error_sqlstate); /* The rest of statement members was bzeroed inside malloc */ + init_alloc_root(&stmt->extension->fields_mem_root, 2048, 0); + DBUG_RETURN(stmt); } @@ -1597,6 +1596,7 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) stmt->bind_param_done= stmt->bind_result_done= FALSE; stmt->param_count= stmt->field_count= 0; free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC)); + free_root(&stmt->extension->fields_mem_root, MYF(0)); int4store(buff, stmt->stmt_id); @@ -1657,21 +1657,21 @@ mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, ulong length) static void alloc_stmt_fields(MYSQL_STMT *stmt) { MYSQL_FIELD *fields, *field, *end; - MEM_ROOT *alloc= &stmt->mem_root; + MEM_ROOT *fields_mem_root= &stmt->extension->fields_mem_root; MYSQL *mysql= stmt->mysql; - DBUG_ASSERT(mysql->field_count); + DBUG_ASSERT(stmt->field_count); - stmt->field_count= mysql->field_count; + free_root(fields_mem_root, MYF(0)); /* Get the field information for non-select statements like SHOW and DESCRIBE commands */ - if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(alloc, + if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(fields_mem_root, sizeof(MYSQL_FIELD) * stmt->field_count)) || - !(stmt->bind= (MYSQL_BIND *) alloc_root(alloc, + !(stmt->bind= (MYSQL_BIND *) alloc_root(fields_mem_root, sizeof(MYSQL_BIND) * stmt->field_count))) { @@ -1684,18 +1684,36 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt) field && fields < end; fields++, field++) { *field= *fields; /* To copy all numeric parts. */ - field->catalog= strmake_root(alloc, fields->catalog, + field->catalog= strmake_root(fields_mem_root, + fields->catalog, fields->catalog_length); - field->db= strmake_root(alloc, fields->db, fields->db_length); - field->table= strmake_root(alloc, fields->table, fields->table_length); - field->org_table= strmake_root(alloc, fields->org_table, + field->db= strmake_root(fields_mem_root, + fields->db, + fields->db_length); + field->table= strmake_root(fields_mem_root, + fields->table, + fields->table_length); + field->org_table= strmake_root(fields_mem_root, + fields->org_table, fields->org_table_length); - field->name= strmake_root(alloc, fields->name, fields->name_length); - field->org_name= strmake_root(alloc, fields->org_name, + field->name= strmake_root(fields_mem_root, + fields->name, + fields->name_length); + field->org_name= strmake_root(fields_mem_root, + fields->org_name, fields->org_name_length); - field->def= fields->def ? strmake_root(alloc, fields->def, - fields->def_length) : 0; - field->def_length= field->def ? fields->def_length : 0; + if (fields->def) + { + field->def= strmake_root(fields_mem_root, + fields->def, + fields->def_length); + field->def_length= fields->def_length; + } + else + { + field->def= NULL; + field->def_length= 0; + } field->extension= 0; /* Avoid dangling links. */ field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */ } @@ -2413,6 +2431,9 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt) prepared statements can't send result set metadata for these queries on prepare stage. Read it now. */ + + stmt->field_count= stmt->mysql->field_count; + alloc_stmt_fields(stmt); } else @@ -2430,7 +2451,7 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt) previous branch always works. TODO: send metadata only when it's really necessary and add a warning 'Metadata changed' when it's sent twice. - */ + */ update_stmt_fields(stmt); } } @@ -4631,6 +4652,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) free_root(&stmt->result.alloc, MYF(0)); free_root(&stmt->mem_root, MYF(0)); + free_root(&stmt->extension->fields_mem_root, MYF(0)); if (mysql) { @@ -4665,6 +4687,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt) } } + my_free(stmt->extension); my_free(stmt); DBUG_RETURN(test(rc)); @@ -4831,16 +4854,13 @@ int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt) stmt->state= MYSQL_STMT_EXECUTE_DONE; stmt->bind_result_done= FALSE; + stmt->field_count= mysql->field_count; if (mysql->field_count) { alloc_stmt_fields(stmt); prepare_to_fetch_result(stmt); } - else - { - stmt->field_count= mysql->field_count; - } DBUG_RETURN(0); } |