Skip to content

Commit d5dcf9b

Browse files
authored
Merge pull request #384 from yitam/azure-ad
Azure AD implementation - preview
2 parents ae0368f + 368b658 commit d5dcf9b

13 files changed

Lines changed: 387 additions & 160 deletions

appveyor.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,9 @@ install:
114114
- ps: (new-object net.webclient).DownloadFile('http://windows.php.net/downloads/releases/php-' + ${env:PHP_VERSION} + '-src.zip', ${env:APPVEYOR_BUILD_FOLDER} + '\..\php.zip')
115115
#- echo Downloading PHP deps [%PHP_DEPSVER%]
116116
#- ps: (new-object net.webclient).DownloadFile('http://windows.php.net/downloads/php-sdk/deps-' + ${env:PHP_DEPSVER} + '-vc' + ${env:PHP_VC} + '-' + ${env:BUILD_PLATFORM} + '.7z', ${env:APPVEYOR_BUILD_FOLDER} + '\..\deps.7z')
117-
- echo Downloading MSODBCSQL 13
118-
- ps: (new-object net.webclient).DownloadFile('https://download.microsoft.com/download/1/E/7/1E7B1181-3974-4B29-9A47-CC857B271AA2/English/' + ${env:BUILD_PLATFORM} + '/msodbcsql.msi', 'msodbcsql.msi')
119-
- ps: msiexec /i msodbcsql.msi /quiet /qn /norestart
117+
- echo Downloading MSODBCSQL 13.1
118+
- ps: (new-object net.webclient).DownloadFile('https://download.microsoft.com/download/D/5/E/D5EEF288-A277-45C8-855B-8E2CB7E25B96/' + ${env:BUILD_PLATFORM} + '/msodbcsql.msi', 'msodbcsql.msi')
119+
- ps: msiexec /i msodbcsql.msi /quiet /qn
120120
- cd ..
121121
- cd
122122
- 7z x -y php-sdk-binary-tools-20110915.zip -o%PHP_SDK%

source/pdo_sqlsrv/pdo_dbh.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ const char APP[] = "APP";
4141
const char ApplicationIntent[] = "ApplicationIntent";
4242
const char AttachDBFileName[] = "AttachDbFileName";
4343
const char ConnectionPooling[] = "ConnectionPooling";
44+
const char Authentication[] = "Authentication";
4445
#ifdef _WIN32
4546
const char ConnectRetryCount[] = "ConnectRetryCount";
4647
const char ConnectRetryInterval[] = "ConnectRetryInterval";
@@ -200,6 +201,15 @@ const connection_option PDO_CONN_OPTS[] = {
200201
CONN_ATTR_STRING,
201202
conn_str_append_func::func
202203
},
204+
{
205+
PDOConnOptionNames::Authentication,
206+
sizeof( PDOConnOptionNames::Authentication ),
207+
SQLSRV_CONN_OPTION_AUTHENTICATION,
208+
ODBCConnOptions::Authentication,
209+
sizeof( ODBCConnOptions::Authentication ),
210+
CONN_ATTR_STRING,
211+
conn_str_append_func::func
212+
},
203213
{
204214
PDOConnOptionNames::ConnectionPooling,
205215
sizeof( PDOConnOptionNames::ConnectionPooling ),

source/pdo_sqlsrv/pdo_parser.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ void string_parser::add_key_value_pair( const char* value, int len TSRMLS_DC )
139139
void sql_string_parser::add_key_int_value_pair( unsigned int value TSRMLS_DC ) {
140140
zval value_z;
141141
ZVAL_LONG( &value_z, value );
142-
142+
143143
core::sqlsrv_zend_hash_index_update( *ctx, this->element_ht, this->current_key, &value_z TSRMLS_CC );
144144
}
145145

@@ -169,6 +169,31 @@ void conn_string_parser::validate_key(const char *key, int key_len TSRMLS_DC )
169169
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_DSN_KEY, static_cast<char*>( key_name ) );
170170
}
171171

