mirror of
https://github.com/MariaDB/server.git
synced 2025-07-30 16:24:05 +03:00
5.5-merge
This commit is contained in:
@ -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()
|
||||
|
@ -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
|
@ -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@
|
@ -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:
|
33
libmysql/authentication_win/CMakeLists.txt
Normal file
33
libmysql/authentication_win/CMakeLists.txt
Normal file
@ -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$")
|
492
libmysql/authentication_win/common.cc
Normal file
492
libmysql/authentication_win/common.cc
Normal file
@ -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;
|
||||
}
|
324
libmysql/authentication_win/common.h
Normal file
324
libmysql/authentication_win/common.h
Normal file
@ -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
|
289
libmysql/authentication_win/handshake.cc
Normal file
289
libmysql/authentication_win/handshake.cc
Normal file
@ -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;
|
||||
}
|
181
libmysql/authentication_win/handshake.h
Normal file
181
libmysql/authentication_win/handshake.h
Normal file
@ -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
|
378
libmysql/authentication_win/handshake_client.cc
Normal file
378
libmysql/authentication_win/handshake_client.cc
Normal file
@ -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);
|
||||
}
|
55
libmysql/authentication_win/log_client.cc
Normal file
55
libmysql/authentication_win/log_client.cc
Normal file
@ -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);
|
||||
}
|
68
libmysql/authentication_win/plugin_client.cc
Normal file
68
libmysql/authentication_win/plugin_client.cc
Normal file
@ -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
|
||||
};
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user