1
0
mirror of https://github.com/mariadb-corporation/mariadb-connector-c.git synced 2025-08-07 02:42:49 +03:00

Added GSSAPI authentication plugin

This commit is contained in:
Georg Richter
2016-01-22 20:00:40 +01:00
parent 8b3099b319
commit f0215ab091
22 changed files with 883 additions and 4 deletions

View File

@@ -258,6 +258,17 @@ IF(NOT REMOTEIO_PLUGIN_TYPE MATCHES "OFF")
ADD_DEFINITIONS("-DHAVE_REMOTEIO=1") ADD_DEFINITIONS("-DHAVE_REMOTEIO=1")
ENDIF() ENDIF()
ENDIF() ENDIF()
IF(NOT WIN32)
IF(NOT AUTH_GSSAPI_PLUGIN_TYPE MATCHES "OFF")
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/FindGSSAPI.cmake)
IF(GSSAPI_FOUND)
INCLUDE_DIRECTORIES(${GSSAPI_INCS})
IF(AUTH_GSSAPI_PLUGIN_TYPE MATCHES "STATIC")
SET(SYSTEM_LIBS ${SYSTEM_LIBS} ${GSSAPI_LIBS})
ENDIF()
ENDIF()
ENDIF()
ENDIF()
ADD_SUBDIRECTORY(include) ADD_SUBDIRECTORY(include)
ADD_SUBDIRECTORY(libmariadb) ADD_SUBDIRECTORY(libmariadb)
ADD_SUBDIRECTORY(plugins) ADD_SUBDIRECTORY(plugins)

View File

@@ -66,3 +66,4 @@ CHECK_INCLUDE_FILES (termio.h HAVE_TERMIO_H)
CHECK_INCLUDE_FILES (termios.h HAVE_TERMIOS_H) CHECK_INCLUDE_FILES (termios.h HAVE_TERMIOS_H)
CHECK_INCLUDE_FILES (unistd.h HAVE_UNISTD_H) CHECK_INCLUDE_FILES (unistd.h HAVE_UNISTD_H)
CHECK_INCLUDE_FILES (utime.h HAVE_UTIME_H) CHECK_INCLUDE_FILES (utime.h HAVE_UTIME_H)
CHECK_INCLUDE_FILES (ucontext.h HAVE_UCONTEXT_H)

View File

@@ -35,6 +35,15 @@ REGISTER_PLUGIN("AUTH_NATIVE" "${CMAKE_SOURCE_DIR}/plugins/auth/my_auth.c" "nati
REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/auth/old_password.c" "old_password_client_plugin" "DYNAMIC" "old_password" 1) REGISTER_PLUGIN("AUTH_OLDPASSWORD" "${CMAKE_SOURCE_DIR}/plugins/auth/old_password.c" "old_password_client_plugin" "DYNAMIC" "old_password" 1)
REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "auth_dialog_plugin" "DYNAMIC" dialog 1) REGISTER_PLUGIN("AUTH_DIALOG" "${CMAKE_SOURCE_DIR}/plugins/auth/dialog.c" "auth_dialog_plugin" "DYNAMIC" dialog 1)
REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "auth_cleartext_plugin" "DYNAMIC" "mysql_clear_password" 1) REGISTER_PLUGIN("AUTH_CLEARTEXT" "${CMAKE_SOURCE_DIR}/plugins/auth/mariadb_clear_text.c" "auth_cleartext_plugin" "DYNAMIC" "mysql_clear_password" 1)
IF(WIN32)
SET(GSSAPI_SOURCES ${CMAKE_SOURCE_DIR}/plugins/auth/auth_gssapi_client.c ${CMAKE_SOURCE_DIR}/plugins/auth/sspi_client.c ${CMAKE_SOURCE_DIR}/plugins/auth/sspi_errmsg.c)
REGISTER_PLUGIN("AUTH_GSSAPI" "${GSSAPI_SOURCES}" "auth_gssapi_plugin" "DYNAMIC" "auth_gssapi_client" 1)
ELSE()
IF(GSSAPI_FOUND)
SET(GSSAPI_SOURCES ${CMAKE_SOURCE_DIR}/plugins/auth/auth_gssapi_client.c ${CMAKE_SOURCE_DIR}/plugins/auth/gssapi_client.c ${CMAKE_SOURCE_DIR}/plugins/auth/gssapi_errmsg.c)
REGISTER_PLUGIN("AUTH_GSSAPI" "${GSSAPI_SOURCES}" "auth_gssapi_plugin" "DYNAMIC" "auth_gssapi_client" 1)
ENDIF()
ENDIF()
#Remote_IO #Remote_IO
IF(CURL_FOUND) IF(CURL_FOUND)