172+
void conn_string_parser::add_key_value_pair( const char* value, int len TSRMLS_DC )
173+
{
174+
// if the keyword is 'Authentication', check whether the user specified option is supported
175+
bool valid = true;
176+
if ( stricmp( this->current_key_name, ODBCConnOptions::Authentication ) == 0 ) {
177+
if (len <= 0)
178+
valid = false;
179+
else {
180+
// extract option from the value by len
181+
sqlsrv_malloc_auto_ptr<char> option;
182+
option = static_cast<char*>( sqlsrv_malloc( len + 1 ) );
183+
memcpy_s( option, len + 1, value, len );
184+
option[len] = '\0';
185+
186+
valid = core_is_authentication_option_valid( option, len );
187+
}
188+
}
189+
if( !valid ) {
190+
THROW_PDO_ERROR( this->ctx, PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION, this->current_key_name );
191+
}
192+
193+
string_parser::add_key_value_pair( value, len );
194+
}
195+
196+
172197
inline bool sql_string_parser::is_placeholder_char( char c )
173198
{
174199
// placeholder only accepts numbers, upper and lower case alphabets and underscore
@@ -411,7 +436,7 @@ void sql_string_parser::parse_sql_string( TSRMLS_D ) {
411436
start_pos = this->pos;
412437
next();
413438
// keep going until the next space or line break
414-
// while (!is_white_space(this->orig_str[pos]) && !this->is_eos()) {
439+
// while (!is_white_space(this->orig_str[pos]) && !this->is_eos()) {
415440
while ( is_placeholder_char( this->orig_str[pos] )) {
416441
next();
417442
}

source/pdo_sqlsrv/pdo_util.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ pdo_error PDO_ERRORS[] = {
377377
PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED,
378378
{ IMSSP, (SQLCHAR*) "Statement with emulate prepare on does not support output or input_output parameters.", -72, false }
379379
},
380+
{
381+
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION,
382+
{ IMSSP, (SQLCHAR*) "Invalid option for the Authentication keyword. Only SqlPassword or ActiveDirectoryPassword is supported.", -73, false }
383+
},
380384
{ UINT_MAX, {} }
381385
};
382386

@@ -558,15 +562,15 @@ namespace {
558562
// Place get_error_message into the anonymous namespace in pdo_util.cpp
559563
sqlsrv_error_const* get_error_message(unsigned int sqlsrv_error_code) {
560564

561-
sqlsrv_error_const *error_message = NULL;
562-
int zr = (error_message = reinterpret_cast<sqlsrv_error_const*>(zend_hash_index_find_ptr(g_pdo_errors_ht, sqlsrv_error_code))) != NULL ? SUCCESS : FAILURE;
563-
if (zr == FAILURE) {
564-
DIE("get_error_message: zend_hash_index_find returned failure for sqlsrv_error_code = %1!d!", sqlsrv_error_code);
565-
}
565+
sqlsrv_error_const *error_message = NULL;
566+
int zr = (error_message = reinterpret_cast<sqlsrv_error_const*>(zend_hash_index_find_ptr(g_pdo_errors_ht, sqlsrv_error_code))) != NULL ? SUCCESS : FAILURE;
567+
if (zr == FAILURE) {
568+
DIE("get_error_message: zend_hash_index_find returned failure for sqlsrv_error_code = %1!d!", sqlsrv_error_code);
569+
}
566570

567-
SQLSRV_ASSERT(error_message != NULL, "get_error_message: error_message was null");
571+
SQLSRV_ASSERT(error_message != NULL, "get_error_message: error_message was null");
568572

569-
return error_message;
573+
return error_message;
570574
}
571575

572576
void pdo_sqlsrv_throw_exception( sqlsrv_error_const* error TSRMLS_DC )

source/pdo_sqlsrv/php_pdo_sqlsrv.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ class conn_string_parser : private string_parser
165165
int discard_trailing_white_spaces(const char* str, int len);
166166
void validate_key(const char *key, int key_len TSRMLS_DC);
167167

168+
protected:
169+
void add_key_value_pair(const char* value, int len TSRMLS_DC);
170+
168171
public:
169172
conn_string_parser( sqlsrv_context& ctx, const char* dsn, int len, _Inout_ HashTable* conn_options_ht );
170173
void parse_conn_string( TSRMLS_D );
@@ -390,6 +393,7 @@ enum PDO_ERROR_CODES {
390393
PDO_SQLSRV_ERROR_INVALID_OUTPUT_PARAM_TYPE,
391394
PDO_SQLSRV_ERROR_INVALID_CURSOR_WITH_SCROLL_TYPE,
392395
PDO_SQLSRV_ERROR_EMULATE_INOUT_UNSUPPORTED,
396+
PDO_SQLSRV_ERROR_INVALID_AUTHENTICATION_OPTION
393397
};
394398

395399
extern pdo_error PDO_ERRORS[];

source/shared/core_conn.cpp

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ void common_conn_str_append_func( const char* odbc_name, const char* val, size_t
9292
sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_ncp, driver_conn_factory conn_factory,
9393
const char* server, const char* uid, const char* pwd,
9494
HashTable* options_ht, error_callback err, const connection_option valid_conn_opts[],
95-
void* driver, const char* driver_func TSRMLS_DC )
95+
void* driver, const char* driver_func TSRMLS_DC )
9696

9797
{
9898
SQLRETURN r;
@@ -112,7 +112,7 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
112112
// Due to the limitations on connection pooling in unixODBC 2.3.1 driver manager, we do not consider
113113
// the connection string attributes to set (enable/disable) connection pooling.
114114
// Instead, MSPHPSQL connection pooling is set according to the ODBCINST.INI file in [ODBC] section.
115-
115+
116116
#ifndef _WIN32
117117
char pooling_string[ 128 ] = {0};
118118
SQLGetPrivateProfileString( "ODBC", "Pooling", "0", pooling_string, sizeof( pooling_string ), "ODBCINST.INI" );
@@ -128,7 +128,7 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
128128
// it in build_connection_string_and_set_conn_attr.
129129

130130
if( options_ht && zend_hash_num_elements( options_ht ) > 0 ) {
131-
131+
132132
zval* option_z = NULL;
133133
option_z = zend_hash_index_find(options_ht, SQLSRV_CONN_OPTION_CONN_POOLING);
134134
if ( option_z ) {
@@ -163,18 +163,18 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
163163

164164
SQLSMALLINT output_conn_size;
165165
#ifndef _WIN32
166-
// unixODBC 2.3.1 requires a non-wide SQLDriverConnect call while pooling enabled.
167-
// connection handle has been allocated using henv_cp, means pooling enabled in a PHP script
168-
if ( henv == &henv_cp )
169-
{
170-
r = SQLDriverConnect( conn->handle(), NULL, (SQLCHAR*)conn_str.c_str(), SQL_NTS, NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
171-
}
172-
else
173-
{
174-
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
166+
// unixODBC 2.3.1 requires a non-wide SQLDriverConnect call while pooling enabled.
167+
// connection handle has been allocated using henv_cp, means pooling enabled in a PHP script
168+
if ( henv == &henv_cp )
169+
{
170+
r = SQLDriverConnect( conn->handle(), NULL, (SQLCHAR*)conn_str.c_str(), SQL_NTS, NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
171+
}
172+
else
173+
{
174+
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
175175
}
176176
#else
177-
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
177+
r = SQLDriverConnectW( conn->handle(), NULL, reinterpret_cast<SQLWCHAR*>( wconn_string.get() ), static_cast<SQLSMALLINT>( wconn_len ), NULL, 0, &output_conn_size, SQL_DRIVER_NOPROMPT );
178178
#endif // !_WIN32
179179

180180
// clear the connection string from memory to remove sensitive data (such as a password).
@@ -215,11 +215,11 @@ sqlsrv_conn* core_sqlsrv_connect( sqlsrv_context& henv_cp, sqlsrv_context& henv_
215215
// but fails if the connection is using a pool, i.e. r= SQL_SUCCESS.
216216
// Thus, in Linux, we don't call determine_server_version() for a connection that uses pool.
217217
#ifndef _WIN32
218-
if ( r == SQL_SUCCESS_WITH_INFO ) {
218+
if ( r == SQL_SUCCESS_WITH_INFO ) {
219219
#endif // !_WIN32
220220
determine_server_version( conn TSRMLS_CC );
221221
#ifndef _WIN32
222-
}
222+
}
223223
#endif // !_WIN32
224224
}
225225
catch( std::bad_alloc& ) {
@@ -550,6 +550,20 @@ bool core_is_conn_opt_value_escaped( const char* value, size_t value_len )
550550
return true;
551551
}
552552

553+
// core_is_authentication_option_valid
554+
// if the option for the authentication is valid, returns true. This returns false otherwise.
555+
bool core_is_authentication_option_valid(const char* value, size_t value_len)
556+
{
557+
if (value_len <= 0)
558+
return false;
559+
560+
if( ! stricmp( value, AzureADOptions::AZURE_AUTH_SQL_PASSWORD ) || ! stricmp( value, AzureADOptions::AZURE_AUTH_AD_PASSWORD ) ) {
561+
return true;
562+
}
563+
564+
return false;
565+
}
566+
553567

554568
// *** internal connection functions and classes ***
555569

@@ -625,33 +639,33 @@ void build_connection_string_and_set_conn_attr( sqlsrv_conn* conn, const char* s
625639
if( zend_hash_index_exists( options, SQLSRV_CONN_OPTION_TRACE_FILE )) {
626640

627641
zval* trace_value = NULL;
628-
trace_value = zend_hash_index_find(options, SQLSRV_CONN_OPTION_TRACE_ON);
642+
trace_value = zend_hash_index_find(options, SQLSRV_CONN_OPTION_TRACE_ON);
629643

630-
if (trace_value == NULL || !zend_is_true(trace_value)) {
644+
if (trace_value == NULL || !zend_is_true(trace_value)) {
631645

632646
zend_hash_index_del( options, SQLSRV_CONN_OPTION_TRACE_FILE );
633647
}
634648
}
635649

636-
zend_string *key = NULL;
637-
zend_ulong index = -1;
638-
zval* data = NULL;
650+
zend_string *key = NULL;
651+
zend_ulong index = -1;
652+
zval* data = NULL;
639653

640-
ZEND_HASH_FOREACH_KEY_VAL( options, index, key, data ) {
641-
int type = HASH_KEY_NON_EXISTENT;
642-
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
654+
ZEND_HASH_FOREACH_KEY_VAL( options, index, key, data ) {
655+
int type = HASH_KEY_NON_EXISTENT;
656+
type = key ? HASH_KEY_IS_STRING : HASH_KEY_IS_LONG;
643657

644-
// The driver layer should ensure a valid key.
645-
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "build_connection_string_and_set_conn_attr: invalid connection option key type." );
658+
// The driver layer should ensure a valid key.
659+
DEBUG_SQLSRV_ASSERT(( type == HASH_KEY_IS_LONG ), "build_connection_string_and_set_conn_attr: invalid connection option key type." );
646660

647-
conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC );
661+
conn_opt = get_connection_option( conn, index, valid_conn_opts TSRMLS_CC );
648662

649-
if( index == SQLSRV_CONN_OPTION_MARS ) {
650-
mars_mentioned = true;
651-
}
663+
if( index == SQLSRV_CONN_OPTION_MARS ) {
664+
mars_mentioned = true;
665+
}
652666

653-
conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC );
654-
} ZEND_HASH_FOREACH_END();
667+
conn_opt->func( conn_opt, data, conn, connection_string TSRMLS_CC );
668+
} ZEND_HASH_FOREACH_END();
655669

656670
// MARS on if not explicitly turned off
657671
if( !mars_mentioned ) {
@@ -707,7 +721,7 @@ const char* get_processor_arch( void )
707721
return PROCESSOR_ARCH[2];
708722
} else {
709723
DIE( "Unknown processor architecture." );
710-
}
724+
}
711725
return NULL;
712726
#else
713727
SYSTEM_INFO sys_info;
@@ -727,7 +741,7 @@ const char* get_processor_arch( void )
727741
DIE( "Unknown Windows processor architecture." );
728742
return NULL;
729743
}
730-
return NULL;
744+
return NULL;
731745
#endif // !_WIN32
732746
}
733747

@@ -747,7 +761,7 @@ void determine_server_version( sqlsrv_conn* conn TSRMLS_DC )
747761
errno = 0;
748762
char version_major_str[ 3 ];
749763
SERVER_VERSION version_major;
750-
memcpy_s( version_major_str, sizeof( version_major_str ), p, 2 );
764+
memcpy_s( version_major_str, sizeof( version_major_str ), p, 2 );
751765

752766
version_major_str[ 2 ] = '\0';
753767
version_major = static_cast<SERVER_VERSION>( atoi( version_major_str ));
@@ -817,7 +831,7 @@ size_t core_str_zval_is_true( zval* value_z )
817831
}
818832

819833
// save adjustments to the value made by stripping whitespace at the end
820-
Z_STRLEN_P( value_z ) = val_len;
834+
Z_STRLEN_P( value_z ) = val_len;
821835

822836
const char VALID_TRUE_VALUE_1[] = "true";
823837
const char VALID_TRUE_VALUE_2[] = "1";

source/shared/core_sqlsrv.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ const int SQL_SERVER_2005_DEFAULT_DATETIME_SCALE = 3;
180180
const int SQL_SERVER_2008_DEFAULT_DATETIME_PRECISION = 34;
181181
const int SQL_SERVER_2008_DEFAULT_DATETIME_SCALE = 7;
182182

183+
namespace AzureADOptions {
184+
const char AZURE_AUTH_SQL_PASSWORD[] = "SqlPassword";
185+
const char AZURE_AUTH_AD_PASSWORD[] = "ActiveDirectoryPassword";
186+
}
187+
183188
// types for conversions on output parameters (though they can be used for input parameters, they are ignored)
184189
enum SQLSRV_PHPTYPE {
185190
MIN_SQLSRV_PHPTYPE = 1, // lowest value for a php type
@@ -1077,6 +1082,7 @@ namespace ODBCConnOptions {
10771082
const char APP[] = "APP";
10781083
const char ApplicationIntent[] = "ApplicationIntent";
10791084
const char AttachDBFileName[] = "AttachDbFileName";
1085+
const char Authentication[] = "Authentication";
10801086
const char CharacterSet[] = "CharacterSet";
10811087
const char ConnectionPooling[] = "ConnectionPooling";
10821088
#ifdef _WIN32
@@ -1121,6 +1127,7 @@ enum SQLSRV_CONN_OPTIONS {
11211127
SQLSRV_CONN_OPTION_ATTACHDBFILENAME,
11221128
SQLSRV_CONN_OPTION_APPLICATION_INTENT,
11231129
SQLSRV_CONN_OPTION_MULTI_SUBNET_FAILOVER,
1130+
SQLSRV_CONN_OPTION_AUTHENTICATION,
11241131
#ifdef _WIN32
11251132
SQLSRV_CONN_OPTION_CONN_RETRY_COUNT,
11261133
SQLSRV_CONN_OPTION_CONN_RETRY_INTERVAL,
@@ -1190,6 +1197,7 @@ void core_sqlsrv_get_server_version( sqlsrv_conn* conn, _Out_ zval *server_versi
11901197
void core_sqlsrv_get_client_info( sqlsrv_conn* conn, _Out_ zval *client_info TSRMLS_DC );
11911198
bool core_is_conn_opt_value_escaped( const char* value, size_t value_len );
11921199
size_t core_str_zval_is_true( zval* str_zval );
1200+
bool core_is_authentication_option_valid( const char* value, size_t value_len );
11931201

11941202
//*********************************************************************************************************************************
11951203
// Statement

source/shared/msodbcsql.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@
9090
#define SQL_COPT_SS_AEKEYSTOREPROVIDER (SQL_COPT_SS_BASE_EX+11) /* Load a keystore provider or read the list of loaded keystore providers */
9191
#define SQL_COPT_SS_AEKEYSTOREDATA (SQL_COPT_SS_BASE_EX+12) /* Communicate with a loaded keystore provider */
9292
#define SQL_COPT_SS_AETRUSTEDCMKPATHS (SQL_COPT_SS_BASE_EX+13) /* List of trusted CMK paths */
93-
#define SQL_COPT_SS_AECEKCACHETTL (SQL_COPT_SS_BASE_EX+14)// Symmetric Key Cache TTL
93+
#define SQL_COPT_SS_AECEKCACHETTL (SQL_COPT_SS_BASE_EX+14) /* Symmetric Key Cache TTL */
94+
#define SQL_COPT_SS_AUTHENTICATION (SQL_COPT_SS_BASE_EX+15) /* The authentication method used for the connection */
9495

9596
/*
9697
* SQLColAttributes driver specific defines.

0 commit comments

Comments
 (0)