[Commits] e6898d1: MDEV-4691 : GSSAPI / SSPI plugin

wlad at mariadb.com wlad at mariadb.com
Fri Dec 25 23:39:57 EET 2015


revision-id: e6898d1044a435e3dfd37e5c73cf4b75af5dcade (mariadb-10.1.10-1-ge6898d1)
parent(s): 8efdfc8b58a84f8e8d62f0bb8b31f5b763664c06
committer: Vladislav Vaintroub
timestamp: 2015-12-25 22:39:16 +0100
message:

MDEV-4691 : GSSAPI / SSPI plugin

---
 plugin/auth_gssapi/CMakeLists.txt                  |  33 +++
 plugin/auth_gssapi/README.md                       | 129 +++++++++
 plugin/auth_gssapi/client_plugin.cc                | 112 ++++++++
 plugin/auth_gssapi/cmake/FindGSSAPI.cmake          |  78 ++++++
 plugin/auth_gssapi/common.h                        |   4 +
 plugin/auth_gssapi/gssapi_client.cc                | 127 +++++++++
 plugin/auth_gssapi/gssapi_errmsg.cc                |  75 +++++
 plugin/auth_gssapi/gssapi_errmsg.h                 |  29 ++
 plugin/auth_gssapi/gssapi_server.cc                | 248 ++++++++++++++++
 .../mysql-test/auth_gssapi/basic.result            |  18 ++
 .../auth_gssapi/mysql-test/auth_gssapi/basic.test  |  45 +++
 .../auth_gssapi/mysql-test/auth_gssapi/suite.opt   |   1 +
 plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm |  47 ++++
 plugin/auth_gssapi/server_plugin.cc                | 175 ++++++++++++
 plugin/auth_gssapi/server_plugin.h                 |  51 ++++
 plugin/auth_gssapi/sspi.h                          |  38 +++
 plugin/auth_gssapi/sspi_client.cc                  | 183 ++++++++++++
 plugin/auth_gssapi/sspi_errmsg.cc                  | 150 ++++++++++
 plugin/auth_gssapi/sspi_server.cc                  | 312 +++++++++++++++++++++
 19 files changed, 1855 insertions(+)

