summaryrefslogtreecommitdiffstats
path: root/plugin/win_auth_client
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 18:07:14 +0000
commita175314c3e5827eb193872241446f2f8f5c9d33c (patch)
treecd3d60ca99ae00829c52a6ca79150a5b6e62528b /plugin/win_auth_client
parentInitial commit. (diff)
downloadmariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.tar.xz
mariadb-10.5-a175314c3e5827eb193872241446f2f8f5c9d33c.zip
Adding upstream version 1:10.5.12.upstream/1%10.5.12upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugin/win_auth_client')
-rw-r--r--plugin/win_auth_client/CMakeLists.txt34
-rw-r--r--plugin/win_auth_client/common.cc510
-rw-r--r--plugin/win_auth_client/common.h324
-rw-r--r--plugin/win_auth_client/handshake.cc289
-rw-r--r--plugin/win_auth_client/handshake.h181
-rw-r--r--plugin/win_auth_client/handshake_client.cc393
-rw-r--r--plugin/win_auth_client/log_client.cc65
-rw-r--r--plugin/win_auth_client/plugin_client.cc68
8 files changed, 1864 insertions, 0 deletions
diff --git a/plugin/win_auth_client/CMakeLists.txt b/plugin/win_auth_client/CMakeLists.txt
new file mode 100644
index 00000000..46f837e5
--- /dev/null
+++ b/plugin/win_auth_client/CMakeLists.txt
@@ -0,0 +1,34 @@
+# Copyright (c) 2011, 2013, 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-1335 USA
+
+IF(WIN32)
+ #
+ # 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)
+
+ MYSQL_ADD_PLUGIN(authentication_windows_client ${PLUGIN_SOURCES} ${HEADERS}
+ LINK_LIBRARIES Secur32
+ MODULE_ONLY COMPONENT ClientPlugins CLIENT)
+
+ENDIF(WIN32)
diff --git a/plugin/win_auth_client/common.cc b/plugin/win_auth_client/common.cc
new file mode 100644
index 00000000..8b731925
--- /dev/null
+++ b/plugin/win_auth_client/common.cc
@@ -0,0 +1,510 @@
+/* Copyright (c) 2011, 2019, 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 Street, Fifth Floor, Boston, MA 02110-1335 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, ...);
+
+/**
+ Option indicating desired level of logging. Values:
+
+ 0 - no logging
+ 1 - log only error messages
+ 2 - additionally log warnings
+ 3 - additionally log info notes
+ 4 - also log debug messages
+
+ Value of this option should be taken into account in the
+ implementation of error_log_vprint() function (see
+ log_client.cc).
+
+ Note: No error or debug messages are logged in production code
+ (see logging macros in common.h).
+*/
+int opt_auth_win_log_level= 2;
+
+
+/** 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(), (int)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 should 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
+ (int)str_len, // its length
+ buf, (int)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
+ (int)buf_len, // its size
+ buf, (int)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, ERRMSG_BUFSIZE , NULL );
+
+ return buf;
+}
diff --git a/plugin/win_auth_client/common.h b/plugin/win_auth_client/common.h
new file mode 100644
index 00000000..8aeb1cd2
--- /dev/null
+++ b/plugin/win_auth_client/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 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;
+};
+
+extern "C" int opt_auth_win_log_level;
+unsigned int get_log_level(void);
+void set_log_level(unsigned int);
+
+
+/*
+ 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: 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).
+*/
+
+#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);
+}
+#define ERRMSG_BUFSIZE 1024
+typedef char Error_message_buf[ERRMSG_BUFSIZE];
+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 (4 > get_log_level()) 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/plugin/win_auth_client/handshake.cc b/plugin/win_auth_client/handshake.cc
new file mode 100644
index 00000000..cd63ee26
--- /dev/null
+++ b/plugin/win_auth_client/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 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/plugin/win_auth_client/handshake.h b/plugin/win_auth_client/handshake.h
new file mode 100644
index 00000000..2eac0afb
--- /dev/null
+++ b/plugin/win_auth_client/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 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= (ULONG)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 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/plugin/win_auth_client/handshake_client.cc b/plugin/win_auth_client/handshake_client.cc
new file mode 100644
index 00000000..28a228cc
--- /dev/null
+++ b/plugin/win_auth_client/handshake_client.cc
@@ -0,0 +1,393 @@
+/* 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 Street, Fifth Floor, Boston, MA 02110-1335 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= (uint)(data.len()/512) + ((data.len() % 512) ? 1 : 0);
+
+#if !defined(DBUG_OFF) && defined(WINAUTH_USE_DBUG_LIB)
+
+ /*
+ For testing purposes, use wrong block count to see how server
+ handles this.
+ */
+ DBUG_EXECUTE_IF("winauth_first_packet_test",{
+ block_count= data.len() == 601 ? 0 :
+ data.len() == 602 ? 1 :
+ block_count;
+ });
+
+#endif
+
+ 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 initial
+ 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-dbug="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= 2;
+ if (!strncasecmp("yes", opt, 3)) opt_val= 2;
+ if (!strncasecmp("true", opt, 4)) opt_val= 2;
+ if (!strncasecmp("debug", opt, 5)) opt_val= 4;
+ if (!strncasecmp("dbug", opt, 4)) opt_val= 4;
+ }
+ set_log_level(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/plugin/win_auth_client/log_client.cc b/plugin/win_auth_client/log_client.cc
new file mode 100644
index 00000000..2e878510
--- /dev/null
+++ b/plugin/win_auth_client/log_client.cc
@@ -0,0 +1,65 @@
+/* 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 Street, Fifth Floor, Boston, MA 02110-1335 USA */
+
+#include <my_global.h>
+#include "common.h"
+
+
+// Client-side logging function
+
+void error_log_vprint(error_log_level::type level,
+ const char *fmt, va_list args)
+{
+ const char *level_string= "";
+ int log_level= get_log_level();
+
+ switch (level)
+ {
+ case error_log_level::INFO:
+ if (3 > log_level)
+ return;
+ level_string= "Note";
+ break;
+ case error_log_level::WARNING:
+ if (2 > log_level)
+ return;
+ level_string= "Warning";
+ break;
+ case error_log_level::ERROR:
+ if (1 > log_level)
+ return;
+ level_string= "ERROR";
+ break;
+ }
+
+ fprintf(stderr, "Windows Authentication Plugin %s: ", level_string);
+ vfprintf(stderr, fmt, args);
+ fputc('\n', stderr);
+ fflush(stderr);
+}
+
+
+// Trivial implementation of log-level setting storage.
+
+void set_log_level(unsigned int level)
+{
+ opt_auth_win_log_level= level;
+}
+
+
+unsigned int get_log_level(void)
+{
+ return opt_auth_win_log_level;
+}
diff --git a/plugin/win_auth_client/plugin_client.cc b/plugin/win_auth_client/plugin_client.cc
new file mode 100644
index 00000000..37bd1bc5
--- /dev/null
+++ b/plugin/win_auth_client/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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 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
+};