Merge pull request #532 from Hadis-Fard/AlwaysEncrypted
ODBC 17 support
This commit is contained in:
commit
ffe1a38f64
|
@ -43,6 +43,7 @@ const char AttachDBFileName[] = "AttachDbFileName";
|
|||
const char ConnectionPooling[] = "ConnectionPooling";
|
||||
const char Authentication[] = "Authentication";
|
||||
const char ColumnEncryption[] = "ColumnEncryption";
|
||||
const char Driver[] = "Driver";
|
||||
const char CEKeystoreProvider[] = "CEKeystoreProvider";
|
||||
const char CEKeystoreName[] = "CEKeystoreName";
|
||||
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
|
||||
|
@ -234,6 +235,15 @@ const connection_option PDO_CONN_OPTS[] = {
|
|||
CONN_ATTR_STRING,
|
||||
column_encryption_set_func::func
|
||||
},
|
||||
{
|
||||
PDOConnOptionNames::Driver,
|
||||
sizeof(PDOConnOptionNames::Driver),
|
||||
SQLSRV_CONN_OPTION_DRIVER,
|
||||
ODBCConnOptions::Driver,
|
||||
sizeof(ODBCConnOptions::Driver),
|
||||
CONN_ATTR_STRING,
|
||||
driver_set_func::func
|
||||
},
|
||||
{
|
||||
PDOConnOptionNames::CEKeystoreProvider,
|
||||
sizeof(PDOConnOptionNames::CEKeystoreProvider),
|
||||
|
|
|
@ -56,8 +56,8 @@ pdo_error PDO_ERRORS[] = {
|
|||
|
||||
{
|
||||
SQLSRV_ERROR_DRIVER_NOT_INSTALLED,
|
||||
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver 13 for SQL Server to "
|
||||
"communicate with SQL Server. Access the following URL to download the ODBC Driver 13 for SQL Server "
|
||||
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver for SQL Server to "
|
||||
"communicate with SQL Server. Access the following URL to download the ODBC Driver for SQL Server "
|
||||
"for %1!s!: "
|
||||
"http://go.microsoft.com/fwlink/?LinkId=163712", -1, true }
|
||||
},
|
||||
|
@ -380,7 +380,7 @@ pdo_error PDO_ERRORS[] = {
|
|||
{
|
||||
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
|
||||
{ IMSSP, (SQLCHAR*) "Invalid option for the Authentication keyword. Only SqlPassword or ActiveDirectoryPassword is supported.", -73, false }
|
||||
},
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_KEYSTORE_NAME_MISSING,
|
||||
{ IMSSP, (SQLCHAR*) "The name of the custom keystore provider is missing.", -74, false}
|
||||
|
@ -397,6 +397,14 @@ pdo_error PDO_ERRORS[] = {
|
|||
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
|
||||
{ IMSSP, (SQLCHAR*) "Invalid value for loading a custom keystore provider.", -77, false}
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_AE_DRIVER_NOT_INSTALLED,
|
||||
{ IMSSP, (SQLCHAR*) "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.", -78, false }
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_CONNECT_INVALID_DRIVER,
|
||||
{ IMSSP, (SQLCHAR*) "Invalid value %1!s! was specified for Driver option.", -79, true }
|
||||
},
|
||||
{ UINT_MAX, {} }
|
||||
};
|
||||
|
||||
|
|
|
@ -26,9 +26,8 @@
|
|||
#include <windows.h>
|
||||
#include <winver.h>
|
||||
#endif // _WIN32
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <sys/utsname.h>
|
||||
|
@ -49,11 +48,12 @@ const int INFO_BUFFER_LEN = 256;
|
|||
// processor architectures
|
||||
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
|
||||
|
||||
// ODBC driver name.
|
||||
const char* CONNECTION_STRING_DRIVER_NAME[] = {"Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};"};
|
||||
// ODBC driver names.
|
||||
// the order of this list should match the order of DRIVER_VERSION enum
|
||||
std::vector<std::string> CONNECTION_STRING_DRIVER_NAME{ "Driver={ODBC Driver 13 for SQL Server};", "Driver={ODBC Driver 11 for SQL Server};", "Driver={ODBC Driver 17 for SQL Server};" };
|
||||
|
||||
// default options if only the server is specified
|
||||
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes}";
|
||||
const char CONNECTION_STRING_DEFAULT_OPTIONS[] = "Mars_Connection={Yes};";
|
||||
|
||||
// connection option appended when no user name or password is given
|
||||
const char CONNECTION_OPTION_NO_CREDENTIALS[] = "Trusted_Connection={Yes};";
|
||||
|
@ -64,8 +64,8 @@ const char CONNECTION_OPTION_MARS_ON[] = "MARS_Connection={Yes};";
|
|||
// *** internal function prototypes ***
|
||||
|
||||
void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
|
||||
_Inout_opt_ HashTable* options_ht, _In_ const connection_option valid_conn_opts[],
|
||||
void* driver,_Inout_ std::string& connection_string TSRMLS_DC );
|
||||
_Inout_opt_ HashTable* options_ht, _In_ const connection_option valid_conn_opts[],
|
||||
void* driver,_Inout_ std::string& connection_string TSRMLS_DC );
|
||||
void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
|
||||
const char* get_processor_arch( void );
|
||||
void get_server_version( _Inout_ sqlsrv_conn* conn, _Outptr_result_buffer_(len) char** server_version, _Out_ SQLSMALLINT& len TSRMLS_DC );
|
||||
|
@ -99,13 +99,14 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
std::string conn_str;
|
||||
conn_str.reserve( DEFAULT_CONN_STR_LEN );
|
||||
sqlsrv_malloc_auto_ptr<sqlsrv_conn> conn;
|
||||
sqlsrv_malloc_auto_ptr<SQLWCHAR> wconn_string;
|
||||
unsigned int wconn_len = 0;
|
||||
bool is_pooled = false;
|
||||
|
||||
#ifdef _WIN32
|
||||
sqlsrv_context* henv = &henv_cp; // by default use the connection pooling henv
|
||||
is_pooled = true;
|
||||
#else
|
||||
sqlsrv_context* henv = &henv_ncp; // by default do not use the connection pooling henv
|
||||
is_pooled = false;
|
||||
#endif // _WIN32
|
||||
|
||||
try {
|
||||
|
@ -121,6 +122,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
( toupper( pooling_string[ 0 ] ) == 'O' && toupper( pooling_string[ 1 ] ) == 'N' ))
|
||||
{
|
||||
henv = &henv_cp;
|
||||
is_pooled = true;
|
||||
}
|
||||
#else
|
||||
// check the connection pooling setting to determine which henv to use to allocate the connection handle
|
||||
|
@ -130,11 +132,12 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
|
||||
|
||||
zval* option_z = NULL;
|
||||
option_z = zend_hash_index_find(options_ht, SQLSRV_CONN_OPTION_CONN_POOLING);
|
||||
option_z = zend_hash_index_find( options_ht, SQLSRV_CONN_OPTION_CONN_POOLING );
|
||||
if ( option_z ) {
|
||||
// if the option was found and it's not true, then use the non pooled environment handle
|
||||
if(( Z_TYPE_P( option_z ) == IS_STRING && !core_str_zval_is_true( option_z )) || !zend_is_true( option_z ) ) {
|
||||
henv = &henv_ncp;
|
||||
henv = &henv_ncp;
|
||||
is_pooled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,75 +145,51 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
|
||||
SQLHANDLE temp_conn_h;
|
||||
core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h TSRMLS_CC );
|
||||
|
||||
conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC );
|
||||
conn->set_func( driver_func );
|
||||
|
||||
build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_str TSRMLS_CC );
|
||||
|
||||
bool is_missing_driver = false;
|
||||
|
||||
for( std::size_t i = DRIVER_VERSION::MIN; i <= DRIVER_VERSION::MAX; ++i ) {
|
||||
conn_str = CONNECTION_STRING_DRIVER_NAME[i];
|
||||
build_connection_string_and_set_conn_attr(conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_str TSRMLS_CC);
|
||||
if ( conn->is_driver_set ) {
|
||||
r = core_odbc_connect( conn, conn_str, is_missing_driver, is_pooled );
|
||||
}
|
||||
else if ( conn->ce_option.enabled ) {
|
||||
conn_str = conn_str + CONNECTION_STRING_DRIVER_NAME[ DRIVER_VERSION::ODBC_DRIVER_17 ];
|
||||
r = core_odbc_connect( conn, conn_str, is_missing_driver, is_pooled );
|
||||
|
||||
// We only support UTF-8 encoding for connection string.
|
||||
// Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW
|
||||
wconn_len = static_cast<unsigned int>( conn_str.length() + 1 ) * sizeof( SQLWCHAR );
|
||||
CHECK_CUSTOM_ERROR( is_missing_driver, conn, SQLSRV_ERROR_AE_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>(conn_str.length()), &wconn_len );
|
||||
|
||||
CHECK_CUSTOM_ERROR( wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message())
|
||||
{
|
||||
for ( std::size_t i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST; ++i ) {
|
||||
is_missing_driver = false;
|
||||
std::string conn_str_driver = conn_str + CONNECTION_STRING_DRIVER_NAME[ DRIVER_VERSION(i) ];
|
||||
r = core_odbc_connect( conn, conn_str_driver, is_missing_driver, is_pooled );
|
||||
CHECK_CUSTOM_ERROR( is_missing_driver && ( i == DRIVER_VERSION::LAST ), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
|
||||
SQLSMALLINT output_conn_size;
|
||||
#ifndef _WIN32
|
||||
// unixODBC 2.3.1 requires a non-wide SQLDriverConnect call while pooling enabled.
|
||||
// connection handle has been allocated using henv_cp, means pooling enabled in a PHP script
|
||||
if ( henv == &henv_cp )
|
||||
{
|
||||
r = SQLDriverConnect( conn->handle(), NULL, (SQLCHAR*)conn_str.c_str(), SQL_NTS, NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
||||
}
|
||||
else
|
||||
{
|
||||
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
||||
}
|
||||
#else
|
||||
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
||||
#endif // !_WIN32
|
||||
|
||||
// clear the connection string from memory to remove sensitive data (such as a password).
|
||||
memset( const_cast<char*>(conn_str.c_str()), 0, conn_str.size());
|
||||
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
||||
conn_str.clear();
|
||||
if ( !SQL_SUCCEEDED( r )) {
|
||||
SQLCHAR state[SQL_SQLSTATE_BUFSIZE];
|
||||
SQLSMALLINT len;
|
||||
SQLRETURN sr = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
|
||||
bool missing_driver_error = ( SQL_SUCCEEDED( sr ) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' && state[4] == '2' );
|
||||
// if it's a IM002, meaning that the correct ODBC driver is not installed
|
||||
CHECK_CUSTOM_ERROR(missing_driver_error && ( i == DRIVER_VERSION::MAX ), conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
if ( !missing_driver_error ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
conn->driver_version = static_cast<DRIVER_VERSION>( i );
|
||||
if ( !is_missing_driver) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CHECK_SQL_ERROR( r, conn ) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
} // for
|
||||
} // else ce_option enabled
|
||||
|
||||
CHECK_SQL_ERROR( r, conn ) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
|
||||
CHECK_SQL_WARNING_AS_ERROR( r, conn ) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
CHECK_SQL_WARNING_AS_ERROR( r, conn ) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
|
||||
load_configure_ksp( conn );
|
||||
load_configure_ksp( conn );
|
||||
|
||||
// determine the version of the server we're connected to. The server version is left in the
|
||||
// connection upon return.
|
||||
// determine the version of the server we're connected to. The server version is left in the
|
||||
// connection upon return.
|
||||
//
|
||||
// unixODBC 2.3.1:
|
||||
// SQLGetInfo works when r = SQL_SUCCESS_WITH_INFO (non-pooled connection)
|
||||
|
@ -225,32 +204,29 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
#endif // !_WIN32
|
||||
}
|
||||
catch( std::bad_alloc& ) {
|
||||
memset( const_cast<char*>( conn_str.c_str()), 0, conn_str.size() );
|
||||
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
||||
conn_str.clear();
|
||||
conn->invalidate();
|
||||
DIE( "C++ memory allocation failure building the connection string." );
|
||||
}
|
||||
catch( std::out_of_range const& ex ) {
|
||||
memset( const_cast<char*>( conn_str.c_str()), 0, conn_str.size() );
|
||||
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
||||
conn_str.clear();
|
||||
LOG( SEV_ERROR, "C++ exception returned: %1!s!", ex.what() );
|
||||
conn->invalidate();
|
||||
throw;
|
||||
}
|
||||
catch( std::length_error const& ex ) {
|
||||
memset( const_cast<char*>( conn_str.c_str()), 0, conn_str.size() );
|
||||
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
||||
conn_str.clear();
|
||||
LOG( SEV_ERROR, "C++ exception returned: %1!s!", ex.what() );
|
||||
conn->invalidate();
|
||||
throw;
|
||||
}
|
||||
catch( core::CoreException& ) {
|
||||
memset( const_cast<char*>( conn_str.c_str()), 0, conn_str.size() );
|
||||
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
||||
conn_str.clear();
|
||||
conn->invalidate();
|
||||
throw;
|
||||
}
|
||||
|
||||
conn_str.clear();
|
||||
sqlsrv_conn* return_conn = conn;
|
||||
conn.transferred();
|
||||
|
||||
|
@ -258,6 +234,60 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
}
|
||||
|
||||
|
||||
// core_odbc_connect
|
||||
// calls odbc connect API to establish the connection to server
|
||||
// Parameters:
|
||||
// conn - The connection structure on which we establish the connection
|
||||
// conn_str - Connection string
|
||||
// missing_driver_error - indicates whether odbc driver is installed on client machine
|
||||
// is_pooled - indicate whether it is a pooled connection
|
||||
// Return - SQLRETURN status returned by SQLDriverConnect
|
||||
|
||||
SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str, _Inout_ bool& is_missing_driver, _In_ bool is_pooled )
|
||||
{
|
||||
SQLRETURN r = SQL_SUCCESS;
|
||||
sqlsrv_malloc_auto_ptr<SQLWCHAR> wconn_string;
|
||||
unsigned int wconn_len = static_cast<unsigned int>( conn_str.length() + 1 ) * sizeof( SQLWCHAR );
|
||||
|
||||
// We only support UTF-8 encoding for connection string.
|
||||
// Convert our UTF-8 connection string to UTF-16 before connecting with SQLDriverConnnectW
|
||||
wconn_string = utf16_string_from_mbcs_string( SQLSRV_ENCODING_UTF8, conn_str.c_str(), static_cast<unsigned int>( conn_str.length() ), &wconn_len );
|
||||
|
||||
CHECK_CUSTOM_ERROR( wconn_string == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE, get_last_error_message())
|
||||
{
|
||||
throw core::CoreException();
|
||||
}
|
||||
|
||||
SQLSMALLINT output_conn_size;
|
||||
#ifndef _WIN32
|
||||
// unixODBC 2.3.1 requires a non-wide SQLDriverConnect call while pooling enabled.
|
||||
// connection handle has been allocated using henv_cp, means pooling enabled in a PHP script
|
||||
if (is_pooled)
|
||||
{
|
||||
r = SQLDriverConnect( conn->handle(), NULL, (SQLCHAR*)conn_str.c_str(), SQL_NTS, NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
||||
}
|
||||
else
|
||||
{
|
||||
r = SQLDriverConnectW( conn->handle(), NULL, wconn_string, static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
||||
}
|
||||
#else
|
||||
r = SQLDriverConnectW( conn->handle(), NULL, wconn_string, static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
||||
#endif // !_WIN32
|
||||
|
||||
// clear the connection string from memory to remove sensitive data (such as a password).
|
||||
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
||||
conn_str.clear();
|
||||
|
||||
if (!SQL_SUCCEEDED(r)) {
|
||||
SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ];
|
||||
SQLSMALLINT len;
|
||||
SQLRETURN sr = SQLGetDiagField( SQL_HANDLE_DBC, conn->handle(), 1, SQL_DIAG_SQLSTATE, state, SQL_SQLSTATE_BUFSIZE, &len );
|
||||
// sql state IM002/IM003 in ODBC 17, means that the correct ODBC driver is not installed
|
||||
is_missing_driver = ( SQL_SUCCEEDED(sr) && state[0] == 'I' && state[1] == 'M' && state[2] == '0' && state[3] == '0' && (state[4] == '2' || state[4] == '3'));
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
// core_sqlsrv_begin_transaction
|
||||
// Begins a transaction on a specified connection. The current transaction
|
||||
|
@ -857,8 +887,7 @@ void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_l
|
|||
} // namespace
|
||||
|
||||
// simply add the parsed value to the connection string
|
||||
void conn_str_append_func::func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Inout_ std::string& conn_str
|
||||
TSRMLS_DC )
|
||||
void conn_str_append_func::func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Inout_ std::string& conn_str TSRMLS_DC )
|
||||
{
|
||||
const char* val_str = Z_STRVAL_P( value );
|
||||
size_t val_len = Z_STRLEN_P( value );
|
||||
|
@ -867,12 +896,72 @@ void conn_str_append_func::func( _In_ connection_option const* option, _In_ zval
|
|||
|
||||
// do nothing for connection pooling since we handled it earlier when
|
||||
// deciding which environment handle to use.
|
||||
void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/
|
||||
TSRMLS_DC )
|
||||
void conn_null_func::func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ TSRMLS_DC )
|
||||
{
|
||||
TSRMLS_C;
|
||||
}
|
||||
|
||||
void driver_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC )
|
||||
{
|
||||
const char* val_str = Z_STRVAL_P( value );
|
||||
size_t val_len = Z_STRLEN_P( value );
|
||||
std::string driver_option( "" );
|
||||
common_conn_str_append_func( option->odbc_name, val_str, val_len, driver_option TSRMLS_CC );
|
||||
|
||||
CHECK_CUSTOM_ERROR( std::find( CONNECTION_STRING_DRIVER_NAME.begin(), CONNECTION_STRING_DRIVER_NAME.end(), driver_option) == CONNECTION_STRING_DRIVER_NAME.end(), conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, val_str){
|
||||
throw core::CoreException();
|
||||
}
|
||||
conn->is_driver_set = true;
|
||||
conn_str += driver_option;
|
||||
}
|
||||
|
||||
void column_encryption_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC )
|
||||
{
|
||||
convert_to_string( value );
|
||||
const char* value_str = Z_STRVAL_P( value );
|
||||
|
||||
// Column Encryption is disabled by default unless it is explicitly 'Enabled'
|
||||
conn->ce_option.enabled = false;
|
||||
if ( !stricmp(value_str, "enabled" )) {
|
||||
conn->ce_option.enabled = true;
|
||||
}
|
||||
|
||||
conn_str += option->odbc_name;
|
||||
conn_str += "=";
|
||||
conn_str += value_str;
|
||||
conn_str += ";";
|
||||
}
|
||||
|
||||
void ce_ksp_provider_set_func::func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC )
|
||||
{
|
||||
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" )
|
||||
|
||||
size_t value_len = Z_STRLEN_P( value );
|
||||
|
||||
CHECK_CUSTOM_ERROR( value_len == 0, conn, SQLSRV_ERROR_KEYSTORE_INVALID_VALUE ) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
|
||||
switch ( option->conn_option_key ) {
|
||||
case SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER:
|
||||
conn->ce_option.ksp_path = value;
|
||||
conn->ce_option.ksp_required = true;
|
||||
break;
|
||||
case SQLSRV_CONN_OPTION_CEKEYSTORE_NAME:
|
||||
conn->ce_option.ksp_name = value;
|
||||
conn->ce_option.ksp_required = true;
|
||||
break;
|
||||
case SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY:
|
||||
conn->ce_option.ksp_encrypt_key = value;
|
||||
conn->ce_option.key_size = value_len;
|
||||
conn->ce_option.ksp_required = true;
|
||||
break;
|
||||
default:
|
||||
SQLSRV_ASSERT(false, "ce_ksp_provider_set_func: Invalid KSP option!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// helper function to evaluate whether a string value is true or false.
|
||||
// Values = ("true" or "1") are treated as true values. Everything else is treated as false.
|
||||
// Returns 1 for true and 0 for false.
|
||||
|
|
|
@ -151,6 +151,7 @@ OACR_WARNING_POP
|
|||
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
|
@ -189,6 +190,9 @@ namespace AzureADOptions {
|
|||
const char AZURE_AUTH_AD_PASSWORD[] = "ActiveDirectoryPassword";
|
||||
}
|
||||
|
||||
// the message returned by ODBC Driver for SQL Server
|
||||
const char ODBC_CONNECTION_BUSY_ERROR[] = "Connection is busy with results for another command";
|
||||
|
||||
// types for conversions on output parameters (though they can be used for input parameters, they are ignored)
|
||||
enum SQLSRV_PHPTYPE {
|
||||
MIN_SQLSRV_PHPTYPE = 1, // lowest value for a php type
|
||||
|
@ -1021,7 +1025,6 @@ struct sqlsrv_henv {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
//*********************************************************************************************************************************
|
||||
// Connection
|
||||
//*********************************************************************************************************************************
|
||||
|
@ -1035,11 +1038,13 @@ enum SERVER_VERSION {
|
|||
};
|
||||
|
||||
// supported driver versions.
|
||||
enum DRIVER_VERSION : size_t {
|
||||
MIN = 0,
|
||||
ODBC_DRIVER_13 = MIN,
|
||||
ODBC_DRIVER_11 = 1,
|
||||
MAX = ODBC_DRIVER_11,
|
||||
// the latest RTWed ODBC is the first one
|
||||
enum DRIVER_VERSION : std::size_t{
|
||||
FIRST = 0,
|
||||
ODBC_DRIVER_13 = FIRST,
|
||||
ODBC_DRIVER_11 = 1,
|
||||
ODBC_DRIVER_17 = 2,
|
||||
LAST = ODBC_DRIVER_17
|
||||
};
|
||||
|
||||
// forward decl
|
||||
|
@ -1067,16 +1072,15 @@ struct sqlsrv_conn : public sqlsrv_context {
|
|||
// instance variables
|
||||
SERVER_VERSION server_version; // version of the server that we're connected to
|
||||
|
||||
DRIVER_VERSION driver_version;
|
||||
|
||||
col_encryption_option ce_option; // holds the details of what are required to enable column encryption
|
||||
bool is_driver_set;
|
||||
|
||||
// initialize with default values
|
||||
sqlsrv_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_opt_ void* drv, _In_ SQLSRV_ENCODING encoding TSRMLS_DC ) :
|
||||
sqlsrv_context( h, SQL_HANDLE_DBC, e, drv, encoding )
|
||||
{
|
||||
server_version = SERVER_VERSION_UNKNOWN;
|
||||
driver_version = ODBC_DRIVER_13;
|
||||
is_driver_set = false;
|
||||
}
|
||||
|
||||
// sqlsrv_conn has no destructor since its allocated using placement new, which requires that the destructor be
|
||||
|
@ -1104,6 +1108,7 @@ const char ApplicationIntent[] = "ApplicationIntent";
|
|||
const char AttachDBFileName[] = "AttachDbFileName";
|
||||
const char Authentication[] = "Authentication";
|
||||
const char ColumnEncryption[] = "ColumnEncryption";
|
||||
const char Driver[] = "Driver";
|
||||
const char CEKeystoreProvider[] = "CEKeystoreProvider";
|
||||
const char CEKeystoreName[] = "CEKeystoreName";
|
||||
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
|
||||
|
@ -1154,6 +1159,7 @@ enum SQLSRV_CONN_OPTIONS {
|
|||
SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER,
|
||||
SQLSRV_CONN_OPTION_AUTHENTICATION,
|
||||
SQLSRV_CONN_OPTION_COLUMNENCRYPTION,
|
||||
SQLSRV_CONN_OPTION_DRIVER,
|
||||
SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER,
|
||||
SQLSRV_CONN_OPTION_CEKEYSTORE_NAME,
|
||||
SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY,
|
||||
|
@ -1197,18 +1203,30 @@ struct connection_option {
|
|||
void (*func)( connection_option const*, zval* value, sqlsrv_conn* conn, std::string& conn_str TSRMLS_DC );
|
||||
};
|
||||
|
||||
// connection attribute functions
|
||||
|
||||
// simply add the parsed value to the connection string
|
||||
struct conn_str_append_func {
|
||||
|
||||
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Inout_ std::string& conn_str TSRMLS_DC );
|
||||
};
|
||||
|
||||
struct conn_null_func {
|
||||
|
||||
static void func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/
|
||||
TSRMLS_DC );
|
||||
static void func( connection_option const* /*option*/, zval* /*value*/, sqlsrv_conn* /*conn*/, std::string& /*conn_str*/ TSRMLS_DC );
|
||||
};
|
||||
|
||||
struct column_encryption_set_func {
|
||||
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
|
||||
};
|
||||
|
||||
struct driver_set_func {
|
||||
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
|
||||
};
|
||||
|
||||
struct ce_ksp_provider_set_func {
|
||||
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
|
||||
};
|
||||
|
||||
|
||||
// factory to create a connection (since they are subclassed to instantiate statements)
|
||||
typedef sqlsrv_conn* (*driver_conn_factory)( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv TSRMLS_DC );
|
||||
|
||||
|
@ -1217,6 +1235,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
|||
_Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
|
||||
_Inout_opt_ HashTable* options_ht, _In_ error_callback err, _In_ const connection_option valid_conn_opts[],
|
||||
_In_ void* driver, _In_z_ const char* driver_func TSRMLS_DC );
|
||||
SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str, _Inout_ bool& is_missing_driver, _In_ bool is_pooled );
|
||||
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC );
|
||||
void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) const char* sql, _In_ SQLLEN sql_len TSRMLS_DC );
|
||||
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
|
||||
|
@ -1620,6 +1639,8 @@ enum SQLSRV_ERROR_CODES {
|
|||
|
||||
SQLSRV_ERROR_ODBC,
|
||||
SQLSRV_ERROR_DRIVER_NOT_INSTALLED,
|
||||
SQLSRV_ERROR_AE_DRIVER_NOT_INSTALLED,
|
||||
SQLSRV_ERROR_CONNECT_INVALID_DRIVER,
|
||||
SQLSRV_ERROR_ZEND_HASH,
|
||||
SQLSRV_ERROR_INVALID_PARAMETER_PHPTYPE,
|
||||
SQLSRV_ERROR_INVALID_PARAMETER_SQLTYPE,
|
||||
|
@ -1668,10 +1689,6 @@ enum SQLSRV_ERROR_CODES {
|
|||
|
||||
};
|
||||
|
||||
// the message returned by ODBC Driver 11 for SQL Server
|
||||
static const char* CONNECTION_BUSY_ODBC_ERROR[] = { "[Microsoft][ODBC Driver 13 for SQL Server]Connection is busy with results for another command",
|
||||
"[Microsoft][ODBC Driver 11 for SQL Server]Connection is busy with results for another command" };
|
||||
|
||||
// SQLSTATE for all internal errors
|
||||
extern SQLCHAR IMSSP[];
|
||||
|
||||
|
@ -1847,8 +1864,12 @@ namespace core {
|
|||
|
||||
throw CoreException();
|
||||
}
|
||||
std::size_t driver_version = stmt->conn->driver_version;
|
||||
if( !strcmp( reinterpret_cast<const char*>( err_msg ), CONNECTION_BUSY_ODBC_ERROR[driver_version] )) {
|
||||
|
||||
// the message returned by ODBC Driver for SQL Server
|
||||
const std::string connection_busy_error( ODBC_CONNECTION_BUSY_ERROR );
|
||||
const std::string returned_error( reinterpret_cast<char*>( err_msg ));
|
||||
|
||||
if(( returned_error.find( connection_busy_error ) != std::string::npos )) {
|
||||
|
||||
THROW_CORE_ERROR( stmt, SQLSRV_ERROR_MARS_OFF );
|
||||
}
|
||||
|
@ -2418,73 +2439,18 @@ sqlsrv_conn* allocate_conn( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void*
|
|||
|
||||
} // namespace core
|
||||
|
||||
// connection attribute functions
|
||||
template <unsigned int Attr>
|
||||
struct str_conn_attr_func {
|
||||
|
||||
static void func( connection_option const* /*option*/, zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
|
||||
{
|
||||
try {
|
||||
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_STRVAL_P( value )),
|
||||
static_cast<SQLINTEGER>(Z_STRLEN_P( value )) TSRMLS_CC );
|
||||
core::SQLSetConnectAttr( conn, Attr, reinterpret_cast<SQLPOINTER>( Z_STRVAL_P( value )), static_cast<SQLINTEGER>( Z_STRLEN_P( value )) TSRMLS_CC );
|
||||
}
|
||||
catch( core::CoreException& ) {
|
||||
catch ( core::CoreException& ) {
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct column_encryption_set_func {
|
||||
|
||||
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC)
|
||||
{
|
||||
convert_to_string(value);
|
||||
const char* value_str = Z_STRVAL_P(value);
|
||||
|
||||
// Column Encryption is disabled by default unless it is explicitly 'Enabled'
|
||||
conn->ce_option.enabled = false;
|
||||
if (!stricmp(value_str, "enabled")) {
|
||||
conn->ce_option.enabled = true;
|
||||
}
|
||||
|
||||
conn_str += option->odbc_name;
|
||||
conn_str += "=";
|
||||
conn_str += value_str;
|
||||
conn_str += ";";
|
||||
}
|
||||
};
|
||||
|
||||
struct ce_ksp_provider_set_func {
|
||||
|
||||
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC)
|
||||
{
|
||||
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" )
|
||||
|
||||
size_t value_len = Z_STRLEN_P( value );
|
||||
|
||||
CHECK_CUSTOM_ERROR( value_len == 0, conn, SQLSRV_ERROR_KEYSTORE_INVALID_VALUE ) {
|
||||
throw core::CoreException();
|
||||
}
|
||||
|
||||
switch ( option->conn_option_key ) {
|
||||
case SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER:
|
||||
conn->ce_option.ksp_path = value;
|
||||
conn->ce_option.ksp_required = true;
|
||||
break;
|
||||
case SQLSRV_CONN_OPTION_CEKEYSTORE_NAME:
|
||||
conn->ce_option.ksp_name = value;
|
||||
conn->ce_option.ksp_required = true;
|
||||
break;
|
||||
case SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY:
|
||||
conn->ce_option.ksp_encrypt_key = value;
|
||||
conn->ce_option.key_size = value_len;
|
||||
conn->ce_option.ksp_required = true;
|
||||
break;
|
||||
default:
|
||||
SQLSRV_ASSERT( false, "ce_ksp_provider_set_func: Invalid KSP option!" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // CORE_SQLSRV_H
|
||||
|
|
|
@ -134,8 +134,7 @@ struct bool_conn_attr_func {
|
|||
static void func( connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ TSRMLS_DC )
|
||||
{
|
||||
try {
|
||||
core::SQLSetConnectAttr(conn, Attr, reinterpret_cast<SQLPOINTER>((zend_long)zend_is_true(value)),
|
||||
SQL_IS_UINTEGER TSRMLS_CC);
|
||||
core::SQLSetConnectAttr(conn, Attr, reinterpret_cast<SQLPOINTER>((zend_long)zend_is_true(value)), SQL_IS_UINTEGER TSRMLS_CC);
|
||||
|
||||
}
|
||||
catch( core::CoreException& ) {
|
||||
|
@ -189,6 +188,7 @@ const char CharacterSet[] = "CharacterSet";
|
|||
const char Authentication[] = "Authentication";
|
||||
const char ConnectionPooling[] = "ConnectionPooling";
|
||||
const char ColumnEncryption[] = "ColumnEncryption";
|
||||
const char Driver[] = "Driver";
|
||||
const char CEKeystoreProvider[] = "CEKeystoreProvider";
|
||||
const char CEKeystoreName[] = "CEKeystoreName";
|
||||
const char CEKeystoreEncryptKey[] = "CEKeystoreEncryptKey";
|
||||
|
@ -316,6 +316,15 @@ const connection_option SS_CONN_OPTS[] = {
|
|||
CONN_ATTR_STRING,
|
||||
column_encryption_set_func::func
|
||||
},
|
||||
{
|
||||
SSConnOptionNames::Driver,
|
||||
sizeof(SSConnOptionNames::Driver),
|
||||
SQLSRV_CONN_OPTION_DRIVER,
|
||||
ODBCConnOptions::Driver,
|
||||
sizeof(ODBCConnOptions::Driver),
|
||||
CONN_ATTR_STRING,
|
||||
driver_set_func::func
|
||||
},
|
||||
{
|
||||
SSConnOptionNames::CEKeystoreProvider,
|
||||
sizeof(SSConnOptionNames::CEKeystoreProvider),
|
||||
|
|
|
@ -300,8 +300,8 @@ ss_error SS_ERRORS[] = {
|
|||
|
||||
{
|
||||
SQLSRV_ERROR_DRIVER_NOT_INSTALLED,
|
||||
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver 11 or 13 for SQL Server. "
|
||||
"Access the following URL to download the ODBC Driver 11 or 13 for SQL Server for %1!s!: "
|
||||
{ IMSSP, (SQLCHAR*) "This extension requires the Microsoft ODBC Driver for SQL Server. "
|
||||
"Access the following URL to download the ODBC Driver for SQL Server for %1!s!: "
|
||||
"http://go.microsoft.com/fwlink/?LinkId=163712", -49, true }
|
||||
},
|
||||
|
||||
|
@ -376,8 +376,7 @@ ss_error SS_ERRORS[] = {
|
|||
SS_SQLSRV_WARNING_FIELD_NAME_EMPTY,
|
||||
{ SSPWARN, (SQLCHAR*)"An empty field name was skipped by sqlsrv_fetch_object.", -100, false }
|
||||
},
|
||||
|
||||
{
|
||||
{
|
||||
SQLSRV_ERROR_KEYSTORE_NAME_MISSING,
|
||||
{ IMSSP, (SQLCHAR*) "The name of the custom keystore provider is missing.", -101, false}
|
||||
},
|
||||
|
@ -393,7 +392,14 @@ ss_error SS_ERRORS[] = {
|
|||
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
|
||||
{ IMSSP, (SQLCHAR*) "Invalid value for loading a custom keystore provider.", -104, false}
|
||||
},
|
||||
|
||||
{
|
||||
SQLSRV_ERROR_AE_DRIVER_NOT_INSTALLED,
|
||||
{ IMSSP, (SQLCHAR*) "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.", -105, false }
|
||||
},
|
||||
{
|
||||
SQLSRV_ERROR_CONNECT_INVALID_DRIVER,
|
||||
{ IMSSP, (SQLCHAR*) "Invalid value %1!s! was specified for Driver option.", -106, true }
|
||||
},
|
||||
// terminate the list of errors/warnings
|
||||
{ UINT_MAX, {} }
|
||||
};
|
||||
|
|
|
@ -4,66 +4,105 @@ Test new connection keyword ColumnEncryption
|
|||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
require_once("MsSetup.inc");
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Enabled;";
|
||||
try
|
||||
require_once("MsSetup.inc");
|
||||
$msodbcsql_maj = "";
|
||||
|
||||
try
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:server = $server", $uid, $pwd );
|
||||
$msodbcsql_ver = $conn->getAttribute( PDO::ATTR_CLIENT_VERSION )['DriverVer'];
|
||||
$msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
|
||||
}
|
||||
catch( PDOException $e )
|
||||
{
|
||||
echo "Failed to connect\n";
|
||||
print_r( $e->getMessage() );
|
||||
echo "\n";
|
||||
}
|
||||
|
||||
test_ColumnEncryption( $server, $uid, $pwd, $msodbcsql_maj );
|
||||
echo "Done";
|
||||
|
||||
|
||||
function verify_output( $PDOerror, $expected )
|
||||
{
|
||||
if( strpos( $PDOerror->getMessage(), $expected ) === false )
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:server = $server ; $connectionInfo", $uid, $pwd );
|
||||
echo "Connected successfully with ColumnEncryption enabled.\n";
|
||||
}
|
||||
catch( PDOException $e )
|
||||
{
|
||||
echo "Failed to connect with ColumnEncryption enabled.\n";
|
||||
print_r( $e->getMessage() );
|
||||
print_r( $PDOerror->getMessage() );
|
||||
echo "\n";
|
||||
}
|
||||
$conn = null;
|
||||
}
|
||||
|
||||
function test_ColumnEncryption( $server, $uid, $pwd, $msodbcsql_maj )
|
||||
{
|
||||
// Only works for ODBC 17
|
||||
////////////////////////////////////////
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = false;";
|
||||
$connectionInfo = "ColumnEncryption = Enabled;";
|
||||
try
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:server = $server ; $connectionInfo", $uid, $pwd );
|
||||
}
|
||||
catch( PDOException $e )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( $e->getMessage() );
|
||||
echo "\n";
|
||||
if($msodbcsql_maj < 17)
|
||||
{
|
||||
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
|
||||
verify_output( $e, $expected );
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r( $e->getMessage() );
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
// Works for ODBC 17, ODBC 13
|
||||
////////////////////////////////////////
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = 1;";
|
||||
$connectionInfo = "ColumnEncryption = Disabled;";
|
||||
try
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:server = $server ; $connectionInfo", $uid, $pwd );
|
||||
}
|
||||
catch( PDOException $e )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( $e->getMessage() );
|
||||
echo "\n";
|
||||
if($msodbcsql_maj < 13)
|
||||
{
|
||||
$expected = "Invalid connection string attribute";
|
||||
verify_output( $e, $expected );
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r( $e->getMessage() );
|
||||
echo "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// should fail for all ODBC drivers
|
||||
////////////////////////////////////////
|
||||
$connectionInfo = "Database = $databaseName; ColumnEncryption = Disabled;";
|
||||
$connectionInfo = "ColumnEncryption = false;";
|
||||
try
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:server = $server ; $connectionInfo", $uid, $pwd );
|
||||
echo "Connected successfully with ColumnEncryption disabled.\n";
|
||||
}
|
||||
catch( PDOException $e )
|
||||
{
|
||||
echo "Failed to connect with ColumnEncryption disabled.\n";
|
||||
print_r( $e->getMessage() );
|
||||
echo "\n";
|
||||
$expected = "Invalid value specified for connection string attribute 'ColumnEncryption'";
|
||||
verify_output( $e, $expected );
|
||||
}
|
||||
$conn = null;
|
||||
echo "Done\n";
|
||||
|
||||
// should fail for all ODBC drivers
|
||||
////////////////////////////////////////
|
||||
$connectionInfo = "ColumnEncryption = 1;";
|
||||
try
|
||||
{
|
||||
$conn = new PDO( "sqlsrv:server = $server ; $connectionInfo", $uid, $pwd );
|
||||
}
|
||||
catch( PDOException $e )
|
||||
{
|
||||
$expected = "Invalid value specified for connection string attribute 'ColumnEncryption'";
|
||||
verify_output( $e, $expected );
|
||||
}
|
||||
}
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
Connected successfully with ColumnEncryption enabled\.
|
||||
Failed to connect\.
|
||||
SQLSTATE\[08001\]: .*\[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]Invalid value specified for connection string attribute 'ColumnEncryption'
|
||||
Failed to connect\.
|
||||
SQLSTATE\[08001\]: .*\[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]Invalid value specified for connection string attribute 'ColumnEncryption'
|
||||
Connected successfully with ColumnEncryption disabled\.
|
||||
--EXPECT--
|
||||
Done
|
111
test/functional/sqlsrv/sqlsrv_connect_driver.phpt
Normal file
111
test/functional/sqlsrv/sqlsrv_connect_driver.phpt
Normal file
|
@ -0,0 +1,111 @@
|
|||
--TEST--
|
||||
Test new connection keyword Driver with valid and invalid values
|
||||
--SKIPIF--
|
||||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
sqlsrv_configure( 'WarningsReturnAsErrors', 0 );
|
||||
require( 'MsSetup.inc' );
|
||||
|
||||
$connectionOptions = array("Database"=>$database,"UID"=>$userName, "PWD"=>$userPassword);
|
||||
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||
if ($conn === false)
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
$msodbcsql_ver = sqlsrv_client_info($conn)['DriverVer'];
|
||||
$msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
|
||||
sqlsrv_close($conn);
|
||||
|
||||
// start test
|
||||
test_valid_values($msodbcsql_maj,$server ,$connectionOptions);
|
||||
test_invalid_values($msodbcsql_maj,$server ,$connectionOptions);
|
||||
echo "Done";
|
||||
// end test
|
||||
|
||||
///////////////////////////
|
||||
function connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions ,$expected = '' )
|
||||
{
|
||||
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||
if ($conn === false)
|
||||
{
|
||||
if( strpos(sqlsrv_errors($conn)[0]['message'], $expected) === false )
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function test_valid_values( $msodbcsql_maj ,$server ,$connectionOptions){
|
||||
$value = "";
|
||||
// Test with {}
|
||||
switch ( $msodbcsql_maj )
|
||||
{
|
||||
case 17:
|
||||
$value = "{ODBC Driver 17 for SQL Server}";
|
||||
break;
|
||||
case 13:
|
||||
$value = "{ODBC Driver 13 for SQL Server}";
|
||||
break;
|
||||
case 12:
|
||||
case 11:
|
||||
$value = "{ODBC Driver 11 for SQL Server}";
|
||||
break;
|
||||
default:
|
||||
$value = "invalid value";
|
||||
}
|
||||
$connectionOptions['Driver']=$value;
|
||||
connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions );
|
||||
|
||||
// Test without {}
|
||||
switch ( $msodbcsql_maj )
|
||||
{
|
||||
case 17:
|
||||
$value = "ODBC Driver 17 for SQL Server";
|
||||
break;
|
||||
case 13:
|
||||
$value = "ODBC Driver 13 for SQL Server";
|
||||
break;
|
||||
case 12:
|
||||
case 11:
|
||||
$value = "ODBC Driver 11 for SQL Server";
|
||||
break;
|
||||
default:
|
||||
$value = "invalid value";
|
||||
}
|
||||
|
||||
$connectionOptions['Driver']=$value;
|
||||
connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions );
|
||||
}
|
||||
|
||||
function test_invalid_values($msodbcsql_maj ,$server ,$connectionOptions){
|
||||
// test invalid value
|
||||
$value = "{SQL Server Native Client 11.0}";
|
||||
$connectionOptions['Driver']=$value;
|
||||
$expected = "Invalid value $value was specified for Driver option.";
|
||||
connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions ,$expected );
|
||||
|
||||
$value = "SQL Server Native Client 11.0";
|
||||
$connectionOptions['Driver']=$value;
|
||||
$expected = "Invalid value $value was specified for Driver option.";
|
||||
connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions ,$expected );
|
||||
|
||||
$value = "ODBC Driver 00 for SQL Server";
|
||||
$connectionOptions['Driver']=$value;
|
||||
$expected = "Invalid value $value was specified for Driver option.";
|
||||
connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions ,$expected );
|
||||
|
||||
$value = 123;
|
||||
$connectionOptions['Driver']=$value;
|
||||
$expected = "Invalid value type for option Driver was specified. String type was expected.";
|
||||
connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions ,$expected );
|
||||
|
||||
$value = false;
|
||||
$connectionOptions['Driver']=$value;
|
||||
$expected = "Invalid value type for option Driver was specified. String type was expected.";
|
||||
connect_verify_output( $msodbcsql_maj ,$server ,$connectionOptions ,$expected );
|
||||
}
|
||||
|
||||
?>
|
||||
--EXPECT--
|
||||
Done
|
|
@ -4,84 +4,99 @@ Test new connection keyword ColumnEncryption
|
|||
<?php require('skipif.inc'); ?>
|
||||
--FILE--
|
||||
<?php
|
||||
sqlsrv_configure( 'WarningsReturnAsErrors', 1 );
|
||||
sqlsrv_configure( 'LogSeverity', SQLSRV_LOG_SEVERITY_ALL );
|
||||
require( 'MsSetup.inc' );
|
||||
$connectionInfo = array( "Database"=>$databaseName, "UID"=>$uid, "PWD"=>$pwd,
|
||||
"ColumnEncryption"=>'Enabled');
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
sqlsrv_configure( 'WarningsReturnAsErrors', 0 );
|
||||
require( 'MsSetup.inc' );
|
||||
|
||||
$connectionOptions = array("Database"=>$database,"UID"=>$userName, "PWD"=>$userPassword);
|
||||
test_ColumnEncryption($server, $connectionOptions);
|
||||
echo "Done";
|
||||
|
||||
function test_ColumnEncryption($server ,$connectionOptions){
|
||||
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||
if ($conn === false)
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
$msodbcsql_ver = sqlsrv_client_info($conn)['DriverVer'];
|
||||
$msodbcsql_maj = explode(".", $msodbcsql_ver)[0];
|
||||
|
||||
// Only works for ODBC 17
|
||||
$connectionOptions['ColumnEncryption']='Enabled';
|
||||
$conn = sqlsrv_connect( $server, $connectionOptions );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
if($msodbcsql_maj < 17){
|
||||
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
|
||||
if( strcasecmp(sqlsrv_errors($conn)[0]['message'], $expected ) != 0 )
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
|
||||
// Works for ODBC 17, ODBC 13
|
||||
$connectionOptions['ColumnEncryption']='Disabled';
|
||||
$conn = sqlsrv_connect( $server, $connectionOptions );
|
||||
if( $conn === false )
|
||||
{
|
||||
if($msodbcsql_maj < 13)
|
||||
{
|
||||
$expected_substr = "Invalid connection string attribute";
|
||||
if( strpos(sqlsrv_errors($conn)[0]['message'], $expected_substr ) === false )
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Connected successfully with ColumnEncryption enabled.\n";
|
||||
sqlsrv_close( $conn );
|
||||
sqlsrv_close($conn);
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
$connectionInfo['ColumnEncryption']='false';
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
|
||||
// should fail for all ODBC drivers
|
||||
$connectionOptions['ColumnEncryption']='false';
|
||||
$conn = sqlsrv_connect( $server, $connectionOptions );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
$expected_substr = "Invalid value specified for connection string attribute 'ColumnEncryption'";
|
||||
if( strpos(sqlsrv_errors($conn)[0]['message'], $expected_substr ) === false )
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////
|
||||
$connectionInfo['ColumnEncryption']=true;
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
|
||||
// should fail for all ODBC drivers
|
||||
$connectionOptions['ColumnEncryption']=true;
|
||||
$conn = sqlsrv_connect( $server, $connectionOptions );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
$expected_substr = "Invalid value type for option ColumnEncryption was specified. String type was expected.";
|
||||
if( strpos(sqlsrv_errors($conn)[0]['message'], $expected_substr ) === false )
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
////////////////////////////////////////
|
||||
$connectionInfo['ColumnEncryption']='Disabled';
|
||||
$conn = sqlsrv_connect( $server, $connectionInfo );
|
||||
|
||||
// should fail for all ODBC drivers
|
||||
$connectionOptions['ColumnEncryption']=false;
|
||||
$conn = sqlsrv_connect( $server, $connectionOptions );
|
||||
if( $conn === false )
|
||||
{
|
||||
echo "Failed to connect.\n";
|
||||
print_r( sqlsrv_errors() );
|
||||
$expected_substr = "Invalid value type for option ColumnEncryption was specified. String type was expected.";
|
||||
if( strpos(sqlsrv_errors($conn)[0]['message'], $expected_substr ) === false )
|
||||
{
|
||||
print_r(sqlsrv_errors());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
echo "Connected successfully with ColumnEncryption disabled.\n";
|
||||
sqlsrv_close( $conn );
|
||||
}
|
||||
|
||||
echo "Done\n";
|
||||
}
|
||||
?>
|
||||
--EXPECTREGEX--
|
||||
Connected successfully with ColumnEncryption enabled.
|
||||
Failed to connect.
|
||||
Array
|
||||
\(
|
||||
\[0\] => Array
|
||||
\(
|
||||
\[0\] => 08001
|
||||
\[SQLSTATE\] => 08001
|
||||
\[1\] => 0
|
||||
\[code\] => 0
|
||||
\[2\] => .*\[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]Invalid value specified for connection string attribute 'ColumnEncryption'
|
||||
\[message\] => .*\[Microsoft\]\[ODBC Driver 1[1-9] for SQL Server\]Invalid value specified for connection string attribute 'ColumnEncryption'
|
||||
\)
|
||||
|
||||
\)
|
||||
Failed to connect.
|
||||
Array
|
||||
\(
|
||||
\[0\] => Array
|
||||
\(
|
||||
\[0\] => IMSSP
|
||||
\[SQLSTATE\] => IMSSP
|
||||
\[1\] => -33
|
||||
\[code\] => -33
|
||||
\[2\] => Invalid value type for option ColumnEncryption was specified. String type was expected.
|
||||
\[message\] => Invalid value type for option ColumnEncryption was specified. String type was expected.
|
||||
\)
|
||||
|
||||
\)
|
||||
Connected successfully with ColumnEncryption disabled.
|
||||
--EXPECT--
|
||||
Done
|
|
@ -187,13 +187,13 @@ array\(1\) \{
|
|||
\["SQLSTATE"\]=>
|
||||
string\(5\) "IMSSP"
|
||||
\[1\]=>
|
||||
int\(-1\)
|
||||
int\(-106\)
|
||||
\["code"\]=>
|
||||
int\(-1\)
|
||||
int\(-106\)
|
||||
\[2\]=>
|
||||
string\([0-9]+\) "Invalid option .* was passed to sqlsrv_connect."
|
||||
string\([0-9]+\) "Invalid value SQL Server Native Client 11.0 was specified for Driver option."
|
||||
\["message"\]=>
|
||||
string\([0-9]+\) "Invalid option .* was passed to sqlsrv_connect."
|
||||
string\([0-9]+\) "Invalid value SQL Server Native Client 11.0 was specified for Driver option."
|
||||
\}
|
||||
\}
|
||||
Test sqlsrv_connect with driver injection
|
||||
|
|
Loading…
Reference in a new issue