diff --git a/plugin/auth_gssapi/CMakeLists.txt b/plugin/auth_gssapi/CMakeLists.txt
new file mode 100644
index 0000000..e20be70
--- /dev/null
+++ b/plugin/auth_gssapi/CMakeLists.txt
@@ -0,0 +1,33 @@
+IF (WIN32)
+ SET(USE_SSPI 1)
+ENDIF()
+
+IF(USE_SSPI)
+  SET(GSSAPI_LIBRARIES secur32)
+  ADD_DEFINITIONS(-DPLUGIN_SSPI)
+  SET(GSSAPI_CLIENT sspi_client.cc)
+  SET(GSSAPI_SERVER sspi_server.cc)
+  SET(GSSAPI_ERRMSG sspi_errmsg.cc)
+ELSE()
+ SET(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
+ FIND_PACKAGE(GSSAPI)
+ IF(GSSAPI_FOUND)
+   INCLUDE_DIRECTORIES(${GSSAPI_INCS})
+   ADD_DEFINITIONS(-DPLUGIN_GSSAPI)
+   SET(GSSAPI_CLIENT gssapi_client.cc)
+   SET(GSSAPI_SERVER gssapi_server.cc)
+   SET(GSSAPI_ERRMSG gssapi_errmsg.cc)
+ ELSE()
+   # Can't build plugin
+   RETURN()
+ ENDIF()
+ENDIF ()
+
+
+MYSQL_ADD_PLUGIN(auth_gssapi server_plugin.cc ${GSSAPI_SERVER} ${GSSAPI_ERRMSG}
+                 LINK_LIBRARIES ${GSSAPI_LIBS}
+                 MODULE_ONLY)
+
+MYSQL_ADD_PLUGIN(auth_gssapi_client client_plugin.cc ${GSSAPI_CLIENT} ${GSSAPI_ERRMSG}
+                 LINK_LIBRARIES ${GSSAPI_LIBS}
+                 MODULE_ONLY)
diff --git a/plugin/auth_gssapi/README.md b/plugin/auth_gssapi/README.md
new file mode 100644
index 0000000..f20128f
--- /dev/null
+++ b/plugin/auth_gssapi/README.md
@@ -0,0 +1,129 @@
+# GSSAPI/SSPI authentication for MariaDB
+
+This article gives instructions on configuring GSSAPI authentication plugin
+for MariaDB for passwordless login.
+
+On Unix systems, GSSAPI is usually synonymous with Kerberos authentication.
+Windows has slightly different but very similar API called SSPI,  that along with Kerberos, also supports NTLM authentication.
+
+This plugin includes support for Kerberos on Unix, but also can be used as for Windows authentication with or without domain
+environment.
+
+## Server-side preparations on Unix
+To use the plugin, some preparation need to be done on the server side on Unixes.
+MariaDB server will read need access to the Kerberos keytab file, that contains  service principal name for the MariaDB server.
+
+
+If you are using **Unix Kerberos KDC (MIT,Heimdal)**
+
+-	Create service principal using kadmin tool
+
+```
+kadmin -q "addprinc -randkey mariadb/host.domain.com"
+```
+
+(replace host.domain.com with fully qualified DNS name for the server host)
+
+-	Export the newly created user to the keytab file
+
+```
+kadmin -q "ktadd -k /path/to/mariadb.keytab mariadb/host.domain.com"
+```
+
+More details can be found [here](http://www.microhowto.info/howto/create_a_service_principal_using_mit_kerberos.html)
+and [here](http://www.microhowto.info/howto/add_a_host_or_service_principal_to_a_keytab_using_mit_kerberos.html)
+
+If you are using **Windows Active Directory KDC**
+you can need to create keytab using ktpass.exe tool on Windows,  map principal user to an existing domain user like this
+
+```
+ktpass.exe /princ mariadb/host.domain.com at DOMAIN.COM /mapuser someuser /pass MyPas$w0rd /out mariadb.keytab /crypto all /ptype KRB5_NT_PRINCIPAL /mapop set
+```
+
+and then transfer the keytab file to the Unix server. See [Microsoft documentation](https://technet.microsoft.com/en-us/library/cc753771.aspx) for details.
+
+
+## Server side preparations on Windows.
+Usually nothing need to be done.  MariaDB server should to run on a domain joined machine, either as NetworkService account
+(which is default if it runs as service) or run under any other domain account credentials.
+Creating service principal is not required here (but you can still do it using [_setspn_](https://technet.microsoft.com/en-us/library/cc731241.aspx) tool)
+
+
+# Installing plugin
+-	Start the server
+
+-	On Unix, edit my the my.cnf/my.ini configuration file, set the parameter gssapi-keytab-path to point to previously
+created keytab path.
+
+```
+	gssapi-keytab-path=/path/to/mariadb.keytab
+```
+
+-	Optionally on Unix, in case the service principal name differs from default mariadb/host.domain.com at REALM,
+configure alternative principal name with
+
+```
+    gssapi-principal-name=alternative/principalname at REALM
+```
+
+-	In mysql command line client, execute
+
+```
+	INSTALL SONAME 'auth_gssapi'
+```
+
+#Creating users
+
+Now, you can create a user for GSSAPI/SSPI authentication. CREATE USER command, for Kerberos user
+would be like this (*long* form, see below for short one)
+
+```
+CREATE USER usr1 IDENTIFIED WITH gssapi AS 'usr1 at EXAMPLE.COM';
+```
+
+(replace  with real username and realm)
+
+The part after AS is mechanism specific, and needs to be ``machine\\usr1`` for Windows users identified with NTLM.
+
+You may also use alternative *short* form of CREATE USER
+
+```
+CREATE USER usr1 IDENTIFIED WITH gssapi;
+```
+
+If this syntax is used, realm part is used for comparison
+thus 'usr1 at EXAMPLE.COM', 'usr1 at EXAMPLE.CO.UK' and 'mymachine\usr1' will all identify as 'usr1'.
+
+#Login as GSSAPI user with command line clients
+
+Using command line client, do
+
+```
+mysql --plugin-dir=/path/to/plugin-dir -u usr1
+```
+
+#Plugin variables
+-	**gssapi-keytab-path** (Unix only) - Path to the server keytab file
+-	**gssapi-principal-name** - name of the service principal.
+-	**gssapi-mech-name** (Windows only) - Name of the SSPI package used by server. Can be either 'Kerberos' or 'Negotiate'.
+ Defaults to 'Negotiate' (both Kerberos and NTLM users can connect)
+ Set it to 'Kerberos', to prevent less secure NTLM in domain environments,  but leave it as default(Negotiate)
+ to allow non-domain environment (e.g if server does not run in domain enviroment).
+
+
+#Implementation
+
+Overview of the protocol between client and server
+
+1. Server : Construct gssapi-principal-name if not set in my.cnf. On Unixes defaults to hostbased name for service "mariadb". On Windows to user's or machine's domain names.
+Acquire credentials for gssapi-principal-name with ```gss_acquire_cred() / AcquireSecurityCredentials()```.
+Send packet with principal name and mech ```"gssapi-principal-name\0gssapi-mech-name\0"``` to client ( on Unix, empty string used for gssapi-mech)
+
+2. Client: execute ```gss_init_sec_context() / InitializeSecurityContext()``` passing gssapi-principal-name / gssapi-mech-name parameters.
+Send resulting GSSAPI blob to server.
+
+3. Server : receive blob from client, execute ```gss_accept_sec_context()/ AcceptSecurityContext()```, send resulting blob back to client
+
+4. Perform  2. and 3. can until both client and server decide that authentication is done, or until some error occured. If authentication was successful, GSSAPI context (an opaque structure) is generated on both client and server sides.
+
+5. Server : Client name is extracted from the context, and compared to the name provided by client(with or without realm). If name matches, plugin returns success.
diff --git a/plugin/auth_gssapi/client_plugin.cc b/plugin/auth_gssapi/client_plugin.cc
new file mode 100644
index 0000000..aac80e0
--- /dev/null
+++ b/plugin/auth_gssapi/client_plugin.cc
@@ -0,0 +1,112 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+  @file
+
+  GSSAPI authentication plugin, client side
+*/
+#include <string.h>
+#include <stdarg.h>
+#include <mysqld_error.h>
+#include <mysql/client_plugin.h>
+#include <mysql.h>
+#include <stdio.h>
+#include "common.h"
+
+extern int auth_client(char *principal_name,
+                       char *mech,
+                       MYSQL *mysql,
+                       MYSQL_PLUGIN_VIO *vio);
+
+static void parse_server_packet(char *packet, size_t packet_len, char *spn, char *mech)
+{
+  size_t spn_len;
+  spn_len = strnlen(packet, packet_len);
+  strncpy(spn, packet, PRINCIPAL_NAME_MAX);
+  if (spn_len == packet_len - 1)
+  {
+    /* Mechanism not included into packet */
+    *mech = 0;
+  }
+  else
+  {
+    strncpy(mech, packet + spn_len + 1, MECH_NAME_MAX);
+  }
+}
+
+/**
+  Set client error message.
+ */
+void log_client_error(MYSQL *mysql,  const char *format, ...)
+{
+  NET *net= &mysql->net;
+  va_list args;
+
+  net->last_errno= ER_UNKNOWN_ERROR;
+  va_start(args, format);
+  vsnprintf(net->last_error, sizeof(net->last_error) - 1,
+          format, args);
+  va_end(args);
+  memcpy(net->sqlstate, "HY000", sizeof(net->sqlstate));
+}
+
+/**
+  The main client function of the GSSAPI plugin.
+ */
+static int gssapi_auth_client(MYSQL_PLUGIN_VIO *vio, MYSQL *mysql)
+{
+  int packet_len;
+  unsigned char *packet;
+  char spn[PRINCIPAL_NAME_MAX + 1];
+  char mech[MECH_NAME_MAX + 1];
+
+  /* read from server for service principal name */
+  packet_len= vio->read_packet(vio, &packet);
+  if (packet_len < 0)
+  {
+    return CR_ERROR;
+  }
+  parse_server_packet((char *)packet, (size_t)packet_len, spn, mech);
+  return auth_client(spn, mech, mysql, vio);
+}
+
+
+/* register client plugin */
+mysql_declare_client_plugin(AUTHENTICATION)
+  "auth_gssapi_client",
+  "Shuang Qiu, Robbie Harwood, Vladislav Vaintroub",
+  "GSSAPI/SSPI based authentication",
+  {0, 1, 0},
+  "BSD",
+  NULL,
+  NULL,
+  NULL,
+  NULL,
+  gssapi_auth_client
+mysql_end_client_plugin;
diff --git a/plugin/auth_gssapi/cmake/FindGSSAPI.cmake b/plugin/auth_gssapi/cmake/FindGSSAPI.cmake
new file mode 100644
index 0000000..faee428
--- /dev/null
+++ b/plugin/auth_gssapi/cmake/FindGSSAPI.cmake
@@ -0,0 +1,78 @@
+# - Try to detect the GSSAPI support
+# Once done this will define
+#
+#  GSSAPI_FOUND - system supports GSSAPI
+#  GSSAPI_INCS - the GSSAPI include directory
+#  GSSAPI_LIBS - the libraries needed to use GSSAPI
+#  GSSAPI_FLAVOR - the type of API - MIT or HEIMDAL
+
+# Copyright (c) 2006, Pino Toscano, <toscano.pino at tiscali.it>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+
+if(GSSAPI_LIBS AND GSSAPI_FLAVOR)
+
+  # in cache already
+  set(GSSAPI_FOUND TRUE)
+
+else(GSSAPI_LIBS AND GSSAPI_FLAVOR)
+
+  find_program(KRB5_CONFIG NAMES krb5-config PATHS
+     /opt/local/bin
+     ONLY_CMAKE_FIND_ROOT_PATH               # this is required when cross compiling with cmake 2.6 and ignored with cmake 2.4, Alex
+  )
+  mark_as_advanced(KRB5_CONFIG)
+
+  #reset vars
+  set(GSSAPI_INCS)
+  set(GSSAPI_LIBS)
+  set(GSSAPI_FLAVOR)
+
+  if(KRB5_CONFIG)
+  
+    set(HAVE_KRB5_GSSAPI TRUE)
+    exec_program(${KRB5_CONFIG} ARGS --libs gssapi RETURN_VALUE _return_VALUE OUTPUT_VARIABLE GSSAPI_LIBS)
+    if(_return_VALUE)
+      message(STATUS "GSSAPI configure check failed.")
+      set(HAVE_KRB5_GSSAPI FALSE)
+    endif(_return_VALUE)
+  
+    exec_program(${KRB5_CONFIG} ARGS --cflags gssapi RETURN_VALUE _return_VALUE OUTPUT_VARIABLE GSSAPI_INCS)
+    string(REGEX REPLACE "(\r?\n)+$" "" GSSAPI_INCS "${GSSAPI_INCS}")
+    string(REGEX REPLACE " *-I" ";" GSSAPI_INCS "${GSSAPI_INCS}")
+
+    exec_program(${KRB5_CONFIG} ARGS --vendor RETURN_VALUE _return_VALUE OUTPUT_VARIABLE gssapi_flavor_tmp)
+    set(GSSAPI_FLAVOR_MIT)
+    if(gssapi_flavor_tmp MATCHES ".*Massachusetts.*")
+      set(GSSAPI_FLAVOR "MIT")
+    else(gssapi_flavor_tmp MATCHES ".*Massachusetts.*")
+      set(GSSAPI_FLAVOR "HEIMDAL")
+    endif(gssapi_flavor_tmp MATCHES ".*Massachusetts.*")
+
+    if(NOT HAVE_KRB5_GSSAPI)
+      if (gssapi_flavor_tmp MATCHES "Sun Microsystems.*")
+         message(STATUS "Solaris Kerberos does not have GSSAPI; this is normal.")
+         set(GSSAPI_LIBS)
+         set(GSSAPI_INCS)
+      else(gssapi_flavor_tmp MATCHES "Sun Microsystems.*")
+         message(WARNING "${KRB5_CONFIG} failed unexpectedly.")
+      endif(gssapi_flavor_tmp MATCHES "Sun Microsystems.*")
+    endif(NOT HAVE_KRB5_GSSAPI)
+
+    if(GSSAPI_LIBS) # GSSAPI_INCS can be also empty, so don't rely on that
+      set(GSSAPI_FOUND TRUE CACHE STRING "")
+      message(STATUS "Found GSSAPI: ${GSSAPI_LIBS}")
+
+      set(GSSAPI_INCS ${GSSAPI_INCS} CACHE STRING "")
+      set(GSSAPI_LIBS ${GSSAPI_LIBS} CACHE STRING "")
+      set(GSSAPI_FLAVOR ${GSSAPI_FLAVOR} CACHE STRING "")
+
+      mark_as_advanced(GSSAPI_INCS GSSAPI_LIBS GSSAPI_FLAVOR)
+
+    endif(GSSAPI_LIBS)
+  
+  endif(KRB5_CONFIG)
+
+endif(GSSAPI_LIBS AND GSSAPI_FLAVOR)
\ No newline at end of file
diff --git a/plugin/auth_gssapi/common.h b/plugin/auth_gssapi/common.h
new file mode 100644
index 0000000..c04241a
--- /dev/null
+++ b/plugin/auth_gssapi/common.h
@@ -0,0 +1,4 @@
+/** Maximal length of the target name */
+#define PRINCIPAL_NAME_MAX 256
+/** Maximal length of the mech string */
+#define MECH_NAME_MAX 30
diff --git a/plugin/auth_gssapi/gssapi_client.cc b/plugin/auth_gssapi/gssapi_client.cc
new file mode 100644
index 0000000..d449bb8
--- /dev/null
+++ b/plugin/auth_gssapi/gssapi_client.cc
@@ -0,0 +1,127 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <gssapi/gssapi.h>
+#include <string.h>
+#include <stdio.h>
+#include <mysql/plugin_auth.h>
+#include <mysqld_error.h>
+#include <mysql.h>
+#include "gssapi_errmsg.h"
+
+extern void log_client_error(MYSQL *mysql,const char *fmt,...);
+
+
+/* This sends the error to the client */
+static void log_error(MYSQL *mysql, OM_uint32 major, OM_uint32 minor, const char *msg)
+{
+  if (GSS_ERROR(major))
+  {
+    char sysmsg[1024];
+    gssapi_errmsg(major, minor, sysmsg, sizeof(sysmsg));
+    log_client_error(mysql,
+      "Client GSSAPI error (major %u, minor %u) : %s - %s",
+       major, minor, msg, sysmsg);
+  }
+  else
+  {
+    log_client_error(mysql, "Client GSSAPI error : %s", msg);
+  }
+}
+
+int auth_client(char *principal_name, char *mech, MYSQL *mysql, MYSQL_PLUGIN_VIO *vio)
+{
+
+  int ret= CR_ERROR;
+  OM_uint32 major= 0, minor= 0;
+  gss_ctx_id_t ctxt= GSS_C_NO_CONTEXT;
+  gss_name_t service_name= GSS_C_NO_NAME;
+
+  if (principal_name && principal_name[0])
+  {
+    /* import principal from plain text */
+    gss_buffer_desc principal_name_buf;
+    principal_name_buf.length= strlen(principal_name);
+    principal_name_buf.value= (void *) principal_name;
+    major= gss_import_name(&minor, &principal_name_buf, GSS_C_NT_USER_NAME, &service_name);
+    if (GSS_ERROR(major))
+    {
+      log_error(mysql, major, minor, "gss_import_name");
+      return CR_ERROR;
+    }
+  }
+
+  gss_buffer_desc input= {0,0};
+  do
+  {
+    gss_buffer_desc output= {0,0};
+    major= gss_init_sec_context(&minor, GSS_C_NO_CREDENTIAL, &ctxt, service_name,
+                                GSS_C_NO_OID, 0, 0, GSS_C_NO_CHANNEL_BINDINGS,
+                                &input, NULL, &output, NULL, NULL);
+    if (output.length)
+    {
+      /* send credential */
+      if(vio->write_packet(vio, (unsigned char *)output.value, output.length))
+      {
+        /* Server error packet contains detailed message. */
+        ret= CR_OK_HANDSHAKE_COMPLETE;
+        gss_release_buffer (&minor, &output);
+        goto cleanup;
+      }
+    }
+    gss_release_buffer (&minor, &output);
+
+    if (GSS_ERROR(major))
+    {
+       log_error(mysql, major, minor,"gss_init_sec_context");
+       goto cleanup;
+    }
+
+    if (major & GSS_S_CONTINUE_NEEDED)
+    {
+      int len= vio->read_packet(vio, (unsigned char **) &input.value);
+      if (len <= 0)
+      {
+        /* Server error packet contains detailed message. */
+        ret= CR_OK_HANDSHAKE_COMPLETE;
+        goto cleanup;
+      }
+      input.length= len;
+    }
+  } while (major & GSS_S_CONTINUE_NEEDED);
+
+  ret= CR_OK;
+
+cleanup:
+  if (service_name != GSS_C_NO_NAME)
+    gss_release_name(&minor, &service_name);
+  if (ctxt != GSS_C_NO_CONTEXT)
+    gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER);
+
+  return ret;
+}
diff --git a/plugin/auth_gssapi/gssapi_errmsg.cc b/plugin/auth_gssapi/gssapi_errmsg.cc
new file mode 100644
index 0000000..29adf60
--- /dev/null
+++ b/plugin/auth_gssapi/gssapi_errmsg.cc
@@ -0,0 +1,75 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <gssapi.h>
+#include <string.h>
+void gssapi_errmsg(OM_uint32 major, OM_uint32 minor, char *buf, size_t size)
+{
+  OM_uint32 message_context;
+  OM_uint32 status_code;
+  OM_uint32 maj_status;
+  OM_uint32 min_status;
+  gss_buffer_desc status_string;
+  char *p= buf;
+  char *end= buf + size - 1;
+  int types[] = {GSS_C_GSS_CODE,GSS_C_MECH_CODE};
+
+  for(int i= 0; i < 2;i++)
+  {
+    message_context= 0;
+    status_code= types[i] == GSS_C_GSS_CODE?major:minor;
+
+    if(!status_code)
+      continue;
+    do
+    {
+      maj_status = gss_display_status(
+        &min_status,
+        status_code,
+        types[i],
+        GSS_C_NO_OID,
+        &message_context,
+        &status_string);
+
+      if(maj_status)
+        break;
+
+      if(p + status_string.length + 2 < end)
+      {
+        memcpy(p,status_string.value, status_string.length);
+        p += status_string.length;
+        *p++ = '.';
+        *p++ = ' ';
+      }
+
+      gss_release_buffer(&min_status, &status_string);
+    }
+    while (message_context != 0);
+  }
+  *p= 0;
+}
diff --git a/plugin/auth_gssapi/gssapi_errmsg.h b/plugin/auth_gssapi/gssapi_errmsg.h
new file mode 100644
index 0000000..786b2f6
--- /dev/null
+++ b/plugin/auth_gssapi/gssapi_errmsg.h
@@ -0,0 +1,29 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+extern void gssapi_errmsg(OM_uint32 major, OM_uint32 minor, char *buf, size_t size);
diff --git a/plugin/auth_gssapi/gssapi_server.cc b/plugin/auth_gssapi/gssapi_server.cc
new file mode 100644
index 0000000..0f64dc7
--- /dev/null
+++ b/plugin/auth_gssapi/gssapi_server.cc
@@ -0,0 +1,248 @@
+#include <my_config.h>
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_ext.h>
+#include <stdio.h>
+#include <mysql/plugin_auth.h>
+#include <my_sys.h>
+#include <mysqld_error.h>
+#include <log.h>
+#include "server_plugin.h"
+#include "gssapi_errmsg.h"
+
+static gss_name_t service_name = GSS_C_NO_NAME;
+
+/* This sends the error to the client */
+static void log_error( OM_uint32 major, OM_uint32 minor, const char *msg)
+{
+  if (GSS_ERROR(major))
+  {
+    char sysmsg[1024];
+    gssapi_errmsg(major, minor, sysmsg, sizeof(sysmsg));
+    my_printf_error(ER_UNKNOWN_ERROR,"Server GSSAPI error (major %u, minor %u) : %s -%s",
+      MYF(0), major, minor, msg, sysmsg);
+  }
+  else
+  {
+    my_printf_error(ER_UNKNOWN_ERROR, "Server GSSAPI error : %s", MYF(0), msg);
+  }
+}
+
+
+/*
+  Generate default principal service name formatted as principal name "mariadb/server.fqdn at REALM"
+*/
+#include <krb5.h>
+static char* get_default_principal_name()
+{
+  static char default_name[1024];
+  char *unparsed_name= NULL;
+  krb5_context context= NULL;
+  krb5_principal principal= NULL;
+  krb5_keyblock *key= NULL;
+
+  if(krb5_init_context(&context))
+  {
+    sql_print_warning("GSSAPI plugin : krb5_init_context failed");
+    goto cleanup;
+  }
+
+  if (krb5_sname_to_principal(context, NULL, "mariadb", KRB5_NT_SRV_HST, &principal))
+  {
+    sql_print_warning("GSSAPI plugin :  krb5_sname_to_principal failed");
+    goto cleanup;
+  }
+
+  if (krb5_unparse_name(context, principal, &unparsed_name))
+  {
+    sql_print_warning("GSSAPI plugin :  krb5_unparse_name failed");
+    goto cleanup;
+  }
+
+  /* Check for entry in keytab */
+  if (krb5_kt_read_service_key(context, NULL, principal, 0, 0, &key))
+  {
+    sql_print_warning("GSSAPI plugin : default principal '%s' not found in keytab", unparsed_name);
+    goto cleanup;
+  }
+
+  strncpy(default_name, unparsed_name, sizeof(default_name)-1);
+
+cleanup:
+  if (key)
+    krb5_free_keyblock(context, key);
+  if (unparsed_name)
+    krb5_free_unparsed_name(context, unparsed_name);
+  if (principal)
+    krb5_free_principal(context, principal);
+  if (context)
+    krb5_free_context(context);
+
+  return default_name;
+}
+
+
+int plugin_init()
+{
+  gss_buffer_desc principal_name_buf;
+  OM_uint32 major= 0, minor= 0;
+  gss_cred_id_t cred= GSS_C_NO_CREDENTIAL;
+
+  if(srv_keytab_path && srv_keytab_path[0])
+  {
+     setenv("KRB5_KTNAME", srv_keytab_path, 1);
+  }
+
+  if(!srv_principal_name || !srv_principal_name[0])
+    srv_principal_name= get_default_principal_name();
+
+  /* import service principal from plain text */
+  if(srv_principal_name && srv_principal_name[0])
+  {
+    sql_print_information("GSSAPI plugin : using principal name '%s'", srv_principal_name);
+    principal_name_buf.length= strlen(srv_principal_name);
+    principal_name_buf.value= srv_principal_name;
+    major= gss_import_name(&minor, &principal_name_buf, GSS_C_NT_USER_NAME, &service_name);
+    if(GSS_ERROR(major))
+    {
+      log_error(major, minor, "gss_import_name");
+      return -1;
+    }
+  }
+  else
+  {
+    service_name=  GSS_C_NO_NAME;
+  }
+
+
+
+  /* Check if SPN configuration is OK */
+  major= gss_acquire_cred(&minor, service_name, GSS_C_INDEFINITE,
+                            GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cred, NULL,
+                            NULL);
+
+  if (GSS_ERROR(major))
+  {
+    log_error(major, minor, "gss_acquire_cred failed");
+    return -1;
+  }
+  gss_release_cred(&minor, &cred);
+
+  return 0;
+}
+
+int plugin_deinit()
+{
+  if (service_name != GSS_C_NO_NAME)
+  {
+    OM_uint32 minor;
+    gss_release_name(&minor, &service_name);
+  }
+  return 0;
+}
+
+
+int auth_server(MYSQL_PLUGIN_VIO *vio,const char *user, size_t userlen, int use_full_name)
+{
+
+  int rc= CR_ERROR; /* return code */
+
+  /* GSSAPI related fields */
+  OM_uint32 major= 0, minor= 0, flags= 0;
+  gss_cred_id_t cred= GSS_C_NO_CREDENTIAL; /* credential identifier */
+  gss_ctx_id_t ctxt= GSS_C_NO_CONTEXT; /* context identifier */
+  gss_name_t client_name;
+  gss_buffer_desc client_name_buf, input, output;
+  char *client_name_str;
+
+  /* server acquires credential */
+  major= gss_acquire_cred(&minor, service_name, GSS_C_INDEFINITE,
+                            GSS_C_NO_OID_SET, GSS_C_ACCEPT, &cred, NULL,
+                            NULL);
+
+  if (GSS_ERROR(major))
+  {
+    log_error(major, minor, "gss_acquire_cred failed");
+    goto cleanup;
+  }
+
+  input.length= 0;
+  input.value= NULL;
+  do
+  {
+    /* receive token from peer */
+    int len= vio->read_packet(vio, (unsigned char **) &input.value);
+    if (len < 0)
+    {
+      log_error(0, 0, "fail to read token from client");
+      goto cleanup;
+    }
+
+    input.length= len;
+    major= gss_accept_sec_context(&minor, &ctxt, cred, &input,
+                                  GSS_C_NO_CHANNEL_BINDINGS, &client_name,
+                                  NULL, &output, &flags, NULL, NULL);
+    if (GSS_ERROR(major))
+    {
+
+      log_error(major, minor, "gss_accept_sec_context");
+      rc= CR_ERROR;
+      goto cleanup;
+    }
+
+    /* send token to peer */
+    if (output.length)
+    {
+      if (vio->write_packet(vio, (const uchar *) output.value, output.length))
+      {
+        gss_release_buffer(&minor, &output);
+        log_error(major, minor, "communication error(write)");
+        goto cleanup;
+      }
+      gss_release_buffer(&minor, &output);
+    }
+  } while (major & GSS_S_CONTINUE_NEEDED);
+
+  /* extract plain text client name */
+  major= gss_display_name(&minor, client_name, &client_name_buf, NULL);
+  if (GSS_ERROR(major))
+  {
+    log_error(major, minor, "gss_display_name");
+    goto cleanup;
+  }
+
+  client_name_str= (char *)client_name_buf.value;
+
+  /*
+   * Compare input user name with the actual one. Return success if
+   * the names match exactly, or if use_full_name parameter is not set
+   * up to the '@' separator.
+   */
+  if ((userlen == client_name_buf.length) ||
+      (!use_full_name
+       && userlen < client_name_buf.length
+       && client_name_str[userlen] == '@'))
+  {
+    if (strncmp(client_name_str, user, userlen) == 0)
+    {
+      rc= CR_OK;
+    }
+  }
+
+  if(rc != CR_OK)
+  {
+    my_printf_error(ER_ACCESS_DENIED_ERROR,
+      "GSSAPI name mismatch, requested '%s', actual name '%.*s'",
+      MYF(0), user, (int)client_name_buf.length, client_name_str);
+  }
+
+  gss_release_buffer(&minor, &client_name_buf);
+
+
+cleanup:
+  if (ctxt != GSS_C_NO_CONTEXT)
+    gss_delete_sec_context(&minor, &ctxt, GSS_C_NO_BUFFER);
+  if (cred != GSS_C_NO_CREDENTIAL)
+    gss_release_cred(&minor, &cred);
+
+  return(rc);
+}
diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result
new file mode 100644
index 0000000..a859ce5
--- /dev/null
+++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.result
@@ -0,0 +1,18 @@
+INSTALL SONAME 'auth_gssapi';
+CREATE USER GSSAPI_SHORTNAME IDENTIFIED WITH gssapi;
+SELECT USER(),CURRENT_USER();
+USER()	CURRENT_USER()
+GSSAPI_SHORTNAME at localhost	GSSAPI_SHORTNAME@%
+DROP USER GSSAPI_SHORTNAME;
+CREATE USER nosuchuser IDENTIFIED WITH gssapi;
+ERROR 28000: GSSAPI name mismatch, requested 'nosuchuser', actual name 'GSSAPI_SHORTNAME'
+DROP USER nosuchuser;
+CREATE USER usr1 IDENTIFIED WITH gssapi as 'GSSAPI_FULLNAME';
+SELECT USER(),CURRENT_USER();
+USER()	CURRENT_USER()
+usr1 at localhost	usr1@%
+DROP USER usr1;
+CREATE USER nosuchuser IDENTIFIED WITH gssapi AS 'nosuchuser at EXAMPLE.COM';
+ERROR 28000: GSSAPI name mismatch, requested 'nosuchuser at EXAMPLE.COM', actual name 'GSSAPI_FULLNAME'
+DROP USER nosuchuser;
+UNINSTALL SONAME 'auth_gssapi';
diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test
new file mode 100644
index 0000000..cb49c2e
--- /dev/null
+++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/basic.test
@@ -0,0 +1,45 @@
+INSTALL SONAME 'auth_gssapi';
+
+#
+# CREATE USER without 'AS' clause
+#
+--replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME
+eval CREATE USER $GSSAPI_SHORTNAME IDENTIFIED WITH gssapi;
+connect (con1,localhost,$GSSAPI_SHORTNAME,,);
+--replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME
+SELECT USER(),CURRENT_USER();
+disconnect con1;
+
+connection default;
+--replace_result $GSSAPI_SHORTNAME GSSAPI_SHORTNAME
+eval DROP USER $GSSAPI_SHORTNAME;
+
+CREATE USER nosuchuser IDENTIFIED WITH gssapi;
+--disable_query_log
+--replace_regex /actual name '.*'/actual name 'GSSAPI_SHORTNAME'/
+--error ER_ACCESS_DENIED_ERROR
+connect (con1,localhost,nosuchuser,,);
+--enable_query_log
+DROP USER nosuchuser;
+
+#
+# CREATE USER with 'AS' clause
+#
+--replace_result $GSSAPI_FULLNAME GSSAPI_FULLNAME
+eval CREATE USER usr1 IDENTIFIED WITH gssapi as '$GSSAPI_FULLNAME';
+connect (con1,localhost,usr1,,);
+--replace_result $GSSAPI_FULLNAME GSSAPI_FULLNAME
+SELECT USER(),CURRENT_USER();
+disconnect con1;
+connection default;
+DROP USER usr1;
+
+CREATE USER nosuchuser IDENTIFIED WITH gssapi AS 'nosuchuser at EXAMPLE.COM';
+--disable_query_log
+--replace_regex /actual name '.*'/actual name 'GSSAPI_FULLNAME'/
+--error ER_ACCESS_DENIED_ERROR
+connect (con1,localhost,nosuchuser,,);
+--enable_query_log
+DROP USER nosuchuser;
+
+UNINSTALL SONAME 'auth_gssapi';
\ No newline at end of file
diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.opt b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.opt
new file mode 100644
index 0000000..3077d70
--- /dev/null
+++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.opt
@@ -0,0 +1 @@
+--loose-gssapi-keytab-path=$GSSAPI_KEYTAB_PATH --loose-gssapi-principal-name=$GSSAPI_PRINCIPAL_NAME
diff --git a/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm
new file mode 100644
index 0000000..3ffc6f1
--- /dev/null
+++ b/plugin/auth_gssapi/mysql-test/auth_gssapi/suite.pm
@@ -0,0 +1,47 @@
+package My::Suite::AuthGSSAPI;
+
+ at ISA = qw(My::Suite);
+
+return "No AUTH_GSSAPI plugin" unless $ENV{AUTH_GSSAPI_SO};
+
+return "Not run for embedded server" if $::opt_embedded_server;
+
+# Following environment variables may need to be set
+if ($^O eq "MSWin32")
+{
+  chomp(my $whoami =`whoami /UPN 2>NUL` || `whoami`);
+  my $fullname = $whoami;
+  $fullname =~ s/\\/\\\\/; # SQL escaping for backslash
+  $ENV{'GSSAPI_FULLNAME'}  = $fullname;
+  $ENV{'GSSAPI_SHORTNAME'} = $ENV{'USERNAME'};
+}
+else
+{
+  if (!$ENV{'GSSAPI_FULLNAME'})
+  {
+    my $s = `klist |grep 'Default principal: '`;
+    if ($s)
+    {
+      chomp($s);
+      my $fullname = substr($s,19);
+      $ENV{'GSSAPI_FULLNAME'} = $fullname;
+    }
+  }
+  $ENV{'GSSAPI_SHORTNAME'} = (split /@/, $ENV{'GSSAPI_FULLNAME'}) [0];
+}
+
+
+if (!$ENV{'GSSAPI_FULLNAME'}  || !$ENV{'GSSAPI_SHORTNAME'})
+{
+  return "Environment variable GSSAPI_SHORTNAME and GSSAPI_FULLNAME need to be set"
+}
+
+foreach $var ('GSSAPI_SHORTNAME','GSSAPI_FULLNAME','GSSAPI_KEYTAB_PATH','GSSAPI_PRINCIPAL_NAME')
+{
+   print "$var=$ENV{$var}\n";
+}
+
+sub is_default { 1 }
+
+bless { };
+
diff --git a/plugin/auth_gssapi/server_plugin.cc b/plugin/auth_gssapi/server_plugin.cc
new file mode 100644
index 0000000..64f52a3
--- /dev/null
+++ b/plugin/auth_gssapi/server_plugin.cc
@@ -0,0 +1,175 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+   Vladislav Vaintroub & MariaDB Corporation
+
+   All rights reserved.
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are met:
+
+   1. Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimer.
+
+   2. Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimer in the documentation
+      and/or other materials provided with the distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+   AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+   ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+   LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+   CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+   SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+   INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+   CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+   POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/**
+  @file
+
+  GSSAPI authentication plugin, server side
+*/
+#include <my_sys.h>
+#include <mysqld_error.h>
+#include <mysql/plugin_auth.h>
+#include "server_plugin.h"
+#include "common.h"
+
+/* First packet sent from server to client, contains srv_principal_name\0mech\0 */
+static char first_packet[PRINCIPAL_NAME_MAX + MECH_NAME_MAX +2];
+static int  first_packet_len;
+
+/*
+ Target name in GSSAPI/SSPI , for Kerberos it is service principal name
+ (often user principal name of the server user will work)
+*/
+char *srv_principal_name;
+char *srv_keytab_path;
+char *srv_mech_name=(char *)"";
+unsigned long srv_mech;
+
+/**
+  The main server function of the GSSAPI plugin.
+ */
+static int gssapi_auth(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *auth_info)
+{
+  int use_full_name;
+  const char *user;
+  int user_len;
+
+  /* Send first packet with target name and mech name */
+  if (vio->write_packet(vio, (unsigned char *)first_packet, first_packet_len))
+  {
+    return CR_ERROR;
+  }
+
+  /* Figure out whether to use full name (as given in IDENTIFIED AS clause)
+   * or just short username auth_string
+   */
+  if (auth_info->auth_string_length > 0)
+  {
+    use_full_name= 1;
+    user= auth_info->auth_string;
+    user_len= auth_info->auth_string_length;
+  }
+  else
+  {
+    use_full_name= 0;
+    user= auth_info->user_name;
+    user_len= auth_info->user_name_length;
+  }
+
+  return auth_server(vio, user, user_len, use_full_name);
+}
+
+static int initialize_plugin(void *unused)
+{
+  int rc;
+  rc = plugin_init();
+  if (rc)
+    return rc;
+
+  strcpy(first_packet, srv_principal_name);
+  strcpy(first_packet + strlen(srv_principal_name) + 1,srv_mech_name);
+  first_packet_len = strlen(srv_principal_name) + strlen(srv_mech_name) + 2;
+
+  return 0;
+}
+
+static int deinitialize_plugin(void *unused)
+{
+  return plugin_deinit();
+}
+
+/* system variable */
+static MYSQL_SYSVAR_STR(keytab_path, srv_keytab_path,
+                        PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY,
+                        "Keytab file path (Kerberos)",
+                        NULL,
+                        NULL,
+                        "");
+static MYSQL_SYSVAR_STR(principal_name, srv_principal_name,
+                        PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY,
+                        "GSSAPI target name - service principal name for Kerberos authentication.",
+                        NULL,
+                        NULL,
+                        "");
+#ifdef PLUGIN_SSPI
+static const char* mech_names[] = {
+  "Kerberos",
+  "Negotiate",
+  "",
+  NULL
+};
+static TYPELIB mech_name_typelib = {
+  array_elements(mech_names) - 1,
+  "mech_name_typelib",
+  mech_names,
+  NULL
+};
+static MYSQL_SYSVAR_ENUM(mech_name, srv_mech,
+                        PLUGIN_VAR_RQCMDARG|PLUGIN_VAR_READONLY,
+                        "GSSAPI mechanism : either Kerberos or Negotiate",
+                        NULL,
+                        NULL,
+                        2,&mech_name_typelib);
+#endif
+
+static struct st_mysql_sys_var *system_variables[]= {
+  MYSQL_SYSVAR(principal_name),
+#ifdef PLUGIN_SSPI
+  MYSQL_SYSVAR(mech_name),
+#endif
+#ifdef PLUGIN_GSSAPI
+  MYSQL_SYSVAR(keytab_path),
+#endif
+  NULL
+};
+
+/* Register authentication plugin */
+static struct st_mysql_auth server_handler= {
+  MYSQL_AUTHENTICATION_INTERFACE_VERSION,
+  "auth_gssapi_client",
+  gssapi_auth
+};
+
+maria_declare_plugin(gssapi_server)
+{
+  MYSQL_AUTHENTICATION_PLUGIN,
+  &server_handler,
+  "gssapi",
+  "Shuang Qiu, Robbie Harwood, Vladislav Vaintroub",
+  "Plugin for GSSAPI/SSPI based authentication.",
+  PLUGIN_LICENSE_BSD,
+  initialize_plugin,
+  deinitialize_plugin,                   /* destructor */
+  0x0100,                                /* version */
+  NULL,                                  /* status variables */
+  system_variables,                      /* system variables */
+  "1.0",
+  MariaDB_PLUGIN_MATURITY_EXPERIMENTAL
+}
+maria_declare_plugin_end;
+
diff --git a/plugin/auth_gssapi/server_plugin.h b/plugin/auth_gssapi/server_plugin.h
new file mode 100644
index 0000000..6f1a2fc
--- /dev/null
+++ b/plugin/auth_gssapi/server_plugin.h
@@ -0,0 +1,51 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* Plugin variables*/
+#include <mysql/plugin_auth.h>
+typedef enum
+{
+  PLUGIN_MECH_KERBEROS = 0,
+  PLUGIN_MECH_SPNEGO   = 1,
+  PLUGIN_MECH_DEFAULT  = 2
+}PLUGIN_MECH;
+
+extern unsigned long srv_mech;
+extern char *srv_principal_name;
+extern char *srv_mech_name;
+extern char *srv_keytab_path;
+/*
+  Check, with GSSAPI/SSPI username of logged on user.
+
+  Depending on use_full_name parameter, compare either full name
+  (principal name like user at real), or local name (first component)
+*/
+int plugin_init();
+int plugin_deinit();
+
+int auth_server(MYSQL_PLUGIN_VIO *vio, const char *username, size_t  username_len, int use_full_name);
diff --git a/plugin/auth_gssapi/sspi.h b/plugin/auth_gssapi/sspi.h
new file mode 100644
index 0000000..ceb6687
--- /dev/null
+++ b/plugin/auth_gssapi/sspi.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define SECURITY_WIN32
+#include <windows.h>
+#include <sspi.h>
+#include <SecExt.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#define SSPI_MAX_TOKEN_SIZE 50000
+#define SEC_ERROR(err) (err < 0)
+extern void sspi_errmsg(int err, char *buf, size_t size);
\ No newline at end of file
diff --git a/plugin/auth_gssapi/sspi_client.cc b/plugin/auth_gssapi/sspi_client.cc
new file mode 100644
index 0000000..4946a0f
--- /dev/null
+++ b/plugin/auth_gssapi/sspi_client.cc
@@ -0,0 +1,183 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define SECURITY_WIN32
+#include <windows.h>
+#include <sspi.h>
+#include <SecExt.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+#include <mysql/plugin_auth.h>
+#include <mysql.h>
+#include <mysqld_error.h>
+
+#include "sspi.h"
+
+extern void log_client_error(MYSQL *mysql, const char *fmt, ...);
+static void log_error(MYSQL *mysql, SECURITY_STATUS err, const char *msg)
+{
+  if (err)
+  {
+    char buf[1024];
+    sspi_errmsg(err, buf, sizeof(buf));
+    log_client_error(mysql, "SSPI client error 0x%x - %s - %s", err, msg, buf);
+  }
+  else
+  {
+    log_client_error(mysql, "SSPI client error %s", msg);
+  }
+}
+
+
+/** Client side authentication*/
+int auth_client(char *principal_name, char *mech, MYSQL *mysql, MYSQL_PLUGIN_VIO *vio)
+{
+
+  int ret;
+  CredHandle cred;
+  CtxtHandle ctxt;
+  ULONG attribs = 0;
+  TimeStamp lifetime;
+  SECURITY_STATUS sspi_err;
+
+  SecBufferDesc inbuf_desc;
+  SecBuffer     inbuf;
+  SecBufferDesc outbuf_desc;
+  SecBuffer     outbuf;
+  PBYTE         out = NULL;
+
+  ret= CR_ERROR;
+  SecInvalidateHandle(&ctxt);
+  SecInvalidateHandle(&cred);
+
+  if (!mech || strcmp(mech, "Negotiate") != 0)
+  {
+    mech= "Kerberos";
+  }
+
+  sspi_err = AcquireCredentialsHandle(
+    NULL,
+    mech,
+    SECPKG_CRED_OUTBOUND,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    &cred,
+    &lifetime);
+
+  if (SEC_ERROR(sspi_err))
+  {
+    log_error(mysql, sspi_err, "AcquireCredentialsHandle");
+    return CR_ERROR;
+  }
+
+  out = (PBYTE)malloc(SSPI_MAX_TOKEN_SIZE);
+  if (!out)
+  {
+    log_error(mysql, SEC_E_OK, "memory allocation error");
+    goto cleanup;
+  }
+
+  /* Prepare buffers */
+  inbuf_desc.ulVersion = SECBUFFER_VERSION;
+  inbuf_desc.cBuffers = 1;
+  inbuf_desc.pBuffers = &inbuf;
+  inbuf.BufferType = SECBUFFER_TOKEN;
+  inbuf.cbBuffer = 0;
+  inbuf.pvBuffer = NULL;
+
+  outbuf_desc.ulVersion = SECBUFFER_VERSION;
+  outbuf_desc.cBuffers = 1;
+  outbuf_desc.pBuffers = &outbuf;
+  outbuf.BufferType = SECBUFFER_TOKEN;
+  outbuf.pvBuffer = out;
+
+  do
+  {
+    outbuf.cbBuffer= SSPI_MAX_TOKEN_SIZE;
+    sspi_err= InitializeSecurityContext(
+      &cred,
+      SecIsValidHandle(&ctxt) ? &ctxt : NULL,
+      principal_name,
+      0,
+      0,
+      SECURITY_NATIVE_DREP,
+      inbuf.cbBuffer ? &inbuf_desc : NULL,
+      0,
+      &ctxt,
+      &outbuf_desc,
+      &attribs,
+      &lifetime);
+    if (SEC_ERROR(sspi_err))
+    {
+      log_error(mysql, sspi_err, "InitializeSecurityContext");
+      goto cleanup;
+    }
+    if (sspi_err != SEC_E_OK && sspi_err != SEC_I_CONTINUE_NEEDED)
+    {
+      log_error(mysql, sspi_err, "Unexpected response from InitializeSecurityContext");
+      goto cleanup;
+    }
+
+    if (outbuf.cbBuffer)
+    {
+      /* send credential to server */
+      if (vio->write_packet(vio, (unsigned char *)outbuf.pvBuffer, outbuf.cbBuffer))
+      {
+        /* Server error packet contains detailed message. */
+        ret= CR_OK_HANDSHAKE_COMPLETE;
+        goto cleanup;
+      }
+    }
+
+    if (sspi_err == SEC_I_CONTINUE_NEEDED)
+    {
+      int len= vio->read_packet(vio, (unsigned char **)&inbuf.pvBuffer);
+      if (len <= 0)
+      {
+        /* Server side error is in the last server packet. */
+        ret= CR_OK_HANDSHAKE_COMPLETE;
+        goto cleanup;
+      }
+      inbuf.cbBuffer= len;
+    }
+  } while (sspi_err == SEC_I_CONTINUE_NEEDED);
+
+  ret= CR_OK;
+
+cleanup:
+
+  if (SecIsValidHandle(&ctxt))
+    DeleteSecurityContext(&ctxt);
+  if (SecIsValidHandle(&cred))
+    FreeCredentialsHandle(&cred);
+  free(out);
+  return ret;
+}
\ No newline at end of file
diff --git a/plugin/auth_gssapi/sspi_errmsg.cc b/plugin/auth_gssapi/sspi_errmsg.cc
new file mode 100644
index 0000000..8c3eb99
--- /dev/null
+++ b/plugin/auth_gssapi/sspi_errmsg.cc
@@ -0,0 +1,150 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include <windows.h>
+#include <stdio.h>
+
+#define ERRSYM(x) {x, #x}
+static struct {
+  int error;
+  const char *sym;
+} error_symbols[] =
+{
+  ERRSYM(SEC_E_OK),
+  ERRSYM(SEC_E_INSUFFICIENT_MEMORY),
+  ERRSYM(SEC_E_INVALID_HANDLE),
+  ERRSYM(SEC_E_UNSUPPORTED_FUNCTION),
+  ERRSYM(SEC_E_TARGET_UNKNOWN),
+  ERRSYM(SEC_E_INTERNAL_ERROR),
+  ERRSYM(SEC_E_SECPKG_NOT_FOUND),
+  ERRSYM(SEC_E_NOT_OWNER),
+  ERRSYM(SEC_E_CANNOT_INSTALL),
+  ERRSYM(SEC_E_INVALID_TOKEN),
+  ERRSYM(SEC_E_CANNOT_PACK),
+  ERRSYM(SEC_E_QOP_NOT_SUPPORTED),
+  ERRSYM(SEC_E_NO_IMPERSONATION),
+  ERRSYM(SEC_E_LOGON_DENIED),
+  ERRSYM(SEC_E_UNKNOWN_CREDENTIALS),
+  ERRSYM(SEC_E_NO_CREDENTIALS),
+  ERRSYM(SEC_E_MESSAGE_ALTERED),
+  ERRSYM(SEC_E_OUT_OF_SEQUENCE),
+  ERRSYM(SEC_E_NO_AUTHENTICATING_AUTHORITY),
+  ERRSYM(SEC_E_BAD_PKGID),
+  ERRSYM(SEC_E_CONTEXT_EXPIRED),
+  ERRSYM(SEC_E_INCOMPLETE_MESSAGE),
+  ERRSYM(SEC_E_INCOMPLETE_CREDENTIALS),
+  ERRSYM(SEC_E_BUFFER_TOO_SMALL),
+  ERRSYM(SEC_E_WRONG_PRINCIPAL),
+  ERRSYM(SEC_E_TIME_SKEW),
+  ERRSYM(SEC_E_UNTRUSTED_ROOT),
+  ERRSYM(SEC_E_ILLEGAL_MESSAGE),
+  ERRSYM(SEC_E_CERT_UNKNOWN),
+  ERRSYM(SEC_E_CERT_EXPIRED),
+  ERRSYM(SEC_E_ENCRYPT_FAILURE),
+  ERRSYM(SEC_E_DECRYPT_FAILURE),
+  ERRSYM(SEC_E_ALGORITHM_MISMATCH),
+  ERRSYM(SEC_E_SECURITY_QOS_FAILED),
+  ERRSYM(SEC_E_UNFINISHED_CONTEXT_DELETED),
+  ERRSYM(SEC_E_NO_TGT_REPLY),
+  ERRSYM(SEC_E_NO_IP_ADDRESSES),
+  ERRSYM(SEC_E_WRONG_CREDENTIAL_HANDLE),
+  ERRSYM(SEC_E_CRYPTO_SYSTEM_INVALID),
+  ERRSYM(SEC_E_MAX_REFERRALS_EXCEEDED),
+  ERRSYM(SEC_E_MUST_BE_KDC),
+  ERRSYM(SEC_E_STRONG_CRYPTO_NOT_SUPPORTED),
+  ERRSYM(SEC_E_TOO_MANY_PRINCIPALS),
+  ERRSYM(SEC_E_NO_PA_DATA),
+  ERRSYM(SEC_E_PKINIT_NAME_MISMATCH),
+  ERRSYM(SEC_E_SMARTCARD_LOGON_REQUIRED),
+  ERRSYM(SEC_E_SHUTDOWN_IN_PROGRESS),
+  ERRSYM(SEC_E_KDC_INVALID_REQUEST),
+  ERRSYM(SEC_E_KDC_UNABLE_TO_REFER),
+  ERRSYM(SEC_E_KDC_UNKNOWN_ETYPE),
+  ERRSYM(SEC_E_UNSUPPORTED_PREAUTH),
+  ERRSYM(SEC_E_DELEGATION_REQUIRED),
+  ERRSYM(SEC_E_BAD_BINDINGS),
+  ERRSYM(SEC_E_MULTIPLE_ACCOUNTS),
+  ERRSYM(SEC_E_NO_KERB_KEY),
+  ERRSYM(SEC_E_CERT_WRONG_USAGE),
+  ERRSYM(SEC_E_DOWNGRADE_DETECTED),
+  ERRSYM(SEC_E_SMARTCARD_CERT_REVOKED),
+  ERRSYM(SEC_E_ISSUING_CA_UNTRUSTED),
+  ERRSYM(SEC_E_REVOCATION_OFFLINE_C),
+  ERRSYM(SEC_E_PKINIT_CLIENT_FAILURE),
+  ERRSYM(SEC_E_SMARTCARD_CERT_EXPIRED),
+  ERRSYM(SEC_E_NO_S4U_PROT_SUPPORT),
+  ERRSYM(SEC_E_CROSSREALM_DELEGATION_FAILURE),
+  ERRSYM(SEC_E_REVOCATION_OFFLINE_KDC),
+  ERRSYM(SEC_E_ISSUING_CA_UNTRUSTED_KDC),
+  ERRSYM(SEC_E_KDC_CERT_EXPIRED),
+  ERRSYM(SEC_E_KDC_CERT_REVOKED),
+  ERRSYM(SEC_E_INVALID_PARAMETER),
+  ERRSYM(SEC_E_DELEGATION_POLICY),
+  ERRSYM(SEC_E_POLICY_NLTM_ONLY),
+  ERRSYM(SEC_E_NO_CONTEXT),
+  ERRSYM(SEC_E_PKU2U_CERT_FAILURE),
+  ERRSYM(SEC_E_MUTUAL_AUTH_FAILED),
+  ERRSYM(SEC_E_NO_SPM),
+  ERRSYM(SEC_E_NOT_SUPPORTED),
+  {0,0}
+};
+
+void sspi_errmsg(int err, char *buf, size_t size)
+{
+  buf[size - 1] = 0;
+  size_t len;
+
+  for (size_t i= 0; error_symbols[i].sym; i++)
+  {
+    if (error_symbols[i].error == err)
+    {
+      size_t len= strlen(error_symbols[i].sym);
+      if (len + 2 < size)
+      {
+        memcpy(buf, error_symbols[i].sym, len);
+        buf[len]= ' ';
+        buf += len + 1;
+        size-= len + 1;
+      }
+      break;
+    }
+  }
+
+  len = FormatMessageA(
+    FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
+    err, MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
+    buf, size, NULL);
+
+  if(len > 0)
+  {
+    /* Trim trailing \n\r*/
+    char *p;
+    for(p= buf + len;p > buf && (*p == '\n' || *p=='\r' || *p == 0);p--)
+      *p= 0;
+  }
+}
diff --git a/plugin/auth_gssapi/sspi_server.cc b/plugin/auth_gssapi/sspi_server.cc
new file mode 100644
index 0000000..1d51a66
--- /dev/null
+++ b/plugin/auth_gssapi/sspi_server.cc
@@ -0,0 +1,312 @@
+/* Copyright (c) 2015, Shuang Qiu, Robbie Hardwood,
+Vladislav Vaintroub & MariaDB Corporation
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "sspi.h"
+#include "common.h"
+#include "server_plugin.h"
+#include <mysql/plugin_auth.h>
+#include <my_sys.h>
+#include <mysqld_error.h>
+#include <log.h>
+
+
+/* This sends the error to the client */
+static void log_error(SECURITY_STATUS err, const char *msg)
+{
+  if (err)
+  {
+    char buf[1024];
+    sspi_errmsg(err, buf, sizeof(buf));
+    my_printf_error(ER_UNKNOWN_ERROR, "SSPI server error 0x%x - %s - %s", MYF(0), msg, buf);
+  }
+  else
+  {
+    my_printf_error(ER_UNKNOWN_ERROR, "SSPI server error %s", MYF(0), msg);
+  }
+
+}
+
+static char INVALID_KERBEROS_PRINCIPAL[] = "localhost";
+
+static char *get_default_principal_name()
+{
+  static char default_principal[PRINCIPAL_NAME_MAX +1];
+  ULONG size= sizeof(default_principal);
+
+  if (GetUserNameEx(NameUserPrincipal,default_principal,&size))
+    return default_principal;
+
+  size= sizeof(default_principal);
+  if (GetUserNameEx(NameServicePrincipal,default_principal,&size))
+    return default_principal;
+
+  char domain[PRINCIPAL_NAME_MAX+1];
+  char host[PRINCIPAL_NAME_MAX+1];
+  size= sizeof(domain);
+  if (GetComputerNameEx(ComputerNameDnsDomain,domain,&size) && size > 0)
+  {
+    size= sizeof(host);
+    if (GetComputerNameEx(ComputerNameDnsHostname,host,&size))
+    {
+      _snprintf(default_principal,sizeof(default_principal),"%s$@%s",host, domain);
+      return default_principal;
+    }
+  }
+  /* Unable to retrieve useful name, return something */
+  return INVALID_KERBEROS_PRINCIPAL;
+}
+
+
+/* Extract client name from SSPI context */
+static int get_client_name_from_context(CtxtHandle *ctxt,
+  char *name,
+  size_t name_len,
+  int use_full_name)
+{
+  SecPkgContext_NativeNames native_names;
+  SECURITY_STATUS sspi_ret;
+  char *p;
+
+  sspi_ret= QueryContextAttributes(ctxt, SECPKG_ATTR_NATIVE_NAMES, &native_names);
+  if (sspi_ret == SEC_E_OK)
+  {
+    /* Extract user from Kerberos principal name user at realm */
+    if(!use_full_name)
+    {
+      p = strrchr(native_names.sClientName,'@');
+      if(p)
+        *p = 0;
+    }
+    strncpy(name, native_names.sClientName, name_len);
+    FreeContextBuffer(&native_names);
+    return CR_OK;
+  }
+
+  sspi_ret= ImpersonateSecurityContext(ctxt);
+  if (sspi_ret == SEC_E_OK)
+  {
+    ULONG len= name_len;
+    if (!GetUserNameEx(NameSamCompatible, name, &len))
+    {
+      log_error(GetLastError(), "GetUserNameEx");
+      RevertSecurityContext(ctxt);
+      return CR_ERROR;
+    }
+    RevertSecurityContext(ctxt);
+
+    /* Extract user from Windows name realm\user */
+    if (!use_full_name)
+    {
+      p = strrchr(name, '\\');
+      if (p)
+      {
+        p++;
+        memmove(name, p, name + len + 1 - p);
+      }
+    }
+    return CR_OK;
+  }
+
+  log_error(sspi_ret, "ImpersonateSecurityContext");
+  return CR_ERROR;
+}
+
+
+int auth_server(MYSQL_PLUGIN_VIO *vio, const char *user, size_t user_len, int compare_full_name)
+{
+  int ret;
+  SECURITY_STATUS sspi_ret;
+  ULONG  attribs = 0;
+  TimeStamp   lifetime;
+  CredHandle  cred;
+  CtxtHandle  ctxt;
+
+  SecBufferDesc inbuf_desc;
+  SecBuffer     inbuf;
+  SecBufferDesc outbuf_desc;
+  SecBuffer     outbuf;
+  void*         out= NULL;
+  char client_name[MYSQL_USERNAME_LENGTH + 1];
+
+  ret= CR_ERROR;
+  SecInvalidateHandle(&cred);
+  SecInvalidateHandle(&ctxt);
+
+  out= malloc(SSPI_MAX_TOKEN_SIZE);
+  if (!out)
+  {
+    log_error(SEC_E_OK, "memory allocation failed");
+    goto cleanup;
+  }
+  sspi_ret= AcquireCredentialsHandle(
+    srv_principal_name,
+    srv_mech_name,
+    SECPKG_CRED_INBOUND,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    &cred,
+    &lifetime);
+
+  if (SEC_ERROR(sspi_ret))
+  {
+    log_error(sspi_ret, "AcquireCredentialsHandle failed");
+    goto cleanup;
+  }
+
+  inbuf.cbBuffer= 0;
+  inbuf.BufferType= SECBUFFER_TOKEN;
+  inbuf.pvBuffer= NULL;
+  inbuf_desc.ulVersion= SECBUFFER_VERSION;
+  inbuf_desc.cBuffers= 1;
+  inbuf_desc.pBuffers= &inbuf;
+
+  outbuf.BufferType= SECBUFFER_TOKEN;
+  outbuf.cbBuffer= SSPI_MAX_TOKEN_SIZE;
+  outbuf.pvBuffer= out;
+
+  outbuf_desc.ulVersion= SECBUFFER_VERSION;
+  outbuf_desc.cBuffers= 1;
+  outbuf_desc.pBuffers= &outbuf;
+
+  do
+  {
+    /* Read SSPI blob from client. */
+    int len= vio->read_packet(vio, (unsigned char **)&inbuf.pvBuffer);
+    if (len < 0)
+    {
+      log_error(SEC_E_OK, "communication error(read)");
+      goto cleanup;
+    }
+    inbuf.cbBuffer= len;
+    outbuf.cbBuffer= SSPI_MAX_TOKEN_SIZE;
+    sspi_ret= AcceptSecurityContext(
+      &cred,
+      SecIsValidHandle(&ctxt) ? &ctxt : NULL,
+      &inbuf_desc,
+      attribs,
+      SECURITY_NATIVE_DREP,
+      &ctxt,
+      &outbuf_desc,
+      &attribs,
+      &lifetime);
+
+    if (SEC_ERROR(sspi_ret))
+    {
+      log_error(sspi_ret, "AcceptSecurityContext");
+      goto cleanup;
+    }
+    if (sspi_ret != SEC_E_OK && sspi_ret != SEC_I_CONTINUE_NEEDED)
+    {
+      log_error(sspi_ret, "AcceptSecurityContext unexpected return value");
+      goto cleanup;
+    }
+    if (outbuf.cbBuffer)
+    {
+      /* Send generated blob to client. */
+      if (vio->write_packet(vio, (unsigned char *)outbuf.pvBuffer, outbuf.cbBuffer))
+      {
+        log_error(SEC_E_OK, "communicaton error(write)");
+        goto cleanup;
+      }
+    }
+  } while (sspi_ret == SEC_I_CONTINUE_NEEDED);
+
+  /* Authentication done, now extract and compare user name. */
+  ret= get_client_name_from_context(&ctxt, client_name, MYSQL_USERNAME_LENGTH, compare_full_name);
+  if (ret != CR_OK)
+    goto cleanup;
+
+  /* Always compare case-insensitive on Windows. */
+  ret= _stricmp(client_name, user) == 0 ? CR_OK : CR_ERROR;
+  if (ret != CR_OK)
+  {
+    my_printf_error(ER_ACCESS_DENIED_ERROR,
+      "GSSAPI name mismatch, requested '%s', actual name '%s'",
+      MYF(0), user, client_name);
+  }
+
+cleanup:
+  if (SecIsValidHandle(&ctxt))
+    DeleteSecurityContext(&ctxt);
+
+  if (SecIsValidHandle(&cred))
+    FreeCredentialsHandle(&cred);
+
+  free(out);
+  return ret;
+}
+
+int plugin_init()
+{
+  CredHandle cred;
+  SECURITY_STATUS ret;
+
+  /*
+    Use negotiate by default, which accepts raw kerberos
+    and also NTLM.
+  */
+  if (srv_mech == PLUGIN_MECH_DEFAULT)
+    srv_mech=  PLUGIN_MECH_SPNEGO;
+
+  if(srv_mech == PLUGIN_MECH_KERBEROS)
+    srv_mech_name= "Kerberos";
+  else if(srv_mech == PLUGIN_MECH_SPNEGO )
+    srv_mech_name= "Negotiate";
+
+  if(!srv_principal_name[0])
+  {
+    srv_principal_name= get_default_principal_name();
+  }
+  sql_print_information("SSPI: using principal name '%s', mech '%s'",
+    srv_principal_name, srv_mech_name);
+
+  ret = AcquireCredentialsHandle(
+    srv_principal_name,
+    srv_mech_name,
+    SECPKG_CRED_INBOUND,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    &cred,
+    NULL);
+  if (SEC_ERROR(ret))
+  {
+    log_error(ret, "AcquireCredentialsHandle");
+    return -1;
+  }
+  FreeCredentialsHandle(&cred);
+  return 0;
+}
+
+int plugin_deinit()
+{
+  return 0;
+}


More information about the commits mailing list