View File

@@ -46,6 +46,7 @@
#cmakedefine HAVE_TERMIOS_H 1 #cmakedefine HAVE_TERMIOS_H 1
#cmakedefine HAVE_UNISTD_H 1 #cmakedefine HAVE_UNISTD_H 1
#cmakedefine HAVE_UTIME_H 1 #cmakedefine HAVE_UTIME_H 1
#cmakedefine HAVE_UCONTEXT_H 1
/* /*
* function definitions - processed in LibmysqlFunctions.txt * function definitions - processed in LibmysqlFunctions.txt

View File

@@ -31,7 +31,7 @@
#define MY_CONTEXT_USE_X86_64_GCC_ASM #define MY_CONTEXT_USE_X86_64_GCC_ASM
#elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__i386__) #elif defined(__GNUC__) && __GNUC__ >= 3 && defined(__i386__)
#define MY_CONTEXT_USE_I386_GCC_ASM #define MY_CONTEXT_USE_I386_GCC_ASM
#elif defined(HAVE_UCONTEXT) #elif defined(HAVE_UCONTEXT_H)
#define MY_CONTEXT_USE_UCONTEXT #define MY_CONTEXT_USE_UCONTEXT
#else #else
#define MY_CONTEXT_DISABLE #define MY_CONTEXT_DISABLE

View File

@@ -2147,6 +2147,9 @@ mysql_real_query(MYSQL *mysql, const char *query, size_t length)
DBUG_PRINT("enter",("handle: %lx",mysql)); DBUG_PRINT("enter",("handle: %lx",mysql));
DBUG_PRINT("query",("Query = \"%.255s\" length=%u",query, length)); DBUG_PRINT("query",("Query = \"%.255s\" length=%u",query, length));
if (length == -1)
length= strlen(query);
free_old_query(mysql); free_old_query(mysql);
if (simple_command(mysql, COM_QUERY,query,length,1,0)) if (simple_command(mysql, COM_QUERY,query,length,1,0))

View File

@@ -20,6 +20,7 @@
swapcontext(). swapcontext().
*/ */
#include "my_global.h"
#include "mysys_priv.h" #include "mysys_priv.h"
#include "m_string.h" #include "m_string.h"
#include "my_context.h" #include "my_context.h"

View File

