Fixed ActiveDirectoryMsi Authentication behavior when specified UID (#1374)

* ActiveDirectoryMsi uid-specified support
This commit is contained in:
SAEKI Yoshiyasu 2022-03-19 06:49:32 +09:00 committed by GitHub
parent ba53591cf5
commit a11822b154
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 49 deletions

View file

@ -74,7 +74,7 @@ void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const DWORD
void configure_azure_key_vault( sqlsrv_conn* conn, BYTE config_attr, const char* 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);
std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver); std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver);
#ifndef _WIN32 #ifndef _WIN32
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver); bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver);
#endif #endif
} }
@ -184,29 +184,29 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
else { else {
// ODBC driver not specified, so check ODBC 17 first then ODBC 18 and/or ODBC 13 // ODBC driver not specified, so check ODBC 17 first then ODBC 18 and/or ODBC 13
// If column encryption is enabled, check up to ODBC 18 // If column encryption is enabled, check up to ODBC 18
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 }; ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
ODBC_DRIVER last_version = (conn->ce_option.enabled) ? ODBC_DRIVER::VER_18 : ODBC_DRIVER::VER_13; ODBC_DRIVER last_version = (conn->ce_option.enabled) ? ODBC_DRIVER::VER_18 : ODBC_DRIVER::VER_13;
ODBC_DRIVER version = ODBC_DRIVER::VER_UNKNOWN; ODBC_DRIVER version = ODBC_DRIVER::VER_UNKNOWN;
for (auto &d : drivers) { for (auto &d : drivers) {
std::string driver_name = get_ODBC_driver_name(d); std::string driver_name = get_ODBC_driver_name(d);
#ifndef _WIN32 #ifndef _WIN32
if (core_search_odbc_driver_unix(d)) { if (core_search_odbc_driver_unix(d)) {
// now append the driver name to the connection string // now append the driver name to the connection string
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str); common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str);
r = core_odbc_connect(conn, conn_str, is_pooled); r = core_odbc_connect(conn, conn_str, is_pooled);
break; break;
} }
#else #else
std::string conn_str_driver = conn_str; // use a copy of conn_str instead std::string conn_str_driver = conn_str; // use a copy of conn_str instead
common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str_driver); common_conn_str_append_func(ODBCConnOptions::Driver, driver_name.c_str(), driver_name.length(), conn_str_driver);
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
break; break;
} }
#endif #endif
else if (d == last_version) { else if (d == last_version) {
// if column encryption is enabled, throw the exception related to column encryption // if column encryption is enabled, throw the exception related to column encryption
CHECK_CUSTOM_ERROR(conn->ce_option.enabled, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) { CHECK_CUSTOM_ERROR(conn->ce_option.enabled, conn, SQLSRV_ERROR_CE_DRIVER_REQUIRED, get_processor_arch()) {
throw core::CoreException(); throw core::CoreException();
@ -216,7 +216,7 @@ sqlsrv_conn* core_sqlsrv_connect( _In_ sqlsrv_context& henv_cp, _In_ sqlsrv_cont
CHECK_CUSTOM_ERROR(true, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) { CHECK_CUSTOM_ERROR(true, conn, SQLSRV_ERROR_DRIVER_NOT_INSTALLED, get_processor_arch()) {
throw core::CoreException(); throw core::CoreException();
} }
} }
} }
} }
@ -676,8 +676,8 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
try { try {
// Since connection options access token and authentication cannot coexist, check if both of them are used. // Since connection options access token and authentication cannot coexist, check if both of them are used.
// If access token is specified, check UID and PWD as well. // If access token is specified, check UID and PWD as well.
// No need to check the keyword Trusted_Connection because it is not among the acceptable options for SQLSRV drivers // No need to check the keyword Trusted_Connection because it is not among the acceptable options for SQLSRV drivers
if (zend_hash_index_exists(options, SQLSRV_CONN_OPTION_ACCESS_TOKEN)) { if (zend_hash_index_exists(options, SQLSRV_CONN_OPTION_ACCESS_TOKEN)) {
bool invalidOptions = false; bool invalidOptions = false;
@ -715,6 +715,19 @@ void build_connection_string_and_set_conn_attr( _Inout_ sqlsrv_conn* conn, _Inou
// Add the server name // Add the server name
common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string ); common_conn_str_append_func( ODBCConnOptions::SERVER, server, strnlen_s( server ), connection_string );
// Check uid when Authentication is ActiveDirectoryMSI
// uid can be specified when using user-assigned identity
if (activeDirectoryMSI) {
if (uid != NULL && strnlen_s(uid) > 0) {
bool escaped = core_is_conn_opt_value_escaped(uid, strnlen_s(uid));
CHECK_CUSTOM_ERROR(!escaped, conn, SQLSRV_ERROR_UID_PWD_BRACES_NOT_ESCAPED) {
throw core::CoreException();
}
common_conn_str_append_func(ODBCConnOptions::UID, uid, strnlen_s(uid), connection_string);
}
}
// If uid is not present then we use trusted connection -- but not when connecting // If uid is not present then we use trusted connection -- but not when connecting
// using the access token or Authentication is ActiveDirectoryMSI // using the access token or Authentication is ActiveDirectoryMSI
if (!access_token_used && !activeDirectoryMSI) { if (!access_token_used && !activeDirectoryMSI) {
@ -963,29 +976,29 @@ std::string get_ODBC_driver_name(_In_ ODBC_DRIVER driver)
// Parameters: // Parameters:
// driver - a valid value in enum ODBC_DRIVER // driver - a valid value in enum ODBC_DRIVER
// Return - a boolean flag that indicates if the specified driver version is found or not // Return - a boolean flag that indicates if the specified driver version is found or not
bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver) bool core_search_odbc_driver_unix(_In_ ODBC_DRIVER driver)
{ {
char szBuf[DEFAULT_CONN_STR_LEN + 1] = { '\0' }; // use a large enough buffer size char szBuf[DEFAULT_CONN_STR_LEN + 1] = { '\0' }; // 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;
// get all the names of the installed drivers delimited by null characters // get all the names of the installed drivers delimited by null characters
if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut)) if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut))
return false; return false;
// search for the derived ODBC driver name based on the given version // search for the derived ODBC driver name based on the given version
std::string driver_name = get_ODBC_driver_name(driver); std::string driver_name = get_ODBC_driver_name(driver);
do do
{ {
if (strstr(pszBuf, driver_name.c_str()) != 0) if (strstr(pszBuf, driver_name.c_str()) != 0)
return true; return true;
// get the next driver // get the next driver
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
return false; return false;
} }
#endif // !_WIN32 #endif // !_WIN32
@ -1018,15 +1031,15 @@ void driver_set_func::func(_In_ connection_option const* option, _In_ zval* valu
// Check if the user provided driver_option matches any of the acceptable driver names // Check if the user provided driver_option matches any of the acceptable driver names
std::string driver_option(val_str, val_len); std::string driver_option(val_str, val_len);
ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 }; ODBC_DRIVER drivers[] = { ODBC_DRIVER::VER_17, ODBC_DRIVER::VER_18, ODBC_DRIVER::VER_13 };
conn->driver_version = ODBC_DRIVER::VER_UNKNOWN; conn->driver_version = ODBC_DRIVER::VER_UNKNOWN;
for (auto &d : drivers) { for (auto &d : drivers) {
std::string name = get_ODBC_driver_name(d); std::string name = get_ODBC_driver_name(d);
if (!driver_option.compare(name)) { if (!driver_option.compare(name)) {
conn->driver_version = d; conn->driver_version = d;
break; break;
} }
} }
CHECK_CUSTOM_ERROR(conn->driver_version == ODBC_DRIVER::VER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, Z_STRVAL_P(value)) { CHECK_CUSTOM_ERROR(conn->driver_version == ODBC_DRIVER::VER_UNKNOWN, conn, SQLSRV_ERROR_CONNECT_INVALID_DRIVER, Z_STRVAL_P(value)) {

View file

@ -48,12 +48,44 @@ function connectInvalidServer()
} }
} }
function connectInvalidServerWithUser()
{
global $server, $driver, $uid, $pwd;
try {
$conn = new PDO("sqlsrv:server = $server; driver=$driver;", $uid, $pwd);
$msodbcsqlVer = $conn->getAttribute(PDO::ATTR_CLIENT_VERSION)["DriverVer"];
$version = explode(".", $msodbcsqlVer);
if ($version[0] < 17 || $version[1] < 3) {
//skip the rest of this test, which requires ODBC driver 17.3 or above
return;
}
unset($conn);
// Try connecting to an invalid server, should get an exception from ODBC
$connectionInfo = "Authentication = ActiveDirectoryMsi;";
$user = "user";
$testCase = 'invalidServer';
try {
$conn = new PDO("sqlsrv:server = invalidServer; $connectionInfo", $user, null);
echo $message . $testCase . PHP_EOL;
} catch(PDOException $e) {
// TODO: check the exception message here
}
} catch(PDOException $e) {
print_r($e->getMessage());
}
}
require_once('MsSetup.inc'); require_once('MsSetup.inc');
// Make a connection to an invalid server // Make a connection to an invalid server
connectInvalidServer(); connectInvalidServer();
connectInvalidServerWithUser();
echo "Done\n"; echo "Done\n";
?> ?>
--EXPECT-- --EXPECT--
Done Done