Update how PHP drivers handle Encrypt keyword

This commit is contained in:
yitam 2021-12-06 16:40:41 -08:00
parent 1a45ee10e2
commit dad6e4c245
6 changed files with 263 additions and 29 deletions

View file

@ -125,7 +125,7 @@ struct pdo_int_conn_str_func {
static void func( _In_ connection_option const* option, _In_ zval* value, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str )
{
SQLSRV_ASSERT( Z_TYPE_P( value ) == IS_STRING, "Wrong zval type for this keyword" )
SQLSRV_ASSERT(Z_TYPE_P(value) == IS_STRING, "Wrong zval type for this keyword");
std::string val_str = Z_STRVAL_P( value );
@ -136,6 +136,42 @@ struct pdo_int_conn_str_func {
}
};
struct pdo_encrypt_set_func
{
static void func(_In_ connection_option const* option, _Inout_ zval* value_z, sqlsrv_conn* /*conn*/, _Out_ std::string& conn_str)
{
SQLSRV_ASSERT(Z_TYPE_P(value_z) == IS_STRING, "Wrong zval type for this keyword");
std::string val_str = Z_STRVAL_P(value_z);
std::string whitespaces(" \t\f\v\n\r");
// Trim white spaces
std::size_t found = val_str.find_last_not_of(whitespaces);
if (found != std::string::npos)
val_str.erase(found + 1);
const char TRUE_VALUE_1[] = "true";
const char TRUE_VALUE_2[] = "1";
const char FALSE_VALUE_1[] = "false";
const char FALSE_VALUE_2[] = "0";
// For backward compatibility, convert true/1 to yes and false/0 to no
std::string attr;
if (!val_str.compare(TRUE_VALUE_1) || !val_str.compare(TRUE_VALUE_2)) {
attr = "yes";
} else if (!val_str.compare(FALSE_VALUE_1) || !val_str.compare(FALSE_VALUE_2)) {
attr = "no";
} else {
// simply pass the attribute value to ODBC driver
attr = val_str;
}
conn_str += option->odbc_name;
conn_str += "={";
conn_str += attr;
conn_str += "};";
}
};
template <unsigned int Attr>
struct pdo_int_conn_attr_func {
@ -303,8 +339,8 @@ const connection_option PDO_CONN_OPTS[] = {
SQLSRV_CONN_OPTION_ENCRYPT,
ODBCConnOptions::Encrypt,
sizeof( ODBCConnOptions::Encrypt ),
CONN_ATTR_BOOL,
pdo_bool_conn_str_func::func
CONN_ATTR_MIXED,
pdo_encrypt_set_func::func
},
{
PDOConnOptionNames::Failover_Partner,

View file

@ -1124,34 +1124,22 @@ void ce_akv_str_set_func::func(_In_ connection_option const* option, _In_ zval*
// Values = ("true" or "1") are treated as true values. Everything else is treated as 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." );
std::string val_str = Z_STRVAL_P(value_z);
std::string whitespaces(" \t\f\v\n\r");
char* value_in = Z_STRVAL_P( value_z );
size_t val_len = Z_STRLEN_P( value_z );
// Trim white spaces
std::size_t found = val_str.find_last_not_of(whitespaces);
if (found != std::string::npos)
val_str.erase(found + 1);
// strip any whitespace at the end (whitespace is the same value in ASCII and UTF-8)
size_t last_char = val_len - 1;
while( isspace(( unsigned char )value_in[last_char] )) {
value_in[last_char] = '\0';
val_len = last_char;
--last_char;
const char TRUE_VALUE_1[] = "true";
const char TRUE_VALUE_2[] = "1";
if (!val_str.compare(TRUE_VALUE_1) || !val_str.compare(TRUE_VALUE_2)) {
return 1; // true
}
// save adjustments to the value made by stripping whitespace at the end
Z_STRLEN_P( value_z ) = val_len;
const char VALID_TRUE_VALUE_1[] = "true";
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 )) ||
( val_len == ( sizeof( VALID_TRUE_VALUE_2 ) - 1 ) && !strnicmp( value_in, VALID_TRUE_VALUE_2, val_len ))
) {
return 1; // true
}
return 0; // false
}

View file

@ -1215,6 +1215,7 @@ enum CONN_ATTR_TYPE {
CONN_ATTR_INT,
CONN_ATTR_BOOL,
CONN_ATTR_STRING,
CONN_ATTR_MIXED,
CONN_ATTR_INVALID,
};

View file

@ -55,7 +55,6 @@ struct format_decimals_func
struct decimal_places_func
{
static void func(connection_option const* /*option*/, _In_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/)
{
// first check if the input is an integer
@ -73,6 +72,33 @@ struct decimal_places_func
}
};
struct srv_encrypt_set_func {
static void func(connection_option const* option, _In_ zval* value_z, _Inout_ sqlsrv_conn* conn, std::string& conn_str)
{
std::string attr;
if (Z_TYPE_P(value_z) == IS_LONG) {
long val = Z_LVAL_P(value_z);
if (val == 1) {
attr = "yes";
} else if (val == 0) {
attr = "no";
} else {
attr = std::to_string(val);
}
} else if (Z_TYPE_P(value_z) == IS_TRUE || Z_TYPE_P(value_z) == IS_FALSE) {
attr = zend_is_true(value_z) ? "yes" : "no";
} else {
attr = Z_STRVAL_P(value_z);
}
char temp_str[MAX_CONN_VALSTRING_LEN];
snprintf(temp_str, MAX_CONN_VALSTRING_LEN, "%s={%s};", option->odbc_name, attr.c_str());
conn_str += temp_str;
}
};
struct conn_char_set_func {
static void func( connection_option const* /*option*/, _Inout_ zval* value, _Inout_ sqlsrv_conn* conn, std::string& /*conn_str*/ )
@ -421,8 +447,8 @@ const connection_option SS_CONN_OPTS[] = {
SQLSRV_CONN_OPTION_ENCRYPT,
ODBCConnOptions::Encrypt,
sizeof( ODBCConnOptions::Encrypt ),
CONN_ATTR_BOOL,
bool_conn_str_func::func
CONN_ATTR_MIXED,
srv_encrypt_set_func::func
},
{
SSConnOptionNames::Failover_Partner,
@ -1336,6 +1362,8 @@ int get_conn_option_key( _Inout_ sqlsrv_context& ctx, _In_ zend_string* key, _In
// if we ever introduce a boolean connection option that maps to a string connection
// attribute.
break;
case CONN_ATTR_MIXED:
break;
case CONN_ATTR_INT:
{
CHECK_CUSTOM_ERROR( (Z_TYPE_P( value_z ) != IS_LONG ), ctx, SQLSRV_ERROR_INVALID_OPTION_TYPE_INT,

View file

@ -0,0 +1,102 @@
--TEST--
Test various encrypt attributes
--SKIPIF--
<?php require('skipif.inc');?>
--FILE--
<?php
require_once 'MsSetup.inc';
try {
$encrypt = ' true ';
$trust = 'true ';
$connectionInfo = "Database = $databaseName; Encrypt = $encrypt; TrustServerCertificate = $trust;";
$conn1 = new PDO("sqlsrv:server = $server ; $connectionInfo", $uid, $pwd);
echo 'Test case 1' . PHP_EOL;
} catch (PDOException $e) {
echo 'Failed to connect (test case 1)' . PHP_EOL;
print_r($e->getMessage());
echo PHP_EOL;
}
unset($conn1);
try {
$encrypt = ' 1 ';
$trust = 'true ';
$connectionInfo = "Database = $databaseName; Encrypt = $encrypt; TrustServerCertificate = $trust;";
$conn2 = new PDO("sqlsrv:server = $server ; $connectionInfo", $uid, $pwd);
echo 'Test case 2' . PHP_EOL;
} catch (PDOException $e) {
echo 'Failed to connect (test case 2)' . PHP_EOL;
print_r($e->getMessage());
echo PHP_EOL;
}
unset($conn2);
try {
$encrypt = ' yes ';
$trust = 'true';
$connectionInfo = "Database = $databaseName; Encrypt = $encrypt; TrustServerCertificate = $trust;";
$conn3 = new PDO("sqlsrv:server = $server ; $connectionInfo", $uid, $pwd);
echo 'Test case 3' . PHP_EOL;
} catch (PDOException $e) {
echo 'Failed to connect (test case 3)' . PHP_EOL;
print_r($e->getMessage());
echo PHP_EOL;
}
unset($conn3);
try {
$encrypt = ' 0 ';
$trust = 'false ';
$connectionInfo = "Database = $databaseName; Encrypt = $encrypt; TrustServerCertificate = $trust;";
$conn4 = new PDO("sqlsrv:server = $server ; $connectionInfo", $uid, $pwd);
echo 'Test case 4' . PHP_EOL;
} catch (PDOException $e) {
echo 'Failed to connect (test case 4)' . PHP_EOL;
print_r($e->getMessage());
echo PHP_EOL;
}
unset($conn4);
try {
$encrypt = ' false ';
$trust = 'false ';
$connectionInfo = "Database = $databaseName; Encrypt = $encrypt; TrustServerCertificate = $trust;";
$conn5 = new PDO("sqlsrv:server = $server ; $connectionInfo", $uid, $pwd);
echo 'Test case 5' . PHP_EOL;
} catch (PDOException $e) {
echo 'Failed to connect (test case 5)' . PHP_EOL;
print_r($e->getMessage());
echo PHP_EOL;
}
unset($conn5);
try {
$encrypt = 'no ';
$trust = 'false ';
$connectionInfo = "Database = $databaseName; Encrypt = $encrypt; TrustServerCertificate = $trust;";
$conn6 = new PDO("sqlsrv:server = $server ; $connectionInfo", $uid, $pwd);
echo 'Test case 6' . PHP_EOL;
} catch (PDOException $e) {
echo 'Failed to connect (test case 6)' . PHP_EOL;
print_r($e->getMessage());
echo PHP_EOL;
}
unset($conn6);
echo 'Done' . PHP_EOL;
?>
--EXPECT--
Test case 1
Test case 2
Test case 3
Test case 4
Test case 5
Test case 6
Done

View file

@ -0,0 +1,79 @@
--TEST--
Test various encrypt attributes
--SKIPIF--
<?php require('skipif.inc');?>
--FILE--
<?php
require_once 'MsSetup.inc';
echo 'Test case 1' . PHP_EOL;
$connectionOptions = array('Encrypt' => true, 'TrustServerCertificate' => true);
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_close($conn);
echo 'Test case 2' . PHP_EOL;
$connectionOptions = array('Encrypt' => 1, 'TrustServerCertificate' => true);
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_close($conn);
echo 'Test case 3' . PHP_EOL;
$connectionOptions = array('Encrypt' => "yes", 'TrustServerCertificate' => true);
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_close($conn);
echo 'Test case 4' . PHP_EOL;
$connectionOptions = array('Encrypt' => "no");
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_close($conn);
echo 'Test case 5' . PHP_EOL;
$connectionOptions = array('Encrypt' => false);
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
sqlsrv_close($conn);
echo 'Test case 6' . PHP_EOL;
$connectionOptions = array('Encrypt' => 0);
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn === false) {
die(print_r(sqlsrv_errors(), true));
}
echo 'Test case 7' . PHP_EOL;
$connectionOptions = array('Encrypt' => 3);
$conn = sqlsrv_connect($server, $connectionOptions);
if ($conn !== false) {
echo 'Expect this to fail' . PHP_EOL;
}
echo 'Done' . PHP_EOL;
?>
--EXPECT--
Test case 1
Test case 2
Test case 3
Test case 4
Test case 5
Test case 6
Test case 7
Done