@@ -1227,6 +1227,9 @@ int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, size_t lengt
int rc= 1; int rc= 1;
DBUG_ENTER("mysql_stmt_prepare"); DBUG_ENTER("mysql_stmt_prepare");
if (length == -1)
length= strlen(query);
if (!stmt->mysql) if (!stmt->mysql)
{ {
SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0); SET_CLIENT_STMT_ERROR(stmt, CR_SERVER_LOST, SQLSTATE_UNKNOWN, 0);

View File

@@ -1,4 +1,5 @@
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include) INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/plugins/auth)
INCLUDE(${CMAKE_SOURCE_DIR}/cmake/install_plugins.cmake) INCLUDE(${CMAKE_SOURCE_DIR}/cmake/install_plugins.cmake)
IF(WITH_SIGNCODE) IF(WITH_SIGNCODE)
@@ -64,3 +65,36 @@ IF(AUTH_CLEARTEXT_PLUGIN_TYPE MATCHES "DYNAMIC")
INSTALL_PLUGIN(mysql_clear_password ${CMAKE_BINARY_DIR}/plugins/auth) INSTALL_PLUGIN(mysql_clear_password ${CMAKE_BINARY_DIR}/plugins/auth)
SIGN_TARGET(mysql_clear_password) SIGN_TARGET(mysql_clear_password)
ENDIF() ENDIF()
# SSPI/GSSAPI plugin
IF(WIN32)
SET(USE_SSPI 1)
ENDIF()
MESSAGE(STATUS "GSSAPI: ${AUTH_GSSAPI_PLUGIN_TYPE}")
IF(${AUTH_GSSAPI_PLUGIN_TYPE} MATCHES "DYNAMIC")
IF(WIN32)
SET_VERSION_INFO("TARGET:auth_gssapi_client"
"FILE_TYPE:VFT_DLL"
"SOURCE_FILE:plugins/auth/auth_gssapi_client.c"
"ORIGINAL_FILE_NAME:auth_gssapi_client.dll"
"FILE_DESCRIPTION:Authentication plugin")
ADD_DEFINITIONS(-DHAVE_AUTH_GSSAPI_DYNAMIC=1)
SET(GSSAPI_SOURCES auth_gssapi_client.c sspi_client.c sspi_errmsg.c ${CMAKE_SOURCE_DIR}/plugins/plugin.def ${gssapi_RC})
ELSE()
IF(GSSAPI_FOUND)
SET(GSSAPI_SOURCES auth_gssapi_client.c gssapi_client.c gssapi_errmsg.c)
ENDIF()
ENDIF()
IF(GSSAPI_FOUND)
ADD_LIBRARY(auth_gssapi_client SHARED ${GSSAPI_SOURCES})
IF(WIN32)
TARGET_LINK_LIBRARIES(auth_gssapi_client secur32.lib)
ELSE()
TARGET_LINK_LIBRARIES(auth_gssapi_client ${GSSAPI_LIBS})
ENDIF()
SET_TARGET_PROPERTIES(auth_gssapi_client PROPERTIES PREFIX "")
INSTALL_PLUGIN(auth_gssapi_client ${CMAKE_BINARY_DIR}/plugins/auth)
SIGN_TARGET(auth_gssapi_client)
ENDIF()
ENDIF()

View File

@@ -0,0 +1,119 @@
/* Copyright (c) 2015-2016, Shuang Qiu, Robbie Harwood,
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 <my_global.h>
#include <mysql.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 */
#ifndef HAVE_GSSAPI_DYNAMIC
struct st_mysql_client_plugin_AUTHENTICATION auth_gssapi_plugin=
#else
struct st_mysql_client_plugin_AUTHENTICATION _mysql_client_plugin_declaration_ =
#endif
{
MYSQL_CLIENT_AUTHENTICATION_PLUGIN,
MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION,
"auth_gssapi_client",
"Shuang Qiu, Robbie Harwood, Vladislav Vaintroub, Georg Richter",
"GSSAPI/SSPI based authentication",
{0, 1, 0},
"BSD",
NULL,
NULL,
gssapi_auth_client
};

4
plugins/auth/common.h Normal file
View File

@@ -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

View File

@@ -0,0 +1,127 @@
/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
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;
}

View File

@@ -0,0 +1,75 @@
/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
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;
}

View File

@@ -0,0 +1,29 @@
/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
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);

View File

@@ -0,0 +1,51 @@
/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
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@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);

183
plugins/auth/sspi_client.c Normal file
View File

@@ -0,0 +1,183 @@
/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
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_common.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;
}

View File

@@ -0,0 +1,38 @@
/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
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);

150
plugins/auth/sspi_errmsg.c Normal file
View File

