Merge pull request #768 from david-puglielli/azure-key-vault-support
Azure key vault support
This commit is contained in:
commit
e0f9afae2f
|
@ -40,17 +40,20 @@ const char Server[] = "Server";
|
||||||
const char APP[] = "APP";
|
const char APP[] = "APP";
|
||||||
const char ApplicationIntent[] = "ApplicationIntent";
|
const char ApplicationIntent[] = "ApplicationIntent";
|
||||||
const char AttachDBFileName[] = "AttachDbFileName";
|
const char AttachDBFileName[] = "AttachDbFileName";
|
||||||
const char ConnectionPooling[] = "ConnectionPooling";
|
|
||||||
const char Authentication[] = "Authentication";
|
const char Authentication[] = "Authentication";
|
||||||
const char Driver[] = "Driver";
|
|
||||||
#ifdef _WIN32
|
|
||||||
const char ColumnEncryption[] = "ColumnEncryption";
|
const char ColumnEncryption[] = "ColumnEncryption";
|
||||||
|
const char ConnectionPooling[] = "ConnectionPooling";
|
||||||
|
#ifdef _WIN32
|
||||||
const char ConnectRetryCount[] = "ConnectRetryCount";
|
const char ConnectRetryCount[] = "ConnectRetryCount";
|
||||||
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
const char Database[] = "Database";
|
const char Database[] = "Database";
|
||||||
|
const char Driver[] = "Driver";
|
||||||
const char Encrypt[] = "Encrypt";
|
const char Encrypt[] = "Encrypt";
|
||||||
const char Failover_Partner[] = "Failover_Partner";
|
const char Failover_Partner[] = "Failover_Partner";
|
||||||
|
const char KeyStoreAuthentication[] = "KeyStoreAuthentication";
|
||||||
|
const char KeyStorePrincipalId[] = "KeyStorePrincipalId";
|
||||||
|
const char KeyStoreSecret[] = "KeyStoreSecret";
|
||||||
const char LoginTimeout[] = "LoginTimeout";
|
const char LoginTimeout[] = "LoginTimeout";
|
||||||
const char MARS_Option[] = "MultipleActiveResultSets";
|
const char MARS_Option[] = "MultipleActiveResultSets";
|
||||||
const char MultiSubnetFailover[] = "MultiSubnetFailover";
|
const char MultiSubnetFailover[] = "MultiSubnetFailover";
|
||||||
|
@ -231,7 +234,6 @@ const connection_option PDO_CONN_OPTS[] = {
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
driver_set_func::func
|
driver_set_func::func
|
||||||
},
|
},
|
||||||
#ifdef _WIN32
|
|
||||||
{
|
{
|
||||||
PDOConnOptionNames::ColumnEncryption,
|
PDOConnOptionNames::ColumnEncryption,
|
||||||
sizeof(PDOConnOptionNames::ColumnEncryption),
|
sizeof(PDOConnOptionNames::ColumnEncryption),
|
||||||
|
@ -241,6 +243,7 @@ const connection_option PDO_CONN_OPTS[] = {
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
column_encryption_set_func::func
|
column_encryption_set_func::func
|
||||||
},
|
},
|
||||||
|
#ifdef _WIN32
|
||||||
{
|
{
|
||||||
PDOConnOptionNames::ConnectRetryCount,
|
PDOConnOptionNames::ConnectRetryCount,
|
||||||
sizeof( PDOConnOptionNames::ConnectRetryCount ),
|
sizeof( PDOConnOptionNames::ConnectRetryCount ),
|
||||||
|
@ -287,6 +290,33 @@ const connection_option PDO_CONN_OPTS[] = {
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
conn_str_append_func::func
|
conn_str_append_func::func
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
PDOConnOptionNames::KeyStoreAuthentication,
|
||||||
|
sizeof( PDOConnOptionNames::KeyStoreAuthentication ),
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_AUTHENTICATION,
|
||||||
|
ODBCConnOptions::KeyStoreAuthentication,
|
||||||
|
sizeof( ODBCConnOptions::KeyStoreAuthentication ),
|
||||||
|
CONN_ATTR_STRING,
|
||||||
|
ce_akv_str_set_func::func
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PDOConnOptionNames::KeyStorePrincipalId,
|
||||||
|
sizeof( PDOConnOptionNames::KeyStorePrincipalId ),
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_PRINCIPAL_ID,
|
||||||
|
ODBCConnOptions::KeyStorePrincipalId,
|
||||||
|
sizeof( ODBCConnOptions::KeyStorePrincipalId ),
|
||||||
|
CONN_ATTR_STRING,
|
||||||
|
ce_akv_str_set_func::func
|
||||||
|
},
|
||||||
|
{
|
||||||
|
PDOConnOptionNames::KeyStoreSecret,
|
||||||
|
sizeof( PDOConnOptionNames::KeyStoreSecret ),
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_SECRET,
|
||||||
|
ODBCConnOptions::KeyStoreSecret,
|
||||||
|
sizeof( ODBCConnOptions::KeyStoreSecret ),
|
||||||
|
CONN_ATTR_STRING,
|
||||||
|
ce_akv_str_set_func::func
|
||||||
|
},
|
||||||
{
|
{
|
||||||
PDOConnOptionNames::LoginTimeout,
|
PDOConnOptionNames::LoginTimeout,
|
||||||
sizeof( PDOConnOptionNames::LoginTimeout ),
|
sizeof( PDOConnOptionNames::LoginTimeout ),
|
||||||
|
@ -362,7 +392,7 @@ const connection_option PDO_CONN_OPTS[] = {
|
||||||
{
|
{
|
||||||
PDOConnOptionNames::TransparentNetworkIPResolution,
|
PDOConnOptionNames::TransparentNetworkIPResolution,
|
||||||
sizeof(PDOConnOptionNames::TransparentNetworkIPResolution),
|
sizeof(PDOConnOptionNames::TransparentNetworkIPResolution),
|
||||||
SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION,
|
SQLSRV_CONN_OPTION_TRANSPARENT_NETWORK_IP_RESOLUTION,
|
||||||
ODBCConnOptions::TransparentNetworkIPResolution,
|
ODBCConnOptions::TransparentNetworkIPResolution,
|
||||||
sizeof(ODBCConnOptions::TransparentNetworkIPResolution),
|
sizeof(ODBCConnOptions::TransparentNetworkIPResolution),
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
|
|
|
@ -189,7 +189,7 @@ void conn_string_parser::add_key_value_pair( _In_reads_(len) const char* value,
|
||||||
if( !valid ) {
|
if( !valid ) {
|
||||||
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION, this->current_key_name );
|
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION, this->current_key_name );
|
||||||
}
|
}
|
||||||
|
|
||||||
string_parser::add_key_value_pair( value, len );
|
string_parser::add_key_value_pair( value, len );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -409,6 +409,26 @@ pdo_error PDO_ERRORS[] = {
|
||||||
SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED,
|
SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED,
|
||||||
{ IMSSP, (SQLCHAR*) "Error converting a double (value out of range) to an integer.", -84, false }
|
{ IMSSP, (SQLCHAR*) "Error converting a double (value out of range) to an integer.", -84, false }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_INVALID_AKV_AUTHENTICATION_OPTION,
|
||||||
|
{ IMSSP, (SQLCHAR*) "Invalid option for the KeyStoreAuthentication keyword. Only KeyVaultPassword or KeyVaultClientSecret is allowed.", -85, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_AKV_AUTH_MISSING,
|
||||||
|
{ IMSSP, (SQLCHAR*) "The authentication method for Azure Key Vault is missing. KeyStoreAuthentication must be set to KeyVaultPassword or KeyVaultClientSecret.", -86, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_AKV_NAME_MISSING,
|
||||||
|
{ IMSSP, (SQLCHAR*) "The username or client Id for Azure Key Vault is missing.", -87, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_AKV_SECRET_MISSING,
|
||||||
|
{ IMSSP, (SQLCHAR*) "The password or client secret for Azure Key Vault is missing.", -88, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
|
||||||
|
{ IMSSP, (SQLCHAR*) "Invalid value for loading Azure Key Vault.", -89, false}
|
||||||
|
},
|
||||||
{ UINT_MAX, {} }
|
{ UINT_MAX, {} }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,13 +7,13 @@
|
||||||
// Copyright(c) Microsoft Corporation
|
// Copyright(c) Microsoft Corporation
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
// MIT License
|
// MIT License
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files(the ""Software""),
|
||||||
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
// to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
|
// and / or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
|
||||||
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
// THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||||
// IN THE SOFTWARE.
|
// IN THE SOFTWARE.
|
||||||
//---------------------------------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
@ -45,6 +45,9 @@ const int DEFAULT_CONN_STR_LEN = 2048;
|
||||||
// length of buffer used to retrieve information for client and server info buffers
|
// length of buffer used to retrieve information for client and server info buffers
|
||||||
const int INFO_BUFFER_LEN = 256;
|
const int INFO_BUFFER_LEN = 256;
|
||||||
|
|
||||||
|
// length for name of keystore used in CEKeyStoreData
|
||||||
|
const int MAX_CE_NAME_LEN = 260;
|
||||||
|
|
||||||
// processor architectures
|
// processor architectures
|
||||||
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
|
const char* PROCESSOR_ARCH[] = { "x86", "x64", "ia64" };
|
||||||
|
|
||||||
|
@ -63,14 +66,17 @@ const char CONNECTION_OPTION_MARS_ON[] = "MARS_Connection={Yes};";
|
||||||
|
|
||||||
// *** internal function prototypes ***
|
// *** 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,
|
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[],
|
_Inout_opt_ HashTable* options_ht, _In_ const connection_option valid_conn_opts[],
|
||||||
void* driver,_Inout_ std::string& connection_string TSRMLS_DC );
|
void* driver,_Inout_ std::string& connection_string TSRMLS_DC );
|
||||||
void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
|
void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
|
||||||
const char* get_processor_arch( void );
|
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 );
|
void get_server_version( _Inout_ sqlsrv_conn* conn, _Outptr_result_buffer_(len) char** server_version, _Out_ SQLSMALLINT& len TSRMLS_DC );
|
||||||
connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ const char* key, _In_ SQLULEN key_len TSRMLS_DC );
|
connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ const char* key, _In_ SQLULEN key_len TSRMLS_DC );
|
||||||
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str TSRMLS_DC );
|
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str TSRMLS_DC );
|
||||||
|
void load_azure_key_vault( _Inout_ sqlsrv_conn* conn TSRMLS_DC );
|
||||||
|
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const DWORD config_value, size_t key_size);
|
||||||
|
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char* config_value, size_t key_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// core_sqlsrv_connect
|
// core_sqlsrv_connect
|
||||||
|
@ -79,7 +85,7 @@ void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_l
|
||||||
// henv_cp - connection pooled env context
|
// henv_cp - connection pooled env context
|
||||||
// henv_ncp - non connection pooled env context
|
// henv_ncp - non connection pooled env context
|
||||||
// server - name of the server we're connecting to
|
// server - name of the server we're connecting to
|
||||||
// uid - username
|
// uid - username
|
||||||
// pwd - password
|
// pwd - password
|
||||||
// options_ht - zend_hash list of options
|
// options_ht - zend_hash list of options
|
||||||
// err - error callback to put into the connection's context
|
// err - error callback to put into the connection's context
|
||||||
|
@ -89,8 +95,8 @@ void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_l
|
||||||
// A sqlsrv_conn structure. An exception is thrown if an error occurs
|
// A sqlsrv_conn structure. An exception is thrown if an error occurs
|
||||||
|
|
||||||
sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_context& henv_ncp, _In_ driver_conn_factory conn_factory,
|
sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_context& henv_ncp, _In_ driver_conn_factory conn_factory,
|
||||||
_Inout_z_ const char* server, _Inout_opt_z_ const char* uid, _Inout_opt_z_ const char* pwd,
|
_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[],
|
_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 )
|
_In_ void* driver, _In_z_ const char* driver_func TSRMLS_DC )
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -106,13 +112,13 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
#else
|
#else
|
||||||
sqlsrv_context* henv = &henv_ncp; // by default do not use the connection pooling henv
|
sqlsrv_context* henv = &henv_ncp; // by default do not use the connection pooling henv
|
||||||
is_pooled = false;
|
is_pooled = false;
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Due to the limitations on connection pooling in unixODBC 2.3.1 driver manager, we do not consider
|
// Due to the limitations on connection pooling in unixODBC 2.3.1 driver manager, we do not consider
|
||||||
// the connection string attributes to set (enable/disable) connection pooling.
|
// the connection string attributes to set (enable/disable) connection pooling.
|
||||||
// Instead, MSPHPSQL connection pooling is set according to the ODBCINST.INI file in [ODBC] section.
|
// Instead, MSPHPSQL connection pooling is set according to the ODBCINST.INI file in [ODBC] section.
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
char pooling_string[ 128 ] = {0};
|
char pooling_string[ 128 ] = {0};
|
||||||
SQLGetPrivateProfileString( "ODBC", "Pooling", "0", pooling_string, sizeof( pooling_string ), "ODBCINST.INI" );
|
SQLGetPrivateProfileString( "ODBC", "Pooling", "0", pooling_string, sizeof( pooling_string ), "ODBCINST.INI" );
|
||||||
|
@ -127,28 +133,28 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
// check the connection pooling setting to determine which henv to use to allocate the connection handle
|
// check the connection pooling setting to determine which henv to use to allocate the connection handle
|
||||||
// we do this earlier because we have to allocate the connection handle prior to setting attributes on
|
// we do this earlier because we have to allocate the connection handle prior to setting attributes on
|
||||||
// it in build_connection_string_and_set_conn_attr.
|
// it in build_connection_string_and_set_conn_attr.
|
||||||
|
|
||||||
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
|
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
|
||||||
|
|
||||||
zval* option_z = NULL;
|
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 ( option_z ) {
|
||||||
// if the option was found and it's not true, then use the non pooled environment handle
|
// 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 ) ) {
|
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;
|
is_pooled = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // !_WIN32
|
#endif // !_WIN32
|
||||||
|
|
||||||
SQLHANDLE temp_conn_h;
|
SQLHANDLE temp_conn_h;
|
||||||
core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h TSRMLS_CC );
|
core::SQLAllocHandle( SQL_HANDLE_DBC, *henv, &temp_conn_h TSRMLS_CC );
|
||||||
conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC );
|
conn = conn_factory( temp_conn_h, err, driver TSRMLS_CC );
|
||||||
conn->set_func( driver_func );
|
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 );
|
build_connection_string_and_set_conn_attr( conn, server, uid, pwd, options_ht, valid_conn_opts, driver, conn_str TSRMLS_CC );
|
||||||
|
|
||||||
// If column encryption is enabled, must use ODBC driver 17
|
// If column encryption is enabled, must use ODBC driver 17
|
||||||
if( conn->ce_option.enabled && conn->driver_version != ODBC_DRIVER_UNKNOWN) {
|
if( conn->ce_option.enabled && conn->driver_version != ODBC_DRIVER_UNKNOWN) {
|
||||||
CHECK_CUSTOM_ERROR( conn->driver_version != ODBC_DRIVER_17, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch() ) {
|
CHECK_CUSTOM_ERROR( conn->driver_version != ODBC_DRIVER_17, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch() ) {
|
||||||
|
@ -157,7 +163,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
}
|
}
|
||||||
|
|
||||||
// In non-Windows environment, unixODBC 2.3.4 and unixODBC 2.3.1 return different error states when an ODBC driver exists or not
|
// In non-Windows environment, unixODBC 2.3.4 and unixODBC 2.3.1 return different error states when an ODBC driver exists or not
|
||||||
// Therefore, it is unreliable to check for a certain sql state error
|
// Therefore, it is unreliable to check for a certain sql state error
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) {
|
if( conn->driver_version != ODBC_DRIVER_UNKNOWN ) {
|
||||||
// check if the ODBC driver actually exists, if not, throw an exception
|
// check if the ODBC driver actually exists, if not, throw an exception
|
||||||
|
@ -169,7 +175,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if( conn->ce_option.enabled ) {
|
if( conn->ce_option.enabled ) {
|
||||||
// driver not specified, so check if ODBC 17 exists
|
// driver not specified, so check if ODBC 17 exists
|
||||||
CHECK_CUSTOM_ERROR( ! core_search_odbc_driver_unix( ODBC_DRIVER_17 ), conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) {
|
CHECK_CUSTOM_ERROR( ! core_search_odbc_driver_unix( ODBC_DRIVER_17 ), conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) {
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
|
@ -184,7 +190,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
DRIVER_VERSION odbc_version = ODBC_DRIVER_UNKNOWN;
|
DRIVER_VERSION odbc_version = ODBC_DRIVER_UNKNOWN;
|
||||||
if( core_search_odbc_driver_unix( ODBC_DRIVER_17 ) ) {
|
if( core_search_odbc_driver_unix( ODBC_DRIVER_17 ) ) {
|
||||||
odbc_version = ODBC_DRIVER_17;
|
odbc_version = ODBC_DRIVER_17;
|
||||||
}
|
}
|
||||||
else if ( core_search_odbc_driver_unix( ODBC_DRIVER_13 ) ) {
|
else if ( core_search_odbc_driver_unix( ODBC_DRIVER_13 ) ) {
|
||||||
odbc_version = ODBC_DRIVER_13;
|
odbc_version = ODBC_DRIVER_13;
|
||||||
}
|
}
|
||||||
|
@ -222,7 +228,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
std::string conn_str_driver = conn_str + CONNECTION_STRING_DRIVER_NAME[i];
|
std::string conn_str_driver = conn_str + CONNECTION_STRING_DRIVER_NAME[i];
|
||||||
r = core_odbc_connect( conn, conn_str_driver, is_pooled );
|
r = core_odbc_connect( conn, conn_str_driver, is_pooled );
|
||||||
|
|
||||||
if( SQL_SUCCEEDED( r ) || ! core_compare_error_state( conn, r, "IM002" ) ) {
|
if( SQL_SUCCEEDED( r ) || ! core_compare_error_state( conn, r, "IM002" ) ) {
|
||||||
// something else went wrong, exit the loop now other than ODBC driver not found
|
// something else went wrong, exit the loop now other than ODBC driver not found
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
@ -232,10 +238,10 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // for
|
} // for
|
||||||
} // else ce_option enabled
|
} // else ce_option enabled
|
||||||
} // else driver_version not unknown
|
} // else driver_version not unknown
|
||||||
#endif // !_WIN32
|
#endif // !_WIN32
|
||||||
|
|
||||||
CHECK_SQL_ERROR( r, conn ) {
|
CHECK_SQL_ERROR( r, conn ) {
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
|
@ -245,7 +251,9 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
|
|
||||||
// determine the version of the server we're connected to. The server version is left in the
|
load_azure_key_vault( conn );
|
||||||
|
|
||||||
|
// determine the version of the server we're connected to. The server version is left in the
|
||||||
// connection upon return.
|
// connection upon return.
|
||||||
//
|
//
|
||||||
// unixODBC 2.3.1:
|
// unixODBC 2.3.1:
|
||||||
|
@ -254,11 +262,11 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
// Thus, in Linux, we don't call determine_server_version() for a connection that uses pool.
|
// Thus, in Linux, we don't call determine_server_version() for a connection that uses pool.
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if ( r == SQL_SUCCESS_WITH_INFO ) {
|
if ( r == SQL_SUCCESS_WITH_INFO ) {
|
||||||
#endif // !_WIN32
|
#endif // !_WIN32
|
||||||
determine_server_version( conn TSRMLS_CC );
|
determine_server_version( conn TSRMLS_CC );
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
}
|
}
|
||||||
#endif // !_WIN32
|
#endif // !_WIN32
|
||||||
}
|
}
|
||||||
catch( std::bad_alloc& ) {
|
catch( std::bad_alloc& ) {
|
||||||
conn_str.clear();
|
conn_str.clear();
|
||||||
|
@ -280,7 +288,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
catch( core::CoreException& ) {
|
catch( core::CoreException& ) {
|
||||||
conn_str.clear();
|
conn_str.clear();
|
||||||
conn->invalidate();
|
conn->invalidate();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
conn_str.clear();
|
conn_str.clear();
|
||||||
|
@ -293,13 +301,13 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
|
||||||
// core_compare_error_state
|
// core_compare_error_state
|
||||||
// This method compares the error state to the one specified
|
// This method compares the error state to the one specified
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// conn - the connection structure on which we establish the connection
|
// conn - the connection structure on which we establish the connection
|
||||||
// rc - ODBC return code
|
// rc - ODBC return code
|
||||||
// Return - a boolean flag that indicates if the error states are the same
|
// Return - a boolean flag that indicates if the error states are the same
|
||||||
|
|
||||||
bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN rc, _In_ const char* error_state )
|
bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN rc, _In_ const char* error_state )
|
||||||
{
|
{
|
||||||
if( SQL_SUCCEEDED( rc ) )
|
if( SQL_SUCCEEDED( rc ) )
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ] = { 0 };
|
SQLCHAR state[ SQL_SQLSTATE_BUFSIZE ] = { 0 };
|
||||||
|
@ -310,7 +318,7 @@ bool core_compare_error_state( _In_ sqlsrv_conn* conn, _In_ SQLRETURN rc, _In_
|
||||||
}
|
}
|
||||||
|
|
||||||
// core_search_odbc_driver_unix
|
// core_search_odbc_driver_unix
|
||||||
// This method is meant to be used in a non-Windows environment,
|
// This method is meant to be used in a non-Windows environment,
|
||||||
// searching for a particular ODBC driver name in the odbcinst.ini file
|
// searching for a particular ODBC driver name in the odbcinst.ini file
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// driver_version - a valid value in enum DRIVER_VERSION
|
// driver_version - a valid value in enum DRIVER_VERSION
|
||||||
|
@ -320,7 +328,7 @@ bool core_search_odbc_driver_unix( _In_ DRIVER_VERSION driver_version )
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
char szBuf[DEFAULT_CONN_STR_LEN+1]; // use a large enough buffer size
|
char szBuf[DEFAULT_CONN_STR_LEN+1]; // use a large enough buffer size
|
||||||
WORD cbBufMax = DEFAULT_CONN_STR_LEN;
|
WORD cbBufMax = DEFAULT_CONN_STR_LEN;
|
||||||
WORD cbBufOut;
|
WORD cbBufOut;
|
||||||
char *pszBuf = szBuf;
|
char *pszBuf = szBuf;
|
||||||
|
|
||||||
|
@ -330,7 +338,7 @@ bool core_search_odbc_driver_unix( _In_ DRIVER_VERSION driver_version )
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// extract the ODBC driver name
|
// extract the ODBC driver name
|
||||||
std::string driver = CONNECTION_STRING_DRIVER_NAME[driver_version];
|
std::string driver = CONNECTION_STRING_DRIVER_NAME[driver_version];
|
||||||
std::size_t pos1 = driver.find_first_of("{");
|
std::size_t pos1 = driver.find_first_of("{");
|
||||||
std::size_t pos2 = driver.find_first_of("}");
|
std::size_t pos2 = driver.find_first_of("}");
|
||||||
|
@ -348,7 +356,7 @@ bool core_search_odbc_driver_unix( _In_ DRIVER_VERSION driver_version )
|
||||||
pszBuf = strchr( pszBuf, '\0' ) + 1;
|
pszBuf = strchr( pszBuf, '\0' ) + 1;
|
||||||
}
|
}
|
||||||
while( pszBuf[1] != '\0' ); // end when there are two consecutive null characters
|
while( pszBuf[1] != '\0' ); // end when there are two consecutive null characters
|
||||||
#endif // !_WIN32
|
#endif // !_WIN32
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -356,9 +364,9 @@ bool core_search_odbc_driver_unix( _In_ DRIVER_VERSION driver_version )
|
||||||
// core_odbc_connect
|
// core_odbc_connect
|
||||||
// calls odbc connect API to establish the connection to server
|
// calls odbc connect API to establish the connection to server
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// conn - The connection structure on which we establish the connection
|
// conn - The connection structure on which we establish the connection
|
||||||
// conn_str - Connection string
|
// conn_str - Connection string
|
||||||
// is_pooled - indicate whether it is a pooled connection
|
// is_pooled - indicate whether it is a pooled connection
|
||||||
// Return - SQLRETURN status returned by SQLDriverConnect
|
// Return - SQLRETURN status returned by SQLDriverConnect
|
||||||
|
|
||||||
SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str, _In_ bool is_pooled )
|
SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str, _In_ bool is_pooled )
|
||||||
|
@ -388,9 +396,9 @@ SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& con
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
r = SQLDriverConnectW( conn->handle(), NULL, wconn_string, static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
r = SQLDriverConnectW( conn->handle(), NULL, wconn_string, static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
|
||||||
#endif // !_WIN32
|
#endif // !_WIN32
|
||||||
|
|
||||||
// clear the connection string from memory
|
// clear the connection string from memory
|
||||||
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
memset( wconn_string, 0, wconn_len * sizeof( SQLWCHAR )); // wconn_len is the number of characters, not bytes
|
||||||
conn_str.clear();
|
conn_str.clear();
|
||||||
|
|
||||||
|
@ -401,12 +409,12 @@ SQLRETURN core_odbc_connect( _Inout_ sqlsrv_conn* conn, _Inout_ std::string& con
|
||||||
// core_sqlsrv_begin_transaction
|
// core_sqlsrv_begin_transaction
|
||||||
// Begins a transaction on a specified connection. The current transaction
|
// Begins a transaction on a specified connection. The current transaction
|
||||||
// includes all statements on the specified connection that were executed after
|
// includes all statements on the specified connection that were executed after
|
||||||
// the call to core_sqlsrv_begin_transaction and before any calls to
|
// the call to core_sqlsrv_begin_transaction and before any calls to
|
||||||
// core_sqlsrv_rollback or core_sqlsrv_commit.
|
// core_sqlsrv_rollback or core_sqlsrv_commit.
|
||||||
// The default transaction mode is auto-commit. This means that all queries
|
// The default transaction mode is auto-commit. This means that all queries
|
||||||
// are automatically committed upon success unless they have been designated
|
// are automatically committed upon success unless they have been designated
|
||||||
// as part of an explicit transaction by using core_sqlsrv_begin_transaction.
|
// as part of an explicit transaction by using core_sqlsrv_begin_transaction.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// sqlsrv_conn*: The connection with which the transaction is associated.
|
// sqlsrv_conn*: The connection with which the transaction is associated.
|
||||||
|
|
||||||
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
|
@ -414,8 +422,8 @@ void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
try {
|
try {
|
||||||
|
|
||||||
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_begin_transaction: connection object was null." );
|
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_begin_transaction: connection object was null." );
|
||||||
|
|
||||||
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_OFF ),
|
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_OFF ),
|
||||||
SQL_IS_UINTEGER TSRMLS_CC );
|
SQL_IS_UINTEGER TSRMLS_CC );
|
||||||
}
|
}
|
||||||
catch ( core::CoreException& ) {
|
catch ( core::CoreException& ) {
|
||||||
|
@ -428,19 +436,19 @@ void core_sqlsrv_begin_transaction( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
// connection to the auto-commit mode. The current transaction includes all
|
// connection to the auto-commit mode. The current transaction includes all
|
||||||
// statements on the specified connection that were executed after the call to
|
// statements on the specified connection that were executed after the call to
|
||||||
// core_sqlsrv_begin_transaction and before any calls to core_sqlsrv_rollback or
|
// core_sqlsrv_begin_transaction and before any calls to core_sqlsrv_rollback or
|
||||||
// core_sqlsrv_commit.
|
// core_sqlsrv_commit.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// sqlsrv_conn*: The connection on which the transaction is active.
|
// sqlsrv_conn*: The connection on which the transaction is active.
|
||||||
|
|
||||||
void core_sqlsrv_commit( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
void core_sqlsrv_commit( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_commit: connection object was null." );
|
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_commit: connection object was null." );
|
||||||
|
|
||||||
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_COMMIT TSRMLS_CC );
|
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_COMMIT TSRMLS_CC );
|
||||||
|
|
||||||
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_ON ),
|
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_ON ),
|
||||||
SQL_IS_UINTEGER TSRMLS_CC );
|
SQL_IS_UINTEGER TSRMLS_CC );
|
||||||
}
|
}
|
||||||
catch ( core::CoreException& ) {
|
catch ( core::CoreException& ) {
|
||||||
|
@ -462,12 +470,12 @@ void core_sqlsrv_rollback( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
try {
|
try {
|
||||||
|
|
||||||
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_rollback: connection object was null." );
|
DEBUG_SQLSRV_ASSERT( conn != NULL, "core_sqlsrv_rollback: connection object was null." );
|
||||||
|
|
||||||
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC );
|
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC );
|
||||||
|
|
||||||
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_ON ),
|
core::SQLSetConnectAttr( conn, SQL_ATTR_AUTOCOMMIT, reinterpret_cast<SQLPOINTER>( SQL_AUTOCOMMIT_ON ),
|
||||||
SQL_IS_UINTEGER TSRMLS_CC );
|
SQL_IS_UINTEGER TSRMLS_CC );
|
||||||
|
|
||||||
}
|
}
|
||||||
catch ( core::CoreException& ) {
|
catch ( core::CoreException& ) {
|
||||||
throw;
|
throw;
|
||||||
|
@ -475,7 +483,7 @@ void core_sqlsrv_rollback( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
}
|
}
|
||||||
|
|
||||||
// core_sqlsrv_close
|
// core_sqlsrv_close
|
||||||
// Called when a connection resource is destroyed by the Zend engine.
|
// Called when a connection resource is destroyed by the Zend engine.
|
||||||
// Parameters:
|
// Parameters:
|
||||||
// conn - The current active connection.
|
// conn - The current active connection.
|
||||||
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC )
|
void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
|
@ -485,7 +493,7 @@ void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
return;
|
return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// rollback any transaction in progress (we don't care about the return result)
|
// rollback any transaction in progress (we don't care about the return result)
|
||||||
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC );
|
core::SQLEndTran( SQL_HANDLE_DBC, conn, SQL_ROLLBACK TSRMLS_CC );
|
||||||
}
|
}
|
||||||
|
@ -495,7 +503,7 @@ void core_sqlsrv_close( _Inout_opt_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
|
|
||||||
// disconnect from the server
|
// disconnect from the server
|
||||||
SQLRETURN r = SQLDisconnect( conn->handle() );
|
SQLRETURN r = SQLDisconnect( conn->handle() );
|
||||||
if( !SQL_SUCCEEDED( r )) {
|
if( !SQL_SUCCEEDED( r )) {
|
||||||
LOG( SEV_ERROR, "Disconnect failed when closing the connection." );
|
LOG( SEV_ERROR, "Disconnect failed when closing the connection." );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,15 +525,15 @@ void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) c
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// convert the string from its encoding to UTf-16
|
// convert the string from its encoding to UTf-16
|
||||||
// if the string is empty, we initialize the fields and skip since an empty string is a
|
// if the string is empty, we initialize the fields and skip since an empty string is a
|
||||||
// failure case for utf16_string_from_mbcs_string
|
// failure case for utf16_string_from_mbcs_string
|
||||||
sqlsrv_malloc_auto_ptr<SQLWCHAR> wsql_string;
|
sqlsrv_malloc_auto_ptr<SQLWCHAR> wsql_string;
|
||||||
unsigned int wsql_len = 0;
|
unsigned int wsql_len = 0;
|
||||||
if( sql_len == 0 || ( sql[0] == '\0' && sql_len == 1 )) {
|
if( sql_len == 0 || ( sql[0] == '\0' && sql_len == 1 )) {
|
||||||
wsql_string = reinterpret_cast<SQLWCHAR*>( sqlsrv_malloc( sizeof( SQLWCHAR )));
|
wsql_string = reinterpret_cast<SQLWCHAR*>( sqlsrv_malloc( sizeof( SQLWCHAR )));
|
||||||
wsql_string[0] = L'\0';
|
wsql_string[0] = L'\0';
|
||||||
wsql_len = 0;
|
wsql_len = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if( sql_len > INT_MAX ) {
|
if( sql_len > INT_MAX ) {
|
||||||
LOG( SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded.");
|
LOG( SEV_ERROR, "Convert input parameter to utf16: buffer length exceeded.");
|
||||||
|
@ -561,7 +569,7 @@ void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) c
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// core_sqlsrv_get_server_version
|
// core_sqlsrv_get_server_version
|
||||||
// Determines the vesrion of the SQL Server we are connected to. Calls a helper function
|
// Determines the vesrion of the SQL Server we are connected to. Calls a helper function
|
||||||
|
@ -573,7 +581,7 @@ void core_sqlsrv_prepare( _Inout_ sqlsrv_stmt* stmt, _In_reads_bytes_(sql_len) c
|
||||||
void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval* server_version TSRMLS_DC )
|
void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval* server_version TSRMLS_DC )
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
sqlsrv_malloc_auto_ptr<char> buffer;
|
sqlsrv_malloc_auto_ptr<char> buffer;
|
||||||
SQLSMALLINT buffer_len = 0;
|
SQLSMALLINT buffer_len = 0;
|
||||||
|
|
||||||
|
@ -584,7 +592,7 @@ void core_sqlsrv_get_server_version( _Inout_ sqlsrv_conn* conn, _Inout_ zval* se
|
||||||
}
|
}
|
||||||
buffer.transferred();
|
buffer.transferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
catch( core::CoreException& ) {
|
catch( core::CoreException& ) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -613,19 +621,19 @@ void core_sqlsrv_get_server_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *server_
|
||||||
|
|
||||||
core::sqlsrv_add_assoc_string( *conn, server_info, "CurrentDatabase", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
core::sqlsrv_add_assoc_string( *conn, server_info, "CurrentDatabase", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
||||||
buffer.transferred();
|
buffer.transferred();
|
||||||
|
|
||||||
// Get the server version
|
// Get the server version
|
||||||
get_server_version( conn, &buffer, buffer_len TSRMLS_CC );
|
get_server_version( conn, &buffer, buffer_len TSRMLS_CC );
|
||||||
core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerVersion", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerVersion", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
||||||
buffer.transferred();
|
buffer.transferred();
|
||||||
|
|
||||||
// Get the server name
|
// Get the server name
|
||||||
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
|
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
|
||||||
core::SQLGetInfo( conn, SQL_SERVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
|
core::SQLGetInfo( conn, SQL_SERVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
|
||||||
core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerName", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
core::sqlsrv_add_assoc_string( *conn, server_info, "SQLServerName", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
||||||
buffer.transferred();
|
buffer.transferred();
|
||||||
}
|
}
|
||||||
|
|
||||||
catch( core::CoreException& ) {
|
catch( core::CoreException& ) {
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -643,7 +651,7 @@ void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_
|
||||||
|
|
||||||
sqlsrv_malloc_auto_ptr<char> buffer;
|
sqlsrv_malloc_auto_ptr<char> buffer;
|
||||||
SQLSMALLINT buffer_len = 0;
|
SQLSMALLINT buffer_len = 0;
|
||||||
|
|
||||||
// Get the ODBC driver's dll name
|
// Get the ODBC driver's dll name
|
||||||
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
|
buffer = static_cast<char*>( sqlsrv_malloc( INFO_BUFFER_LEN ));
|
||||||
core::SQLGetInfo( conn, SQL_DRIVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
|
core::SQLGetInfo( conn, SQL_DRIVER_NAME, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
|
||||||
|
@ -669,7 +677,7 @@ void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_
|
||||||
core::SQLGetInfo( conn, SQL_DRIVER_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
|
core::SQLGetInfo( conn, SQL_DRIVER_VER, buffer, INFO_BUFFER_LEN, &buffer_len TSRMLS_CC );
|
||||||
core::sqlsrv_add_assoc_string( *conn, client_info, "DriverVer", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
core::sqlsrv_add_assoc_string( *conn, client_info, "DriverVer", buffer, 0 /*duplicate*/ TSRMLS_CC );
|
||||||
buffer.transferred();
|
buffer.transferred();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
catch( core::CoreException& ) {
|
catch( core::CoreException& ) {
|
||||||
|
@ -680,12 +688,12 @@ void core_sqlsrv_get_client_info( _Inout_ sqlsrv_conn* conn, _Out_ zval *client_
|
||||||
|
|
||||||
// core_is_conn_opt_value_escaped
|
// core_is_conn_opt_value_escaped
|
||||||
// determine if connection string value is properly escaped.
|
// determine if connection string value is properly escaped.
|
||||||
// Properly escaped means that any '}' should be escaped by a prior '}'. It is assumed that
|
// Properly escaped means that any '}' should be escaped by a prior '}'. It is assumed that
|
||||||
// the value will be surrounded by { and } by the caller after it has been validated
|
// the value will be surrounded by { and } by the caller after it has been validated
|
||||||
|
|
||||||
bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t value_len )
|
bool core_is_conn_opt_value_escaped( _Inout_ const char* value, _Inout_ size_t value_len )
|
||||||
{
|
{
|
||||||
// if the value is already quoted, then only analyse the part inside the quotes and return it as
|
// if the value is already quoted, then only analyse the part inside the quotes and return it as
|
||||||
// unquoted since we quote it when adding it to the connection string.
|
// unquoted since we quote it when adding it to the connection string.
|
||||||
if( value_len > 0 && value[0] == '{' && value[ value_len - 1 ] == '}' ) {
|
if( value_len > 0 && value[0] == '{' && value[ value_len - 1 ] == '}' ) {
|
||||||
++value;
|
++value;
|
||||||
|
@ -725,12 +733,12 @@ bool core_is_authentication_option_valid( _In_z_ const char* value, _In_ size_t
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ SQLULEN key,
|
connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ SQLULEN key,
|
||||||
_In_ const connection_option conn_opts[] TSRMLS_DC )
|
_In_ const connection_option conn_opts[] TSRMLS_DC )
|
||||||
{
|
{
|
||||||
for( int opt_idx = 0; conn_opts[ opt_idx ].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++opt_idx ) {
|
for( int opt_idx = 0; conn_opts[ opt_idx ].conn_option_key != SQLSRV_CONN_OPTION_INVALID; ++opt_idx ) {
|
||||||
|
|
||||||
if( key == conn_opts[ opt_idx ].conn_option_key ) {
|
if( key == conn_opts[ opt_idx ].conn_option_key ) {
|
||||||
|
|
||||||
return &conn_opts[ opt_idx ];
|
return &conn_opts[ opt_idx ];
|
||||||
}
|
}
|
||||||
|
@ -745,15 +753,15 @@ connection_option const* get_connection_option( sqlsrv_conn* conn, _In_ SQLULEN
|
||||||
// passed to the connection, and then break them out ourselves and either set attributes or put the
|
// passed to the connection, and then break them out ourselves and either set attributes or put the
|
||||||
// option in the connection string.
|
// option in the connection string.
|
||||||
|
|
||||||
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,
|
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, _In_ const connection_option valid_conn_opts[],
|
_Inout_opt_ HashTable* options, _In_ const connection_option valid_conn_opts[],
|
||||||
void* driver, _Inout_ std::string& connection_string TSRMLS_DC )
|
void* driver, _Inout_ std::string& connection_string TSRMLS_DC )
|
||||||
{
|
{
|
||||||
bool mars_mentioned = false;
|
bool mars_mentioned = false;
|
||||||
connection_option const* conn_opt;
|
connection_option const* conn_opt;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// Add the server name
|
// Add the server name
|
||||||
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string TSRMLS_CC );
|
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string TSRMLS_CC );
|
||||||
|
|
||||||
|
@ -778,7 +786,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
||||||
CHECK_CUSTOM_ERROR( !escaped, conn, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED ) {
|
CHECK_CUSTOM_ERROR( !escaped, conn, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED ) {
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
|
|
||||||
common_conn_str_append_func( ODBCConnOptions::PWD, pwd, strnlen_s( pwd ), connection_string TSRMLS_CC );
|
common_conn_str_append_func( ODBCConnOptions::PWD, pwd, strnlen_s( pwd ), connection_string TSRMLS_CC );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -789,16 +797,16 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// workaround for a bug in ODBC Driver Manager wherein the Driver Manager creates a 0 KB file
|
// workaround for a bug in ODBC Driver Manager wherein the Driver Manager creates a 0 KB file
|
||||||
// if the TraceFile option is set, even if the "TraceOn" is not present or the "TraceOn"
|
// if the TraceFile option is set, even if the "TraceOn" is not present or the "TraceOn"
|
||||||
// flag is set to false.
|
// flag is set to false.
|
||||||
if( zend_hash_index_exists( options, SQLSRV_CONN_OPTION_TRACE_FILE )) {
|
if( zend_hash_index_exists( options, SQLSRV_CONN_OPTION_TRACE_FILE )) {
|
||||||
|
|
||||||
zval* trace_value = NULL;
|
zval* trace_value = NULL;
|
||||||
trace_value = zend_hash_index_find(options, SQLSRV_CONN_OPTION_TRACE_ON);
|
trace_value = zend_hash_index_find(options, SQLSRV_CONN_OPTION_TRACE_ON);
|
||||||
|
|
||||||
if (trace_value == NULL || !zend_is_true(trace_value)) {
|
if (trace_value == NULL || !zend_is_true(trace_value)) {
|
||||||
|
|
||||||
zend_hash_index_del( options, SQLSRV_CONN_OPTION_TRACE_FILE );
|
zend_hash_index_del( options, SQLSRV_CONN_OPTION_TRACE_FILE );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -841,7 +849,7 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
|
||||||
void get_server_version( _Inout_ sqlsrv_conn* conn, _Outptr_result_buffer_(len) char** server_version, _Out_ SQLSMALLINT& len TSRMLS_DC )
|
void get_server_version( _Inout_ sqlsrv_conn* conn, _Outptr_result_buffer_(len) char** server_version, _Out_ SQLSMALLINT& len TSRMLS_DC )
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
|
|
||||||
sqlsrv_malloc_auto_ptr<char> buffer;
|
sqlsrv_malloc_auto_ptr<char> buffer;
|
||||||
SQLSMALLINT buffer_len = 0;
|
SQLSMALLINT buffer_len = 0;
|
||||||
|
|
||||||
|
@ -859,7 +867,7 @@ void get_server_version( _Inout_ sqlsrv_conn* conn, _Outptr_result_buffer_(len)
|
||||||
|
|
||||||
|
|
||||||
// get_processor_arch
|
// get_processor_arch
|
||||||
// Calls GetSystemInfo to verify the what architecture of the processor is supported
|
// Calls GetSystemInfo to verify the what architecture of the processor is supported
|
||||||
// and return the string of the processor name.
|
// and return the string of the processor name.
|
||||||
const char* get_processor_arch( void )
|
const char* get_processor_arch( void )
|
||||||
{
|
{
|
||||||
|
@ -932,6 +940,85 @@ void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
|
||||||
conn->server_version = version_major;
|
conn->server_version = version_major;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load_azure_key_vault(_Inout_ sqlsrv_conn* conn TSRMLS_DC)
|
||||||
|
{
|
||||||
|
// If column encryption is not enabled simply do nothing. Otherwise, check if Azure Key Vault
|
||||||
|
// is required for encryption or decryption. Note, in order to load and configure Azure Key Vault,
|
||||||
|
// all fields in conn->ce_option must be defined.
|
||||||
|
if (!conn->ce_option.enabled || !conn->ce_option.akv_required)
|
||||||
|
return;
|
||||||
|
|
||||||
|
CHECK_CUSTOM_ERROR(conn->ce_option.akv_mode == -1, conn, SQLSRV_ERROR_AKV_AUTH_MISSING) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_CUSTOM_ERROR(conn->ce_option.akv_id == NULL, conn, SQLSRV_ERROR_AKV_NAME_MISSING) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK_CUSTOM_ERROR(conn->ce_option.akv_secret == NULL, conn, SQLSRV_ERROR_AKV_SECRET_MISSING) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
char *akv_id = Z_STRVAL_P(conn->ce_option.akv_id);
|
||||||
|
char *akv_secret = Z_STRVAL_P(conn->ce_option.akv_secret);
|
||||||
|
unsigned int id_len = static_cast<unsigned int>(Z_STRLEN_P(conn->ce_option.akv_id));
|
||||||
|
unsigned int key_size = static_cast<unsigned int>(Z_STRLEN_P(conn->ce_option.akv_secret));
|
||||||
|
|
||||||
|
configure_azure_key_vault(conn, AKV_CONFIG_FLAGS, conn->ce_option.akv_mode, 0);
|
||||||
|
configure_azure_key_vault(conn, AKV_CONFIG_PRINCIPALID, akv_id, id_len);
|
||||||
|
configure_azure_key_vault(conn, AKV_CONFIG_AUTHSECRET, akv_secret, key_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure_azure_key_vault(sqlsrv_conn* conn, BYTE config_attr, const DWORD config_value, size_t key_size)
|
||||||
|
{
|
||||||
|
BYTE akv_data[sizeof(CEKEYSTOREDATA) + sizeof(DWORD) + 1];
|
||||||
|
CEKEYSTOREDATA *pData = reinterpret_cast<CEKEYSTOREDATA*>(akv_data);
|
||||||
|
|
||||||
|
char akv_name[] = "AZURE_KEY_VAULT";
|
||||||
|
unsigned int name_len = 15;
|
||||||
|
unsigned int wname_len = 0;
|
||||||
|
sqlsrv_malloc_auto_ptr<SQLWCHAR> wakv_name;
|
||||||
|
wakv_name = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, akv_name, name_len, &wname_len);
|
||||||
|
|
||||||
|
CHECK_CUSTOM_ERROR(wakv_name == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
pData->name = (wchar_t *)wakv_name.get();
|
||||||
|
|
||||||
|
pData->data[0] = config_attr;
|
||||||
|
pData->dataSize = sizeof(config_attr) + sizeof(config_value);
|
||||||
|
*reinterpret_cast<DWORD*>(&pData->data[1]) = config_value;
|
||||||
|
|
||||||
|
core::SQLSetConnectAttr(conn, SQL_COPT_SS_CEKEYSTOREDATA, reinterpret_cast<SQLPOINTER>(pData), SQL_IS_POINTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
void configure_azure_key_vault(sqlsrv_conn* conn, BYTE config_attr, const char* config_value, size_t key_size)
|
||||||
|
{
|
||||||
|
BYTE akv_data[sizeof(CEKEYSTOREDATA) + MAX_CE_NAME_LEN];
|
||||||
|
CEKEYSTOREDATA *pData = reinterpret_cast<CEKEYSTOREDATA*>(akv_data);
|
||||||
|
|
||||||
|
char akv_name[] = "AZURE_KEY_VAULT";
|
||||||
|
unsigned int name_len = 15;
|
||||||
|
unsigned int wname_len = 0;
|
||||||
|
sqlsrv_malloc_auto_ptr<SQLWCHAR> wakv_name;
|
||||||
|
wakv_name = utf16_string_from_mbcs_string(SQLSRV_ENCODING_UTF8, akv_name, name_len, &wname_len);
|
||||||
|
|
||||||
|
CHECK_CUSTOM_ERROR(wakv_name == 0, conn, SQLSRV_ERROR_CONNECT_STRING_ENCODING_TRANSLATE) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
|
||||||
|
pData->name = (wchar_t *)wakv_name.get();
|
||||||
|
|
||||||
|
pData->data[0] = config_attr;
|
||||||
|
pData->dataSize = 1 + key_size;
|
||||||
|
|
||||||
|
memcpy_s(pData->data + 1, key_size * sizeof(char), config_value, key_size);
|
||||||
|
|
||||||
|
core::SQLSetConnectAttr(conn, SQL_COPT_SS_CEKEYSTOREDATA, reinterpret_cast<SQLPOINTER>(pData), SQL_IS_POINTER);
|
||||||
|
}
|
||||||
|
|
||||||
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str TSRMLS_DC )
|
void common_conn_str_append_func( _In_z_ const char* odbc_name, _In_reads_(val_len) const char* val, _Inout_ size_t val_len, _Inout_ std::string& conn_str TSRMLS_DC )
|
||||||
{
|
{
|
||||||
// wrap a connection option in a quote. It is presumed that any character that need to be escaped will
|
// wrap a connection option in a quote. It is presumed that any character that need to be escaped will
|
||||||
|
@ -961,7 +1048,7 @@ void conn_str_append_func::func( _In_ connection_option const* option, _In_ zval
|
||||||
// do nothing for connection pooling since we handled it earlier when
|
// do nothing for connection pooling since we handled it earlier when
|
||||||
// deciding which environment handle to use.
|
// 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;
|
TSRMLS_C;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,19 +1058,19 @@ void driver_set_func::func( _In_ connection_option const* option, _In_ zval* val
|
||||||
size_t val_len = Z_STRLEN_P( value );
|
size_t val_len = Z_STRLEN_P( value );
|
||||||
std::string driver_option( "" );
|
std::string driver_option( "" );
|
||||||
common_conn_str_append_func( option->odbc_name, val_str, val_len, driver_option TSRMLS_CC );
|
common_conn_str_append_func( option->odbc_name, val_str, val_len, driver_option TSRMLS_CC );
|
||||||
|
|
||||||
conn->driver_version = ODBC_DRIVER_UNKNOWN;
|
conn->driver_version = ODBC_DRIVER_UNKNOWN;
|
||||||
for ( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST && conn->driver_version == ODBC_DRIVER_UNKNOWN; ++i ) {
|
for ( short i = DRIVER_VERSION::FIRST; i <= DRIVER_VERSION::LAST && conn->driver_version == ODBC_DRIVER_UNKNOWN; ++i ) {
|
||||||
std::string driver_name = CONNECTION_STRING_DRIVER_NAME[i];
|
std::string driver_name = CONNECTION_STRING_DRIVER_NAME[i];
|
||||||
|
|
||||||
if (! driver_name.compare( driver_option ) ) {
|
if (! driver_name.compare( driver_option ) ) {
|
||||||
conn->driver_version = DRIVER_VERSION( i );
|
conn->driver_version = DRIVER_VERSION( i );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CHECK_CUSTOM_ERROR( conn->driver_version == ODBC_DRIVER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, val_str) {
|
CHECK_CUSTOM_ERROR( conn->driver_version == ODBC_DRIVER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, val_str) {
|
||||||
throw core::CoreException();
|
throw core::CoreException();
|
||||||
}
|
}
|
||||||
|
|
||||||
conn_str += driver_option;
|
conn_str += driver_option;
|
||||||
}
|
}
|
||||||
|
@ -1005,17 +1092,63 @@ void column_encryption_set_func::func( _In_ connection_option const* option, _In
|
||||||
conn_str += ";";
|
conn_str += ";";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ce_akv_str_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, "Azure Key Vault keywords accept only strings.");
|
||||||
|
|
||||||
|
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_KEYSTORE_AUTHENTICATION:
|
||||||
|
{
|
||||||
|
char *value_str = Z_STRVAL_P(value);
|
||||||
|
if (!stricmp(value_str, "KeyVaultPassword")) {
|
||||||
|
conn->ce_option.akv_mode = AKVCFG_AUTHMODE_PASSWORD;
|
||||||
|
} else if (!stricmp(value_str, "KeyVaultClientSecret")) {
|
||||||
|
conn->ce_option.akv_mode = AKVCFG_AUTHMODE_CLIENTKEY;
|
||||||
|
} else {
|
||||||
|
CHECK_CUSTOM_ERROR(1, conn, SQLSRV_ERROR_INVALID_AKV_AUTHENTICATION_OPTION) {
|
||||||
|
throw core::CoreException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->ce_option.akv_required = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SQLSRV_CONN_OPTION_KEYSTORE_PRINCIPAL_ID:
|
||||||
|
{
|
||||||
|
conn->ce_option.akv_id = value;
|
||||||
|
conn->ce_option.akv_required = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SQLSRV_CONN_OPTION_KEYSTORE_SECRET:
|
||||||
|
{
|
||||||
|
conn->ce_option.akv_secret = value;
|
||||||
|
conn->ce_option.akv_required = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
SQLSRV_ASSERT(false, "ce_akv_str_set_func: Invalid AKV option!");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// helper function to evaluate whether a string value is true or false.
|
// 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.
|
// Values = ("true" or "1") are treated as true values. Everything else is treated as false.
|
||||||
// Returns 1 for true and 0 for false.
|
// Returns 1 for true and 0 for false.
|
||||||
|
|
||||||
size_t core_str_zval_is_true( _Inout_ zval* value_z )
|
size_t core_str_zval_is_true( _Inout_ zval* value_z )
|
||||||
{
|
{
|
||||||
SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "core_str_zval_is_true: This function only accepts zval of type string." );
|
SQLSRV_ASSERT( Z_TYPE_P( value_z ) == IS_STRING, "core_str_zval_is_true: This function only accepts zval of type string." );
|
||||||
|
|
||||||
char* value_in = Z_STRVAL_P( value_z );
|
char* value_in = Z_STRVAL_P( value_z );
|
||||||
size_t val_len = Z_STRLEN_P( value_z );
|
size_t val_len = Z_STRLEN_P( value_z );
|
||||||
|
|
||||||
// strip any whitespace at the end (whitespace is the same value in ASCII and UTF-8)
|
// strip any whitespace at the end (whitespace is the same value in ASCII and UTF-8)
|
||||||
size_t last_char = val_len - 1;
|
size_t last_char = val_len - 1;
|
||||||
while( isspace(( unsigned char )value_in[ last_char ] )) {
|
while( isspace(( unsigned char )value_in[ last_char ] )) {
|
||||||
|
@ -1029,9 +1162,9 @@ size_t core_str_zval_is_true( _Inout_ zval* value_z )
|
||||||
|
|
||||||
const char VALID_TRUE_VALUE_1[] = "true";
|
const char VALID_TRUE_VALUE_1[] = "true";
|
||||||
const char VALID_TRUE_VALUE_2[] = "1";
|
const char VALID_TRUE_VALUE_2[] = "1";
|
||||||
|
|
||||||
if(( val_len == ( sizeof( VALID_TRUE_VALUE_1 ) - 1 ) && !strnicmp( value_in, VALID_TRUE_VALUE_1, val_len )) ||
|
if(( val_len == ( sizeof( VALID_TRUE_VALUE_1 ) - 1 ) && !strnicmp( value_in, VALID_TRUE_VALUE_1, val_len )) ||
|
||||||
( val_len == ( sizeof( VALID_TRUE_VALUE_2 ) - 1 ) && !strnicmp( value_in, VALID_TRUE_VALUE_2, val_len ))
|
( val_len == ( sizeof( VALID_TRUE_VALUE_2 ) - 1 ) && !strnicmp( value_in, VALID_TRUE_VALUE_2, val_len ))
|
||||||
) {
|
) {
|
||||||
|
|
||||||
return 1; // true
|
return 1; // true
|
||||||
|
|
|
@ -1055,8 +1055,12 @@ struct stmt_option;
|
||||||
// This holds the various details of column encryption.
|
// This holds the various details of column encryption.
|
||||||
struct col_encryption_option {
|
struct col_encryption_option {
|
||||||
bool enabled; // column encryption enabled, false by default
|
bool enabled; // column encryption enabled, false by default
|
||||||
|
SQLINTEGER akv_mode;
|
||||||
|
zval_auto_ptr akv_id;
|
||||||
|
zval_auto_ptr akv_secret;
|
||||||
|
bool akv_required;
|
||||||
|
|
||||||
col_encryption_option() : enabled( false )
|
col_encryption_option() : enabled( false ), akv_mode(-1), akv_required( false )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1106,14 +1110,17 @@ const char Authentication[] = "Authentication";
|
||||||
const char Driver[] = "Driver";
|
const char Driver[] = "Driver";
|
||||||
const char CharacterSet[] = "CharacterSet";
|
const char CharacterSet[] = "CharacterSet";
|
||||||
const char ConnectionPooling[] = "ConnectionPooling";
|
const char ConnectionPooling[] = "ConnectionPooling";
|
||||||
#ifdef _WIN32
|
|
||||||
const char ColumnEncryption[] = "ColumnEncryption";
|
const char ColumnEncryption[] = "ColumnEncryption";
|
||||||
|
#ifdef _WIN32
|
||||||
const char ConnectRetryCount[] = "ConnectRetryCount";
|
const char ConnectRetryCount[] = "ConnectRetryCount";
|
||||||
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
const char Database[] = "Database";
|
const char Database[] = "Database";
|
||||||
const char Encrypt[] = "Encrypt";
|
const char Encrypt[] = "Encrypt";
|
||||||
const char Failover_Partner[] = "Failover_Partner";
|
const char Failover_Partner[] = "Failover_Partner";
|
||||||
|
const char KeyStoreAuthentication[] = "KeyStoreAuthentication";
|
||||||
|
const char KeyStorePrincipalId[] = "KeyStorePrincipalId";
|
||||||
|
const char KeyStoreSecret[] = "KeyStoreSecret";
|
||||||
const char LoginTimeout[] = "LoginTimeout";
|
const char LoginTimeout[] = "LoginTimeout";
|
||||||
const char MARS_ODBC[] = "MARS_Connection";
|
const char MARS_ODBC[] = "MARS_Connection";
|
||||||
const char MultiSubnetFailover[] = "MultiSubnetFailover";
|
const char MultiSubnetFailover[] = "MultiSubnetFailover";
|
||||||
|
@ -1156,7 +1163,10 @@ enum SQLSRV_CONN_OPTIONS {
|
||||||
SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER,
|
SQLSRV_CONN_OPTION_CEKEYSTORE_PROVIDER,
|
||||||
SQLSRV_CONN_OPTION_CEKEYSTORE_NAME,
|
SQLSRV_CONN_OPTION_CEKEYSTORE_NAME,
|
||||||
SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY,
|
SQLSRV_CONN_OPTION_CEKEYSTORE_ENCRYPT_KEY,
|
||||||
SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION,
|
SQLSRV_CONN_OPTION_KEYSTORE_AUTHENTICATION,
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_PRINCIPAL_ID,
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_SECRET,
|
||||||
|
SQLSRV_CONN_OPTION_TRANSPARENT_NETWORK_IP_RESOLUTION,
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SQLSRV_CONN_OPTION_CONN_RETRY_COUNT,
|
SQLSRV_CONN_OPTION_CONN_RETRY_COUNT,
|
||||||
SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL,
|
SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL,
|
||||||
|
@ -1219,6 +1229,10 @@ 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 );
|
static void func( _In_ connection_option const* option, _In_ zval* value, _Inout_ sqlsrv_conn* conn, _Inout_ std::string& conn_str TSRMLS_DC );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ce_akv_str_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)
|
// 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 );
|
typedef sqlsrv_conn* (*driver_conn_factory)( _In_ SQLHANDLE h, _In_ error_callback e, _In_ void* drv TSRMLS_DC );
|
||||||
|
@ -1701,6 +1715,11 @@ enum SQLSRV_ERROR_CODES {
|
||||||
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED,
|
SQLSRV_ERROR_BUFFER_LIMIT_EXCEEDED,
|
||||||
SQLSRV_ERROR_INVALID_BUFFER_LIMIT,
|
SQLSRV_ERROR_INVALID_BUFFER_LIMIT,
|
||||||
SQLSRV_ERROR_OUTPUT_PARAM_TYPES_NOT_SUPPORTED,
|
SQLSRV_ERROR_OUTPUT_PARAM_TYPES_NOT_SUPPORTED,
|
||||||
|
SQLSRV_ERROR_INVALID_AKV_AUTHENTICATION_OPTION,
|
||||||
|
SQLSRV_ERROR_AKV_AUTH_MISSING,
|
||||||
|
SQLSRV_ERROR_AKV_NAME_MISSING,
|
||||||
|
SQLSRV_ERROR_AKV_SECRET_MISSING,
|
||||||
|
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
|
||||||
SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED,
|
SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED,
|
||||||
|
|
||||||
// Driver specific error codes starts from here.
|
// Driver specific error codes starts from here.
|
||||||
|
@ -1918,7 +1937,7 @@ namespace core {
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SQLAllocHandle( _In_ SQLSMALLINT HandleType, _Inout_ sqlsrv_context& InputHandle,
|
inline void SQLAllocHandle( _In_ SQLSMALLINT HandleType, _Inout_ sqlsrv_context& InputHandle,
|
||||||
_Out_ SQLHANDLE* OutputHandlePtr TSRMLS_DC )
|
_Out_ SQLHANDLE* OutputHandlePtr TSRMLS_DC )
|
||||||
{
|
{
|
||||||
SQLRETURN r;
|
SQLRETURN r;
|
||||||
r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr );
|
r = ::SQLAllocHandle( HandleType, InputHandle.handle(), OutputHandlePtr );
|
||||||
|
|
|
@ -184,19 +184,22 @@ namespace SSConnOptionNames {
|
||||||
const char APP[] = "APP";
|
const char APP[] = "APP";
|
||||||
const char ApplicationIntent[] = "ApplicationIntent";
|
const char ApplicationIntent[] = "ApplicationIntent";
|
||||||
const char AttachDBFileName[] = "AttachDbFileName";
|
const char AttachDBFileName[] = "AttachDbFileName";
|
||||||
const char CharacterSet[] = "CharacterSet";
|
|
||||||
const char Authentication[] = "Authentication";
|
const char Authentication[] = "Authentication";
|
||||||
const char ConnectionPooling[] = "ConnectionPooling";
|
const char CharacterSet[] = "CharacterSet";
|
||||||
const char Driver[] = "Driver";
|
|
||||||
#ifdef _WIN32
|
|
||||||
const char ColumnEncryption[] = "ColumnEncryption";
|
const char ColumnEncryption[] = "ColumnEncryption";
|
||||||
|
const char ConnectionPooling[] = "ConnectionPooling";
|
||||||
|
#ifdef _WIN32
|
||||||
const char ConnectRetryCount[] = "ConnectRetryCount";
|
const char ConnectRetryCount[] = "ConnectRetryCount";
|
||||||
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
const char ConnectRetryInterval[] = "ConnectRetryInterval";
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
const char Database[] = "Database";
|
const char Database[] = "Database";
|
||||||
const char DateAsString[] = "ReturnDatesAsStrings";
|
const char DateAsString[] = "ReturnDatesAsStrings";
|
||||||
|
const char Driver[] = "Driver";
|
||||||
const char Encrypt[] = "Encrypt";
|
const char Encrypt[] = "Encrypt";
|
||||||
const char Failover_Partner[] = "Failover_Partner";
|
const char Failover_Partner[] = "Failover_Partner";
|
||||||
|
const char KeyStoreAuthentication[] = "KeyStoreAuthentication";
|
||||||
|
const char KeyStorePrincipalId[] = "KeyStorePrincipalId";
|
||||||
|
const char KeyStoreSecret[] = "KeyStoreSecret";
|
||||||
const char LoginTimeout[] = "LoginTimeout";
|
const char LoginTimeout[] = "LoginTimeout";
|
||||||
const char MARS_Option[] = "MultipleActiveResultSets";
|
const char MARS_Option[] = "MultipleActiveResultSets";
|
||||||
const char MultiSubnetFailover[] = "MultiSubnetFailover";
|
const char MultiSubnetFailover[] = "MultiSubnetFailover";
|
||||||
|
@ -312,7 +315,6 @@ const connection_option SS_CONN_OPTS[] = {
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
driver_set_func::func
|
driver_set_func::func
|
||||||
},
|
},
|
||||||
#ifdef _WIN32
|
|
||||||
{
|
{
|
||||||
SSConnOptionNames::ColumnEncryption,
|
SSConnOptionNames::ColumnEncryption,
|
||||||
sizeof(SSConnOptionNames::ColumnEncryption),
|
sizeof(SSConnOptionNames::ColumnEncryption),
|
||||||
|
@ -322,6 +324,7 @@ const connection_option SS_CONN_OPTS[] = {
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
column_encryption_set_func::func
|
column_encryption_set_func::func
|
||||||
},
|
},
|
||||||
|
#ifdef _WIN32
|
||||||
{
|
{
|
||||||
SSConnOptionNames::ConnectRetryCount,
|
SSConnOptionNames::ConnectRetryCount,
|
||||||
sizeof( SSConnOptionNames::ConnectRetryCount ),
|
sizeof( SSConnOptionNames::ConnectRetryCount ),
|
||||||
|
@ -368,6 +371,33 @@ const connection_option SS_CONN_OPTS[] = {
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
conn_str_append_func::func
|
conn_str_append_func::func
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
SSConnOptionNames::KeyStoreAuthentication,
|
||||||
|
sizeof( SSConnOptionNames::KeyStoreAuthentication ),
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_AUTHENTICATION,
|
||||||
|
ODBCConnOptions::KeyStoreAuthentication,
|
||||||
|
sizeof( ODBCConnOptions::KeyStoreAuthentication ),
|
||||||
|
CONN_ATTR_STRING,
|
||||||
|
ce_akv_str_set_func::func
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SSConnOptionNames::KeyStorePrincipalId,
|
||||||
|
sizeof( SSConnOptionNames::KeyStorePrincipalId ),
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_PRINCIPAL_ID,
|
||||||
|
ODBCConnOptions::KeyStorePrincipalId,
|
||||||
|
sizeof( ODBCConnOptions::KeyStorePrincipalId ),
|
||||||
|
CONN_ATTR_STRING,
|
||||||
|
ce_akv_str_set_func::func
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SSConnOptionNames::KeyStoreSecret,
|
||||||
|
sizeof( SSConnOptionNames::KeyStoreSecret ),
|
||||||
|
SQLSRV_CONN_OPTION_KEYSTORE_SECRET,
|
||||||
|
ODBCConnOptions::KeyStoreSecret,
|
||||||
|
sizeof( ODBCConnOptions::KeyStoreSecret ),
|
||||||
|
CONN_ATTR_STRING,
|
||||||
|
ce_akv_str_set_func::func
|
||||||
|
},
|
||||||
{
|
{
|
||||||
SSConnOptionNames::LoginTimeout,
|
SSConnOptionNames::LoginTimeout,
|
||||||
sizeof( SSConnOptionNames::LoginTimeout ),
|
sizeof( SSConnOptionNames::LoginTimeout ),
|
||||||
|
@ -443,7 +473,7 @@ const connection_option SS_CONN_OPTS[] = {
|
||||||
{
|
{
|
||||||
SSConnOptionNames::TransparentNetworkIPResolution,
|
SSConnOptionNames::TransparentNetworkIPResolution,
|
||||||
sizeof(SSConnOptionNames::TransparentNetworkIPResolution),
|
sizeof(SSConnOptionNames::TransparentNetworkIPResolution),
|
||||||
SQLSRV_CONN_OPTION_TRANSPARANT_NETWORK_IP_RESOLUTION,
|
SQLSRV_CONN_OPTION_TRANSPARENT_NETWORK_IP_RESOLUTION,
|
||||||
ODBCConnOptions::TransparentNetworkIPResolution,
|
ODBCConnOptions::TransparentNetworkIPResolution,
|
||||||
sizeof(ODBCConnOptions::TransparentNetworkIPResolution),
|
sizeof(ODBCConnOptions::TransparentNetworkIPResolution),
|
||||||
CONN_ATTR_STRING,
|
CONN_ATTR_STRING,
|
||||||
|
@ -525,7 +555,7 @@ PHP_FUNCTION ( sqlsrv_connect )
|
||||||
core::sqlsrv_zend_hash_init( *g_ss_henv_cp, ss_conn_options_ht, 10 /* # of buckets */,
|
core::sqlsrv_zend_hash_init( *g_ss_henv_cp, ss_conn_options_ht, 10 /* # of buckets */,
|
||||||
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
|
ZVAL_PTR_DTOR, 0 /*persistent*/ TSRMLS_CC );
|
||||||
|
|
||||||
// Either of g_ss_henv_cp or g_ss_henv_ncp can be used to propogate the error.
|
// Either of g_ss_henv_cp or g_ss_henv_ncp can be used to propagate the error.
|
||||||
::validate_conn_options( *g_ss_henv_cp, options_z, &uid, &pwd, ss_conn_options_ht TSRMLS_CC );
|
::validate_conn_options( *g_ss_henv_cp, options_z, &uid, &pwd, ss_conn_options_ht TSRMLS_CC );
|
||||||
|
|
||||||
// call the core connect function
|
// call the core connect function
|
||||||
|
|
|
@ -400,6 +400,26 @@ ss_error SS_ERRORS[] = {
|
||||||
SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED,
|
SQLSRV_ERROR_DOUBLE_CONVERSION_FAILED,
|
||||||
{ IMSSP, (SQLCHAR*)"Error converting a double (value out of range) to an integer.", -109, false }
|
{ IMSSP, (SQLCHAR*)"Error converting a double (value out of range) to an integer.", -109, false }
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_INVALID_AKV_AUTHENTICATION_OPTION,
|
||||||
|
{ IMSSP, (SQLCHAR*) "Invalid option for the KeyStoreAuthentication keyword. Only KeyVaultPassword or KeyVaultClientSecret is allowed.", -110, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_AKV_AUTH_MISSING,
|
||||||
|
{ IMSSP, (SQLCHAR*) "The authentication method for Azure Key Vault is missing. KeyStoreAuthentication must be set to KeyVaultPassword or KeyVaultClientSecret.", -111, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_AKV_NAME_MISSING,
|
||||||
|
{ IMSSP, (SQLCHAR*) "The username or client Id for Azure Key Vault is missing.", -112, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_AKV_SECRET_MISSING,
|
||||||
|
{ IMSSP, (SQLCHAR*) "The password or client secret for Azure Key Vault is missing.", -113, false }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
SQLSRV_ERROR_KEYSTORE_INVALID_VALUE,
|
||||||
|
{ IMSSP, (SQLCHAR*) "Invalid value for loading Azure Key Vault.", -114, false}
|
||||||
|
},
|
||||||
|
|
||||||
// terminate the list of errors/warnings
|
// terminate the list of errors/warnings
|
||||||
{ UINT_MAX, {} }
|
{ UINT_MAX, {} }
|
||||||
|
|
|
@ -92,6 +92,13 @@ function getDSN($sqlsrvserver, $database, $keywords = '', $disableCE = false)
|
||||||
if ($keystore != "none" && !$disableCE) {
|
if ($keystore != "none" && !$disableCE) {
|
||||||
$dsn .= "ColumnEncryption=Enabled;";
|
$dsn .= "ColumnEncryption=Enabled;";
|
||||||
}
|
}
|
||||||
|
if ($keystore == "akv" && !$disableCE) {
|
||||||
|
if ($AKVKeyStoreAuthentication == "KeyVaultPassword") {
|
||||||
|
$dsn .= "KeyStoreAuthentication=$AKVKeyStoreAuthentication;KeyStorePrincipalId=$AKVPrincipalName;KeyStoreSecret=$AKVPassword;";
|
||||||
|
} else if ($AKVKeyStoreAuthentication == "KeyVaultClientSecret") {
|
||||||
|
$dsn .= "KeyStoreAuthentication=$AKVKeyStoreAuthentication;KeyStorePrincipalId=$AKVClientID;KeyStoreSecret=$AKVSecret;";
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($keystore == "ksp" && !$disableCE) {
|
if ($keystore == "ksp" && !$disableCE) {
|
||||||
$ksp_path = getKSPPath();
|
$ksp_path = getKSPPath();
|
||||||
$ksp_name = KSP_NAME;
|
$ksp_name = KSP_NAME;
|
||||||
|
|
|
@ -44,4 +44,11 @@ $traceEnabled = false;
|
||||||
$keystore = "none"; // key store provider, acceptable values are none, win, ksp, akv
|
$keystore = "none"; // key store provider, acceptable values are none, win, ksp, akv
|
||||||
$dataEncrypted = false; // whether data is to be encrypted
|
$dataEncrypted = false; // whether data is to be encrypted
|
||||||
|
|
||||||
|
// for Azure Key Vault
|
||||||
|
$AKVKeyStoreAuthentication = 'TARGET_AKV_AUTH'; // can be KeyVaultPassword or KeyVaultClientSecret
|
||||||
|
$AKVPrincipalName = 'TARGET_AKV_PRINCIPAL_NAME'; // for use with KeyVaultPassword
|
||||||
|
$AKVPassword = 'TARGET_AKV_PASSWORD'; // for use with KeyVaultPassword
|
||||||
|
$AKVClientID = 'TARGET_AKV_CLIENT_ID'; // for use with KeyVaultClientSecret
|
||||||
|
$AKVSecret = 'TARGET_AKV_CLIENT_SECRET'; // for use with KeyVaultClientSecret
|
||||||
|
|
||||||
?>
|
?>
|
196
test/functional/pdo_sqlsrv/pdo_ae_azure_key_vault_keywords.phpt
Normal file
196
test/functional/pdo_sqlsrv/pdo_ae_azure_key_vault_keywords.phpt
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
--TEST--
|
||||||
|
Test connection keywords for Azure Key Vault for Always Encrypted.
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
// Skip for 32-bit PHP, as bug causes this test to fail when inserting a bigint
|
||||||
|
if (PHP_INT_SIZE == 4) {
|
||||||
|
echo "Done.\n";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once("MsCommon_mid-refactor.inc");
|
||||||
|
require_once("MsSetup.inc");
|
||||||
|
require_once('values.php');
|
||||||
|
|
||||||
|
// We will test the direct product (set of all possible combinations) of the following
|
||||||
|
$columnEncryption = ['enabled', 'disabled', 'notvalid', ''];
|
||||||
|
$keyStoreAuthentication = ['KeyVaultPassword', 'KeyVaultClientSecret', 'KeyVaultNothing', ''];
|
||||||
|
$keyStorePrincipalId = [$AKVPrincipalName, $AKVClientID, 'notaname', ''];
|
||||||
|
$keyStoreSecret = [$AKVPassword, $AKVSecret, 'notasecret', ''];
|
||||||
|
|
||||||
|
// Verify that the error is in the list of expected errors
|
||||||
|
function checkErrors($errors, ...$codes)
|
||||||
|
{
|
||||||
|
$codeFound = false;
|
||||||
|
|
||||||
|
foreach ($codes as $code) {
|
||||||
|
if ($code[0]==$errors[0] and $code[1]==$errors[1]) {
|
||||||
|
$codeFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($codeFound == false) {
|
||||||
|
echo "Error: ";
|
||||||
|
print_r($errors);
|
||||||
|
echo "\nExpected: ";
|
||||||
|
print_r($codes);
|
||||||
|
echo "\n";
|
||||||
|
fatalError("Error code not found.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the columns and build the insert query. Each data type has an
|
||||||
|
// AE-encrypted and a non-encrypted column side by side in the table.
|
||||||
|
// If column encryption is not set in MsSetup.inc, this function simply
|
||||||
|
// creates two non-encrypted columns side-by-side for each type.
|
||||||
|
function formulateSetupQuery($tableName, &$dataTypes, &$columns, &$insertQuery)
|
||||||
|
{
|
||||||
|
$columns = array();
|
||||||
|
$queryTypes = "(";
|
||||||
|
$queryTypesAE = "(";
|
||||||
|
$valuesString = "VALUES (";
|
||||||
|
$numTypes = sizeof($dataTypes);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $numTypes; ++$i) {
|
||||||
|
// Replace parentheses for column names
|
||||||
|
$colname = str_replace(array("(", ",", ")"), array("_", "_", ""), $dataTypes[$i]);
|
||||||
|
$columns[] = new ColumnMeta($dataTypes[$i], "c_".$colname."_AE", null, "deterministic", false);
|
||||||
|
$columns[] = new ColumnMeta($dataTypes[$i], "c_".$colname, null, "none", false);
|
||||||
|
$queryTypes .= "c_"."$colname, ";
|
||||||
|
$queryTypes .= "c_"."$colname"."_AE, ";
|
||||||
|
$valuesString .= "?, ?, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryTypes = substr($queryTypes, 0, -2).")";
|
||||||
|
$valuesString = substr($valuesString, 0, -2).")";
|
||||||
|
|
||||||
|
$insertQuery = "INSERT INTO $tableName ".$queryTypes." ".$valuesString;
|
||||||
|
}
|
||||||
|
|
||||||
|
$strsize = 64;
|
||||||
|
|
||||||
|
$dataTypes = array("char($strsize)", "varchar($strsize)", "nvarchar($strsize)",
|
||||||
|
"decimal", "float", "real", "bigint", "int", "bit"
|
||||||
|
);
|
||||||
|
|
||||||
|
$tableName = "akv_comparison_table";
|
||||||
|
|
||||||
|
// Test every combination of the keywords above.
|
||||||
|
// Leave out good credentials to ensure that caching does not influence the
|
||||||
|
// results. The cache timeout can only be changed with SQLSetConnectAttr, so
|
||||||
|
// we can't run a PHP test without caching, and if we started with good
|
||||||
|
// credentials then subsequent calls with bad credentials can work, which
|
||||||
|
// would muddle the results of this test. Good credentials are tested in a
|
||||||
|
// separate test.
|
||||||
|
for ($i = 0; $i < sizeof($columnEncryption); ++$i) {
|
||||||
|
for ($j = 0; $j < sizeof($keyStoreAuthentication); ++$j) {
|
||||||
|
for ($k = 0; $k < sizeof($keyStorePrincipalId); ++$k) {
|
||||||
|
for ($m = 0; $m < sizeof($keyStoreSecret); ++$m) {
|
||||||
|
$connectionOptions = "sqlsrv:Server=$server;Database=$databaseName";
|
||||||
|
|
||||||
|
if (!empty($columnEncryption[$i])) {
|
||||||
|
$connectionOptions .= ";ColumnEncryption=".$columnEncryption[$i];
|
||||||
|
}
|
||||||
|
if (!empty($keyStoreAuthentication[$j])) {
|
||||||
|
$connectionOptions .= ";KeyStoreAuthentication=".$keyStoreAuthentication[$j];
|
||||||
|
}
|
||||||
|
if (!empty($keyStorePrincipalId[$k])) {
|
||||||
|
$connectionOptions .= ";KeyStorePrincipalId=".$keyStorePrincipalId[$k];
|
||||||
|
}
|
||||||
|
if (!empty($keyStoreSecret[$m])) {
|
||||||
|
$connectionOptions .= ";KeyStoreSecret=".$keyStoreSecret[$m];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid credentials getting skipped
|
||||||
|
if (($i == 0 and $j == 0 and $k == 0 and $m == 0) or
|
||||||
|
($i == 0 and $j == 1 and $k == 1 and $m == 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$connectionOptions .= ";";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Connect to the AE-enabled database
|
||||||
|
$conn = new PDO($connectionOptions, $uid, $pwd);
|
||||||
|
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
$columns = array();
|
||||||
|
$insertQuery = "";
|
||||||
|
|
||||||
|
// Generate the INSERT query
|
||||||
|
formulateSetupQuery($tableName, $dataTypes, $columns, $insertQuery);
|
||||||
|
|
||||||
|
createTable($conn, $tableName, $columns);
|
||||||
|
|
||||||
|
// Duplicate all values for insertion - one is encrypted, one is not
|
||||||
|
$testValues = array();
|
||||||
|
for ($n = 0; $n < sizeof($small_values); ++$n) {
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the INSERT query
|
||||||
|
// This is never expected to fail
|
||||||
|
$stmt = $conn->prepare($insertQuery);
|
||||||
|
if ($stmt == false) {
|
||||||
|
print_r($conn->errorInfo());
|
||||||
|
fatalError("sqlsrv_prepare failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the INSERT query
|
||||||
|
// Failure expected only if the keywords/credentials are wrong
|
||||||
|
if ($stmt->execute($testValues) == false) {
|
||||||
|
print_r($stmt->errorInfo());
|
||||||
|
$stmt = null;
|
||||||
|
} else {
|
||||||
|
// The INSERT query succeeded with bad credentials, which
|
||||||
|
// should only happen when encryption is not enabled.
|
||||||
|
if (isColEncrypted()) {
|
||||||
|
fatalError("Successful insertion with bad credentials\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the statement and close the connection
|
||||||
|
$stmt = null;
|
||||||
|
$conn = null;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$errors = $e->errorInfo;
|
||||||
|
|
||||||
|
if (!isColEncrypted()) {
|
||||||
|
checkErrors(
|
||||||
|
$errors,
|
||||||
|
array('CE258', '0'),
|
||||||
|
array('CE275', '0'),
|
||||||
|
array('IMSSP', '-85'),
|
||||||
|
array('IMSSP', '-86'),
|
||||||
|
array('IMSSP', '-87'),
|
||||||
|
array('IMSSP', '-88'),
|
||||||
|
array('08001', '0'),
|
||||||
|
array('08001', '-1') // SSL error occurs in Ubuntu
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
checkErrors(
|
||||||
|
$errors,
|
||||||
|
array('CE258', '0'),
|
||||||
|
array('CE275', '0'),
|
||||||
|
array('IMSSP', '-85'),
|
||||||
|
array('IMSSP', '-86'),
|
||||||
|
array('IMSSP', '-87'),
|
||||||
|
array('IMSSP', '-88'),
|
||||||
|
array('08001', '0'),
|
||||||
|
array('08001', '-1'), // SSL error occurs in Ubuntu
|
||||||
|
array('22018', '206')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Done.\n";
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Done.
|
|
@ -0,0 +1,207 @@
|
||||||
|
--TEST--
|
||||||
|
Test credentials for Azure Key Vault for Always Encrypted.
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
// TODO: Fix the test on Ubuntu - right now it produces a SSL error on Ubuntu
|
||||||
|
// The following skips Ubuntu to prevent a test failure
|
||||||
|
$is_ubuntu = php_uname('v');
|
||||||
|
if (strpos($is_ubuntu, 'buntu') !== false) {
|
||||||
|
echo "Skipping test on Ubuntu\n";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once("MsCommon_mid-refactor.inc");
|
||||||
|
require_once("MsSetup.inc");
|
||||||
|
require_once('values.php');
|
||||||
|
|
||||||
|
// Set up the columns and build the insert query. Each data type has an
|
||||||
|
// AE-encrypted and a non-encrypted column side by side in the table.
|
||||||
|
function formulateSetupQuery($tableName, &$dataTypes, &$columns, &$insertQuery)
|
||||||
|
{
|
||||||
|
$columns = array();
|
||||||
|
$queryTypes = "(";
|
||||||
|
$queryTypesAE = "(";
|
||||||
|
$valuesString = "VALUES (";
|
||||||
|
$numTypes = sizeof($dataTypes);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $numTypes; ++$i) {
|
||||||
|
// Replace parentheses for column names
|
||||||
|
$colname = str_replace(array("(", ",", ")"), array("_", "_", ""), $dataTypes[$i]);
|
||||||
|
$columns[] = new ColumnMeta($dataTypes[$i], "c_".$colname."_AE", null, "deterministic", false);
|
||||||
|
$columns[] = new ColumnMeta($dataTypes[$i], "c_".$colname, null, "none", false);
|
||||||
|
$queryTypes .= "c_"."$colname, ";
|
||||||
|
$queryTypes .= "c_"."$colname"."_AE, ";
|
||||||
|
$valuesString .= "?, ?, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryTypes = substr($queryTypes, 0, -2).")";
|
||||||
|
$valuesString = substr($valuesString, 0, -2).")";
|
||||||
|
|
||||||
|
$insertQuery = "INSERT INTO $tableName ".$queryTypes." ".$valuesString;
|
||||||
|
}
|
||||||
|
|
||||||
|
$strsize = 64;
|
||||||
|
|
||||||
|
$dataTypes = array("char($strsize)", "varchar($strsize)", "nvarchar($strsize)",
|
||||||
|
"decimal", "float", "real", "bigint", "int", "bit"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test data insertion and retrieval with username/password
|
||||||
|
// and client Id/client secret combinations.
|
||||||
|
$connectionOptions = "sqlsrv:Server=$server;Database=$databaseName";
|
||||||
|
|
||||||
|
$connectionOptions .= ";ColumnEncryption=enabled";
|
||||||
|
$connectionOptions .= ";KeyStoreAuthentication=KeyVaultPassword";
|
||||||
|
$connectionOptions .= ";KeyStorePrincipalId=".$AKVPrincipalName;
|
||||||
|
$connectionOptions .= ";KeyStoreSecret=".$AKVPassword;
|
||||||
|
$connectionOptions .= ";";
|
||||||
|
|
||||||
|
$tableName = "akv_comparison_table";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Connect to the AE-enabled database
|
||||||
|
$conn = new PDO($connectionOptions, $uid, $pwd);
|
||||||
|
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
$columns = array();
|
||||||
|
$insertQuery = "";
|
||||||
|
|
||||||
|
// Generate the INSERT query
|
||||||
|
formulateSetupQuery($tableName, $dataTypes, $columns, $insertQuery);
|
||||||
|
|
||||||
|
createTable($conn, $tableName, $columns);
|
||||||
|
|
||||||
|
// Duplicate all values for insertion - one is encrypted, one is not
|
||||||
|
$testValues = array();
|
||||||
|
for ($n = 0; $n < sizeof($small_values); ++$n) {
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the INSERT query
|
||||||
|
// This is never expected to fail
|
||||||
|
$stmt = $conn->prepare($insertQuery);
|
||||||
|
if ($stmt == false) {
|
||||||
|
print_r($conn->errorInfo());
|
||||||
|
fatalError("sqlsrv_prepare failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the INSERT query
|
||||||
|
// This should not fail since our credentials are correct
|
||||||
|
if ($stmt->execute($testValues) == false) {
|
||||||
|
print_r($stmt->errorInfo());
|
||||||
|
fatalError("INSERT query execution failed with good credentials.\n");
|
||||||
|
} else {
|
||||||
|
// Get the data back and compare encrypted and non-encrypted versions
|
||||||
|
$selectQuery = "SELECT * FROM $tableName";
|
||||||
|
|
||||||
|
$stmt1 = $conn->query($selectQuery);
|
||||||
|
|
||||||
|
$data = $stmt1->fetchAll(PDO::FETCH_NUM);
|
||||||
|
$data = $data[0];
|
||||||
|
|
||||||
|
if (sizeof($data) != 2*sizeof($dataTypes)) {
|
||||||
|
fatalError("Incorrect number of fields returned.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($n = 0; $n < sizeof($data); $n += 2) {
|
||||||
|
if ($data[$n] != $data[$n + 1]) {
|
||||||
|
echo "Failed on field $n: ".$data[$n]." ".$data[$n + 1]."\n";
|
||||||
|
fatalError("AE and non-AE values do not match.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Successful insertion and retrieval with username/password.\n";
|
||||||
|
|
||||||
|
$stmt = null;
|
||||||
|
$stmt1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the statement and close the connection
|
||||||
|
$stmt = null;
|
||||||
|
$conn = null;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Unexpected error.\n";
|
||||||
|
print_r($e->errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
$connectionOptions = "sqlsrv:Server=$server;Database=$databaseName";
|
||||||
|
|
||||||
|
$connectionOptions .= ";ColumnEncryption=enabled";
|
||||||
|
$connectionOptions .= ";KeyStoreAuthentication=KeyVaultClientSecret";
|
||||||
|
$connectionOptions .= ";KeyStorePrincipalId=".$AKVClientID;
|
||||||
|
$connectionOptions .= ";KeyStoreSecret=".$AKVSecret;
|
||||||
|
$connectionOptions .= ";";
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Connect to the AE-enabled database
|
||||||
|
$conn = new PDO($connectionOptions, $uid, $pwd);
|
||||||
|
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
|
||||||
|
$columns = array();
|
||||||
|
$insertQuery = "";
|
||||||
|
|
||||||
|
// Generate the INSERT query
|
||||||
|
formulateSetupQuery($tableName, $dataTypes, $columns, $insertQuery);
|
||||||
|
|
||||||
|
createTable($conn, $tableName, $columns);
|
||||||
|
|
||||||
|
// Duplicate all values for insertion - one is encrypted, one is not
|
||||||
|
$testValues = array();
|
||||||
|
for ($n = 0; $n < sizeof($small_values); ++$n) {
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the INSERT query
|
||||||
|
// This is never expected to fail
|
||||||
|
$stmt = $conn->prepare($insertQuery);
|
||||||
|
if ($stmt == false) {
|
||||||
|
print_r($conn->errorInfo());
|
||||||
|
fatalError("sqlsrv_prepare failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the INSERT query
|
||||||
|
// This should not fail since our credentials are correct
|
||||||
|
if ($stmt->execute($testValues) == false) {
|
||||||
|
print_r($stmt->errorInfo());
|
||||||
|
fatalError("INSERT query execution failed with good credentials.\n");
|
||||||
|
} else {
|
||||||
|
// Get the data back and compare encrypted and non-encrypted versions
|
||||||
|
$selectQuery = "SELECT * FROM $tableName";
|
||||||
|
|
||||||
|
$stmt1 = $conn->query($selectQuery);
|
||||||
|
|
||||||
|
$data = $stmt1->fetchAll(PDO::FETCH_NUM);
|
||||||
|
$data = $data[0];
|
||||||
|
|
||||||
|
if (sizeof($data) != 2*sizeof($dataTypes)) {
|
||||||
|
fatalError("Incorrect number of fields returned.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($n = 0; $n < sizeof($data); $n += 2) {
|
||||||
|
if ($data[$n] != $data[$n + 1]) {
|
||||||
|
echo "Failed on field $n: ".$data[$n]." ".$data[$n + 1]."\n";
|
||||||
|
fatalError("AE and non-AE values do not match.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Successful insertion and retrieval with client ID/secret.\n";
|
||||||
|
|
||||||
|
$stmt = null;
|
||||||
|
$stmt1 = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the statement and close the connection
|
||||||
|
$stmt = null;
|
||||||
|
$conn = null;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
echo "Unexpected error.\n";
|
||||||
|
print_r($e->errorInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTREGEX--
|
||||||
|
(Successful insertion and retrieval with username\/password\.\nSuccessful insertion and retrieval with client ID\/secret\.|Skipping test on Ubuntu)
|
|
@ -109,11 +109,7 @@ function testEncryptedWithODBC()
|
||||||
$value = "ODBC Driver 13 for SQL Server";
|
$value = "ODBC Driver 13 for SQL Server";
|
||||||
$connectionOptions = "Driver = $value; ColumnEncryption = Enabled;";
|
$connectionOptions = "Driver = $value; ColumnEncryption = Enabled;";
|
||||||
|
|
||||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
|
||||||
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
|
|
||||||
} else {
|
|
||||||
$expected = "An invalid keyword 'ColumnEncryption' was specified in the DSN string.";
|
|
||||||
}
|
|
||||||
|
|
||||||
connectVerifyOutput($connectionOptions, $expected);
|
connectVerifyOutput($connectionOptions, $expected);
|
||||||
}
|
}
|
||||||
|
|
799
test/functional/pdo_sqlsrv/values.php
Normal file
799
test/functional/pdo_sqlsrv/values.php
Normal file
File diff suppressed because one or more lines are too long
|
@ -359,6 +359,17 @@ function connect($options = array(), $disableCE = false)
|
||||||
if (isColEncrypted()) {
|
if (isColEncrypted()) {
|
||||||
$connectionOptions = array_merge($connectionOptions, array("ColumnEncryption" => "Enabled"));
|
$connectionOptions = array_merge($connectionOptions, array("ColumnEncryption" => "Enabled"));
|
||||||
}
|
}
|
||||||
|
if ($keystore == 'akv') {
|
||||||
|
$akv_options = array("KeyStoreAuthentication"=>$AKVKeyStoreAuthentication);
|
||||||
|
if ($AKVKeyStoreAuthentication == "KeyVaultPassword") {
|
||||||
|
$akv_options["KeyStorePrincipalId"] = $AKVPrincipalName;
|
||||||
|
$akv_options["KeyStoreSecret"] = $AKVPassword;
|
||||||
|
} else if ($AKVKeyStoreAuthentication == "KeyVaultClientSecret") {
|
||||||
|
$akv_options["KeyStorePrincipalId"] = $AKVClientID;
|
||||||
|
$akv_options["KeyStoreSecret"] = $AKVSecret;
|
||||||
|
}
|
||||||
|
$connectionOptions = array_merge($connectionOptions, $akv_options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$conn = sqlsrv_connect($server, $connectionOptions);
|
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||||
if ($conn === false) {
|
if ($conn === false) {
|
||||||
|
|
|
@ -44,4 +44,11 @@ if (isset($_ENV['MSSQL_SERVER']) || isset($_ENV['MSSQL_USER']) || isset($_ENV['M
|
||||||
$keystore = "none"; // key store provider, acceptable values are none, win, ksp, akv
|
$keystore = "none"; // key store provider, acceptable values are none, win, ksp, akv
|
||||||
$dataEncrypted = false; // whether data is to be encrypted
|
$dataEncrypted = false; // whether data is to be encrypted
|
||||||
|
|
||||||
|
// for Azure Key Vault
|
||||||
|
$AKVKeyStoreAuthentication = 'TARGET_AKV_AUTH'; // can be KeyVaultPassword or KeyVaultClientSecret
|
||||||
|
$AKVPrincipalName = 'TARGET_AKV_PRINCIPAL_NAME'; // for use with KeyVaultPassword
|
||||||
|
$AKVPassword = 'TARGET_AKV_PASSWORD'; // for use with KeyVaultPassword
|
||||||
|
$AKVClientID = 'TARGET_AKV_CLIENT_ID'; // for use with KeyVaultClientSecret
|
||||||
|
$AKVSecret = 'TARGET_AKV_CLIENT_SECRET'; // for use with KeyVaultClientSecret
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
190
test/functional/sqlsrv/sqlsrv_ae_azure_key_vault_keywords.phpt
Normal file
190
test/functional/sqlsrv/sqlsrv_ae_azure_key_vault_keywords.phpt
Normal file
|
@ -0,0 +1,190 @@
|
||||||
|
--TEST--
|
||||||
|
Test connection keywords for Azure Key Vault for Always Encrypted.
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
require_once('MsCommon.inc');
|
||||||
|
require_once('values.php');
|
||||||
|
|
||||||
|
// We will test the direct product (set of all possible combinations) of the following
|
||||||
|
$columnEncryption = ['enabled', 'disabled', 'notvalid', ''];
|
||||||
|
$keyStoreAuthentication = ['KeyVaultPassword', 'KeyVaultClientSecret', 'KeyVaultNothing', ''];
|
||||||
|
$keyStorePrincipalId = [$AKVPrincipalName, $AKVClientID, 'notaname', ''];
|
||||||
|
$keyStoreSecret = [$AKVPassword, $AKVSecret, 'notasecret', ''];
|
||||||
|
|
||||||
|
$is_win = (strtoupper(substr(php_uname('s'), 0, 3)) === 'WIN');
|
||||||
|
|
||||||
|
function checkErrors($errors, ...$codes)
|
||||||
|
{
|
||||||
|
$codeFound = false;
|
||||||
|
|
||||||
|
foreach ($codes as $code) {
|
||||||
|
if ($code[0]==$errors[0][0] and $code[1]==$errors[0][1]) {
|
||||||
|
$codeFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($codeFound == false) {
|
||||||
|
echo "Error: ";
|
||||||
|
print_r($errors);
|
||||||
|
echo "\nExpected: ";
|
||||||
|
print_r($codes);
|
||||||
|
echo "\n";
|
||||||
|
fatalError("Error code not found.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the columns and build the insert query. Each data type has an
|
||||||
|
// AE-encrypted and a non-encrypted column side by side in the table.
|
||||||
|
function formulateSetupQuery($tableName, &$dataTypes, &$columns, &$insertQuery)
|
||||||
|
{
|
||||||
|
$columns = array();
|
||||||
|
$queryTypes = "(";
|
||||||
|
$queryTypesAE = "(";
|
||||||
|
$valuesString = "VALUES (";
|
||||||
|
$numTypes = sizeof($dataTypes);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $numTypes; ++$i) {
|
||||||
|
// Replace parentheses for column names
|
||||||
|
$colname = str_replace(array("(", ",", ")"), array("_", "_", ""), $dataTypes[$i]);
|
||||||
|
$columns[] = new AE\ColumnMeta($dataTypes[$i], "c_".$colname."_AE");
|
||||||
|
$columns[] = new AE\ColumnMeta($dataTypes[$i], "c_".$colname, null, true, true);
|
||||||
|
$queryTypes .= "c_"."$colname, ";
|
||||||
|
$queryTypes .= "c_"."$colname"."_AE, ";
|
||||||
|
$valuesString .= "?, ?, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryTypes = substr($queryTypes, 0, -2).")";
|
||||||
|
$valuesString = substr($valuesString, 0, -2).")";
|
||||||
|
|
||||||
|
$insertQuery = "INSERT INTO $tableName ".$queryTypes." ".$valuesString;
|
||||||
|
}
|
||||||
|
|
||||||
|
$strsize = 64;
|
||||||
|
|
||||||
|
$dataTypes = array("char($strsize)", "varchar($strsize)", "nvarchar($strsize)",
|
||||||
|
"decimal", "float", "real", "bigint", "int", "bit"
|
||||||
|
);
|
||||||
|
|
||||||
|
$tableName = "akv_comparison_table";
|
||||||
|
|
||||||
|
// Test every combination of the keywords above.
|
||||||
|
// Leave out good credentials to ensure that caching does not influence the
|
||||||
|
// results. The cache timeout can only be changed with SQLSetConnectAttr, so
|
||||||
|
// we can't run a PHP test without caching, and if we started with good
|
||||||
|
// credentials then subsequent calls with bad credentials can work, which
|
||||||
|
// would muddle the results of this test. Good credentials are tested in a
|
||||||
|
// separate test.
|
||||||
|
for ($i = 0; $i < sizeof($columnEncryption); ++$i) {
|
||||||
|
for ($j = 0; $j < sizeof($keyStoreAuthentication); ++$j) {
|
||||||
|
for ($k = 0; $k < sizeof($keyStorePrincipalId); ++$k) {
|
||||||
|
for ($m = 0; $m < sizeof($keyStoreSecret); ++$m) {
|
||||||
|
$connectionOptions = array("CharacterSet"=>"UTF-8",
|
||||||
|
"database"=>$databaseName,
|
||||||
|
"uid"=>$uid,
|
||||||
|
"pwd"=>$pwd,
|
||||||
|
"ConnectionPooling"=>0);
|
||||||
|
|
||||||
|
if (!empty($columnEncryption[$i])) {
|
||||||
|
$connectionOptions['ColumnEncryption'] = $columnEncryption[$i];
|
||||||
|
}
|
||||||
|
if (!empty($keyStoreAuthentication[$j])) {
|
||||||
|
$connectionOptions['KeyStoreAuthentication'] = $keyStoreAuthentication[$j];
|
||||||
|
}
|
||||||
|
if (!empty($keyStorePrincipalId[$k])) {
|
||||||
|
$connectionOptions['KeyStorePrincipalId'] = $keyStorePrincipalId[$k];
|
||||||
|
}
|
||||||
|
if (!empty($keyStoreSecret[$m])) {
|
||||||
|
$connectionOptions['KeyStoreSecret'] = $keyStoreSecret[$m];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid credentials getting skipped
|
||||||
|
if (($i == 0 and $j == 0 and $k == 0 and $m == 0) or
|
||||||
|
($i == 0 and $j == 1 and $k == 1 and $m == 1)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Connect to the AE-enabled database
|
||||||
|
// Failure is expected when the keyword combination is wrong
|
||||||
|
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||||
|
if (!$conn) {
|
||||||
|
$errors = sqlsrv_errors();
|
||||||
|
|
||||||
|
checkErrors(
|
||||||
|
$errors,
|
||||||
|
array('08001','0'),
|
||||||
|
array('08001','-1'), // SSL error occurs in Ubuntu
|
||||||
|
array('IMSSP','-110'),
|
||||||
|
array('IMSSP','-111'),
|
||||||
|
array('IMSSP','-112'),
|
||||||
|
array('IMSSP','-113')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$columns = array();
|
||||||
|
$insertQuery = "";
|
||||||
|
|
||||||
|
// Generate the INSERT query
|
||||||
|
formulateSetupQuery($tableName, $dataTypes, $columns, $insertQuery);
|
||||||
|
|
||||||
|
$stmt = AE\createTable($conn, $tableName, $columns);
|
||||||
|
if (!$stmt) {
|
||||||
|
fatalError("Failed to create table $tableName.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate all values for insertion - one is encrypted, one is not
|
||||||
|
$testValues = array();
|
||||||
|
for ($n = 0; $n < sizeof($small_values); ++$n) {
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the INSERT query
|
||||||
|
// This is never expected to fail
|
||||||
|
$stmt = sqlsrv_prepare($conn, $insertQuery, $testValues);
|
||||||
|
if ($stmt == false) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
fatalError("sqlsrv_prepare failed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the INSERT query
|
||||||
|
// This is where we expect failure if the credentials are incorrect
|
||||||
|
if (sqlsrv_execute($stmt) == false) {
|
||||||
|
$errors = sqlsrv_errors();
|
||||||
|
|
||||||
|
if (!AE\isColEncrypted()) {
|
||||||
|
checkErrors(
|
||||||
|
$errors,
|
||||||
|
array('CE258', '0'),
|
||||||
|
array('CE275', '0')
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
checkErrors(
|
||||||
|
$errors,
|
||||||
|
array('CE258', '0'),
|
||||||
|
array('CE275', '0'),
|
||||||
|
array('22018', '206')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
} else {
|
||||||
|
// The INSERT query succeeded with bad credentials, which
|
||||||
|
// should only happen when encryption is not enabled.
|
||||||
|
if (AE\isColEncrypted()) {
|
||||||
|
fatalError("Successful insertion with bad credentials\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the statement and close the connection
|
||||||
|
sqlsrv_close($conn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Done.\n";
|
||||||
|
?>
|
||||||
|
--EXPECT--
|
||||||
|
Done.
|
|
@ -0,0 +1,203 @@
|
||||||
|
--TEST--
|
||||||
|
Test credentials for Azure Key Vault for Always Encrypted.
|
||||||
|
--SKIPIF--
|
||||||
|
<?php require('skipif.inc'); ?>
|
||||||
|
--FILE--
|
||||||
|
<?php
|
||||||
|
// TODO: Fix the test on Ubuntu - right now it produces a SSL error on Ubuntu
|
||||||
|
// The following skips Ubuntu to prevent a test failure
|
||||||
|
$is_ubuntu = php_uname('v');
|
||||||
|
if (strpos($is_ubuntu, 'buntu') !== false)
|
||||||
|
{
|
||||||
|
echo "Skipping test on Ubuntu\n";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
require_once('MsCommon.inc');
|
||||||
|
require_once('values.php');
|
||||||
|
|
||||||
|
// Set up the columns and build the insert query. Each data type has an
|
||||||
|
// AE-encrypted and a non-encrypted column side by side in the table.
|
||||||
|
function formulateSetupQuery($tableName, &$dataTypes, &$columns, &$insertQuery)
|
||||||
|
{
|
||||||
|
$columns = array();
|
||||||
|
$queryTypes = "(";
|
||||||
|
$queryTypesAE = "(";
|
||||||
|
$valuesString = "VALUES (";
|
||||||
|
$numTypes = sizeof($dataTypes);
|
||||||
|
|
||||||
|
for ($i = 0; $i < $numTypes; ++$i) {
|
||||||
|
// Replace parentheses for column names
|
||||||
|
$colname = str_replace(array("(", ",", ")"), array("_", "_", ""), $dataTypes[$i]);
|
||||||
|
$columns[] = new AE\ColumnMeta($dataTypes[$i], "c_".$colname."_AE");
|
||||||
|
$columns[] = new AE\ColumnMeta($dataTypes[$i], "c_".$colname, null, true, true);
|
||||||
|
$queryTypes .= "c_"."$colname, ";
|
||||||
|
$queryTypes .= "c_"."$colname"."_AE, ";
|
||||||
|
$valuesString .= "?, ?, ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$queryTypes = substr($queryTypes, 0, -2).")";
|
||||||
|
$valuesString = substr($valuesString, 0, -2).")";
|
||||||
|
|
||||||
|
$insertQuery = "INSERT INTO $tableName ".$queryTypes." ".$valuesString;
|
||||||
|
}
|
||||||
|
|
||||||
|
$strsize = 64;
|
||||||
|
|
||||||
|
$dataTypes = array ("char($strsize)", "varchar($strsize)", "nvarchar($strsize)",
|
||||||
|
"decimal", "float", "real", "bigint", "int", "bit"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test data insertion and retrieval with username/password
|
||||||
|
// and client Id/client secret combinations.
|
||||||
|
$connectionOptions = array("CharacterSet"=>"UTF-8",
|
||||||
|
"database"=>$databaseName,
|
||||||
|
"uid"=>$uid,
|
||||||
|
"pwd"=>$pwd,
|
||||||
|
"ConnectionPooling"=>0);
|
||||||
|
|
||||||
|
$connectionOptions['ColumnEncryption'] = "enabled";
|
||||||
|
$connectionOptions['KeyStoreAuthentication'] = "KeyVaultPassword";
|
||||||
|
$connectionOptions['KeyStorePrincipalId'] = $AKVPrincipalName;
|
||||||
|
$connectionOptions['KeyStoreSecret'] = $AKVPassword;
|
||||||
|
|
||||||
|
$tableName = "akv_comparison_table";
|
||||||
|
|
||||||
|
// Connect to the AE-enabled database
|
||||||
|
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||||
|
if (!$conn) {
|
||||||
|
$errors = sqlsrv_errors();
|
||||||
|
fatalError("Connection failed while testing good credentials.\n");
|
||||||
|
} else {
|
||||||
|
$columns = array();
|
||||||
|
$insertQuery = "";
|
||||||
|
|
||||||
|
// Generate the INSERT query
|
||||||
|
formulateSetupQuery($tableName, $dataTypes, $columns, $insertQuery);
|
||||||
|
|
||||||
|
$stmt = AE\createTable($conn, $tableName, $columns);
|
||||||
|
if (!$stmt) {
|
||||||
|
fatalError("Failed to create table $tableName\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate all values for insertion - one is encrypted, one is not
|
||||||
|
$testValues = array();
|
||||||
|
for ($n = 0; $n < sizeof($small_values); ++$n) {
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the INSERT query
|
||||||
|
// This is never expected to fail
|
||||||
|
$stmt = sqlsrv_prepare($conn, $insertQuery, $testValues);
|
||||||
|
if ($stmt == false) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
fatalError("sqlsrv_prepare failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the INSERT query
|
||||||
|
// This should not fail since our credentials are correct
|
||||||
|
if (sqlsrv_execute($stmt) == false) {
|
||||||
|
$errors = sqlsrv_errors();
|
||||||
|
fatalError("INSERT query failed with good credentials.\n");
|
||||||
|
} else {
|
||||||
|
// Get the data back and compare encrypted and non-encrypted versions
|
||||||
|
$selectQuery = "SELECT * FROM $tableName";
|
||||||
|
|
||||||
|
$stmt1 = sqlsrv_query($conn, $selectQuery);
|
||||||
|
$data = sqlsrv_fetch_array($stmt1, SQLSRV_FETCH_NUMERIC);
|
||||||
|
|
||||||
|
if (sizeof($data) != 2*sizeof($dataTypes)) {
|
||||||
|
fatalError("Incorrect number of fields returned.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($n = 0; $n < sizeof($data); $n += 2) {
|
||||||
|
if ($data[$n] != $data[$n + 1]) {
|
||||||
|
echo "Failed on field $n: ".$data[$n]." ".$data[$n + 1]."\n";
|
||||||
|
fatalError("AE and non-AE values do not match.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Successful insertion and retrieval with username/password.\n";
|
||||||
|
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
sqlsrv_free_stmt($stmt1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the statement and close the connection
|
||||||
|
sqlsrv_close($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
$connectionOptions['ColumnEncryption'] = "enabled";
|
||||||
|
$connectionOptions['KeyStoreAuthentication'] = "KeyVaultClientSecret";
|
||||||
|
$connectionOptions['KeyStorePrincipalId'] = $AKVClientID;
|
||||||
|
$connectionOptions['KeyStoreSecret'] = $AKVSecret;
|
||||||
|
|
||||||
|
// Connect to the AE-enabled database
|
||||||
|
$conn = sqlsrv_connect($server, $connectionOptions);
|
||||||
|
if (!$conn) {
|
||||||
|
$errors = sqlsrv_errors();
|
||||||
|
fatalError("Connection failed while testing good credentials.\n");
|
||||||
|
} else {
|
||||||
|
$columns = array();
|
||||||
|
$insertQuery = "";
|
||||||
|
|
||||||
|
// Generate the INSERT query
|
||||||
|
formulateSetupQuery($tableName, $dataTypes, $columns, $insertQuery);
|
||||||
|
|
||||||
|
$stmt = AE\createTable($conn, $tableName, $columns);
|
||||||
|
if (!$stmt) {
|
||||||
|
fatalError("Failed to create table $tableName\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicate all values for insertion - one is encrypted, one is not
|
||||||
|
$testValues = array();
|
||||||
|
for ($n = 0; $n < sizeof($small_values); ++$n) {
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
$testValues[] = $small_values[$n];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the INSERT query
|
||||||
|
// This is never expected to fail
|
||||||
|
$stmt = sqlsrv_prepare($conn, $insertQuery, $testValues);
|
||||||
|
if ($stmt == false) {
|
||||||
|
print_r(sqlsrv_errors());
|
||||||
|
fatalError("sqlsrv_prepare failed\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the INSERT query
|
||||||
|
// This should not fail since our credentials are correct
|
||||||
|
if (sqlsrv_execute($stmt) == false) {
|
||||||
|
$errors = sqlsrv_errors();
|
||||||
|
fatalError("INSERT query execution failed with good credentials.\n");
|
||||||
|
} else {
|
||||||
|
// Get the data back and compare encrypted and non-encrypted versions
|
||||||
|
$selectQuery = "SELECT * FROM $tableName";
|
||||||
|
|
||||||
|
$stmt1 = sqlsrv_query($conn, $selectQuery);
|
||||||
|
$data = sqlsrv_fetch_array($stmt1, SQLSRV_FETCH_NUMERIC);
|
||||||
|
|
||||||
|
if (sizeof($data) != 2*sizeof($dataTypes)) {
|
||||||
|
fatalError("Incorrect number of fields returned.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for ($n = 0; $n < sizeof($data); $n += 2) {
|
||||||
|
if ($data[$n] != $data[$n + 1]) {
|
||||||
|
echo "Failed on field $n: ".$data[$n]." ".$data[$n + 1]."\n";
|
||||||
|
fatalError("AE and non-AE values do not match.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Successful insertion and retrieval with client ID/secret.\n";
|
||||||
|
|
||||||
|
sqlsrv_free_stmt($stmt);
|
||||||
|
sqlsrv_free_stmt($stmt1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free the statement and close the connection
|
||||||
|
sqlsrv_close($conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
--EXPECTREGEX--
|
||||||
|
(Successful insertion and retrieval with username\/password\.\nSuccessful insertion and retrieval with client ID\/secret\.|Skipping test on Ubuntu)
|
|
@ -106,11 +106,7 @@ function testEncryptedWithODBC($msodbcsqlMaj, $server, $connectionOptions)
|
||||||
$connectionOptions['Driver']=$value;
|
$connectionOptions['Driver']=$value;
|
||||||
$connectionOptions['ColumnEncryption']='Enabled';
|
$connectionOptions['ColumnEncryption']='Enabled';
|
||||||
|
|
||||||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
|
||||||
$expected = "The Always Encrypted feature requires Microsoft ODBC Driver 17 for SQL Server.";
|
|
||||||
} else {
|
|
||||||
$expected = "Invalid option ColumnEncryption was passed to sqlsrv_connect.";
|
|
||||||
}
|
|
||||||
|
|
||||||
connectVerifyOutput($server, $connectionOptions, $expected);
|
connectVerifyOutput($server, $connectionOptions, $expected);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
// This file holds different data of many different types for testing
|
||||||
|
// Always Encrypted. Currently, the tests that use this data are:
|
||||||
|
// pdo__ae_azure_key_vault_keywords.phpt ($small_values)
|
||||||
|
// pdo_ae_azure_key_vault_verification.phpt ($small_values)
|
||||||
|
// sqlsrv_ae_fetch_phptypes.phpt ($values)
|
||||||
|
// sqlsrv_ae_azure_key_vault_keywords.phpt ($small_values)
|
||||||
|
// sqlsrv_ae_azure_key_vault_verification.phpt ($small_values)
|
||||||
|
|
||||||
$values = array();
|
$values = array();
|
||||||
$values[] = array(array(("BA3EA123EA8FFF46A01"), null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_BINARY(256)),
|
$values[] = array(array(("BA3EA123EA8FFF46A01"), null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_BINARY(256)),
|
||||||
array(("7BDD1C6794E0BA9556F63B93A"), null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_VARBINARY(256)),
|
array(("7BDD1C6794E0BA9556F63B93A"), null, SQLSRV_PHPTYPE_STRING(SQLSRV_ENC_CHAR), SQLSRV_SQLTYPE_VARBINARY(256)),
|
||||||
|
@ -775,4 +784,16 @@ $values[] = array(array(("C0A0B025C680B0A23D7885F7C203AD211F679679F97F910F0F1A36
|
||||||
"2002-01-31 23:59:59.0498764",
|
"2002-01-31 23:59:59.0498764",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$small_values = array("qwerty",
|
||||||
|
"wertyu",
|
||||||
|
"ϕƆǀđIΩͰDZζ±Áɔd͋ǻĆÅũμ",
|
||||||
|
52.7878,
|
||||||
|
-1.79E+308,
|
||||||
|
-3.4E+38,
|
||||||
|
-987654321987654321,
|
||||||
|
987654321,
|
||||||
|
1,
|
||||||
|
);
|
||||||
|
|
Loading…
Reference in a new issue