@@ -0,0 +1,150 @@
/* Copyright (c) 2015, Shuang Qiu, Robbie Harwood,
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;
}
}

View File

@@ -0,0 +1,15 @@
openssl req -x509 -newkey rsa:1024 \
-keyout server-key-enc.pem -out server-cert.pem \
-subj '/DC=com/DC=example/CN=server' -passout pass:qwerty
openssl rsa -in server-key-enc.pem -out server-key.pem \
-passin pass:qwerty -passout pass:
openssl req -x509 -newkey rsa:1024 \
-keyout client-key-enc.pem -out client-cert.pem \
-subj '/DC=com/DC=example/CN=client' -passout pass:qwerty
openssl rsa -in client-key-enc.pem -out client-key.pem \
-passin pass:qwerty -passout pass:
cat server-cert.pem client-cert.pem > ca-cert.pem

View File

@@ -0,0 +1,21 @@
-----BEGIN CERTIFICATE-----
MIIDXTCCAkWgAwIBAgIJAL4tmDe5DR0sMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzEwMjAyMDI4WhcNMTYwMzA5MjAyMDI4WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEApV9UfWmeXYdexOEn+adOm6FdJUhKMrjTiycwETmDXRVpY4wl+LNGsANp
ohSRovDjFiFO+Ti0bUhpf552oE23wYw+P6f0UY0KkV/PgSght1Ezfffe0BaEjI0X
tA5zdNmxzL3OUWJVcg+I4UE3rbYFHUgymu72P0IRXjmJv1tToNxUxbTBLxU/KAlq
Uy49upB3q3/IPOdP9UzAZDHnRv1gjwUzNgumfcc5d5lSsGpwLDYCQs4I539fCkBD
MfU2BN/qpmPhb/nm5ZUdFUFYGN+XxVPVpJLmeWVRwMSQR2LN5CkqnK9e2Q/QaJ53
G3AAng+fpfEGPpjQdFWuhFjQozOD0wIDAQABo1AwTjAdBgNVHQ4EFgQUyg6WfzL2
JhhjKm1Ex28s4Y3vNGQwHwYDVR0jBBgwFoAUyg6WfzL2JhhjKm1Ex28s4Y3vNGQw
DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAIrj/bHiRf8UJIfv8hyZ1
dXEqvqjxUMXtJ/QhhCQs42p9pHv+mPTkeXh0K18Oj6k/Vp4J1/0mp/kqiQUHt9yO
/3pJPc+JordTjlVLgb95cfBIs4yiPT9biGaA7j0Dh9EcDBOCT4v56Z9BLqGMfBUK
YeZ7ZecWmZCZOYk/X+CPB30GxLy5Wm9D50qEUXXBPZ9Bie6FYaQYOFlQlqxYuLX0
NVqLDvX6zz6FMsgqoyDJ1BMuMsjPDUUUrwGY+R3YqiqkPRbDkr8zvzpqiYvjTZi0
LTJO7GRfwzfhkeEPL/hl/TYdB1GZHixMrAKx1HGKHAa0sgWTWxQGYhfclH8DI7AR
Tw==
-----END CERTIFICATE-----

View File

@@ -29,6 +29,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <memory.h> #include <memory.h>
#include <errmsg.h> #include <errmsg.h>
#ifndef WIN32
#include <pthread.h>
#endif
#ifndef OK #ifndef OK
# define OK 0 # define OK 0
#endif #endif

View File

@@ -70,7 +70,7 @@ static int test_conc83(MYSQL *my)
rc= mysql_ping(mysql); rc= mysql_ping(mysql);
check_mysql_rc(rc, mysql); check_mysql_rc(rc, mysql);
rc= mysql_stmt_prepare(stmt, query, strlen(query)); rc= mysql_stmt_prepare(stmt, query, -1);
check_stmt_rc(rc, stmt); check_stmt_rc(rc, stmt);
diag("Ok"); diag("Ok");
@@ -78,7 +78,7 @@ static int test_conc83(MYSQL *my)
rc= mysql_kill(mysql, mysql_thread_id(mysql)); rc= mysql_kill(mysql, mysql_thread_id(mysql));
sleep(2); sleep(2);
rc= mysql_stmt_prepare(stmt, query, strlen(query)); rc= mysql_stmt_prepare(stmt, query, -1);
FAIL_IF(!rc, "Error expected"); FAIL_IF(!rc, "Error expected");
mysql_stmt_close(stmt); mysql_stmt_close(stmt);
@@ -98,7 +98,7 @@ static int test_conc60(MYSQL *mysql)
rc= mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void *)&x); rc= mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void *)&x);
rc= mysql_stmt_prepare(stmt, query, strlen(query)); rc= mysql_stmt_prepare(stmt, query, -1);
if (rc && mysql_stmt_errno(stmt) == 1146) { if (rc && mysql_stmt_errno(stmt) == 1146) {
diag("Internal test - customer data not available"); diag("Internal test - customer data not available");
mysql_stmt_close(stmt); mysql_stmt_close(stmt);