Skip to content

Added support to load a custom keystore provider for Column Encryption #511

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Aug 31, 2017
Merged

Conversation

yitam
Copy link
Contributor

@yitam yitam commented Aug 23, 2017

No description provided.

@coveralls
Copy link

Coverage Status

Coverage decreased (-0.7%) to 73.294% when pulling b1f8c2f on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 74.081% when pulling 12e0e69 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 74.081% when pulling 66e1d28 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.1%) to 74.081% when pulling 9153ea4 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 74.111% when pulling aa82648 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

coveralls commented Aug 24, 2017

Coverage Status

Coverage increased (+0.2%) to 74.135% when pulling a1b43a1 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 74.135% when pulling 65f0be9 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@yitam yitam requested review from yukiwongky and Hadis-Fard August 24, 2017 23:12
@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 74.135% when pulling 11edad2 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

coveralls commented Aug 25, 2017

Coverage Status

Coverage increased (+0.2%) to 74.135% when pulling 11edad2 on yitam:KSProvider into 3bdcdd7 on Microsoft:AlwaysEncrypted.

core::SQLSetConnectAttr( conn, SQL_COPT_SS_CEKEYSTOREDATA, reinterpret_cast<SQLPOINTER>( pKsd ), SQL_IS_POINTER );
}

void common_conn_str_append_func( const char* odbc_name, const char* val, size_t val_len, std::string& conn_str TSRMLS_DC )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing SAL annotations for this function - they were previously included here

@@ -776,7 +778,68 @@ void determine_server_version( _Inout_ sqlsrv_conn* conn TSRMLS_DC )
conn->server_version = version_major;
}

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 )
// Column Encryption feature: if a custom keystore provider is specified,
// load and configure it when column encryption is enabled, but this step have
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this step has


ksp_data = reinterpret_cast<unsigned char*>( sqlsrv_malloc( sizeof( CEKEYSTOREDATA ) + key_size ) );

CEKEYSTOREDATA *pKsd = (CEKEYSTOREDATA*) ksp_data.get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ksp_data appears to be some sort of intermediary - why is it needed? Why not just have CEKEYSTOREDATA *pKsd;?

Copy link
Contributor Author

@yitam yitam Aug 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I use sqlsrv_malloc_auto_ptr to take care of releasing the memory afterwards

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. Why use a C-style cast though? Wouldn't a static_cast be preferable?

throw core::CoreException();
}

pKsd->name = (wchar_t *) wksp_name.get();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to cast to wchar_t? wchar_t is 16 bits on Windows and 32 on Linux, and wksp_name is defined with SQLWCHAR, which should resolve to wchar_t iirc. But if not, can this lead to cross-platform incompatibility?

Copy link
Contributor Author

@yitam yitam Aug 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the keystore definitions in msodbcsql.h, and they used wchar_t in quite a few places. In Linux/mac, when compiling the KSP source and the app, we use -fshort-wchar


// Next, extract the character string from conn->ce_option.ksp_encrypt_key into encrypt_key
char* encrypt_key = Z_STRVAL_P( conn->ce_option.ksp_encrypt_key );
memcpy_s( pKsd->data, key_size * sizeof( char ) , encrypt_key, key_size );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does key_size account for the null terminator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. The answer is no. Only the length of the key.

sizeof(ODBCConnOptions::CEKeystoreEncryptKey),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is off...

break;
}
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are all three keywords required if the user wants to use a custom keystore?

Copy link
Contributor Author

@yitam yitam Aug 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, as you can see in the helper function load_configure_ksp()

sizeof(ODBCConnOptions::CEKeystoreEncryptKey),
CONN_ATTR_STRING,
ce_ksp_provider_set_func::func
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation is off

if( ! $conn )
{
die( "skip - could not connect during SKIPIF." );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we really want to skip if we can't connect?? Seems like this should be an error...

Copy link
Contributor Author

@yitam yitam Aug 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got the idea from skipif_protocol_not_tcp.inc :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmmm I agree with David too. If somehow it can't connect, the test should fail instead of being skipped. Should fix this in skipif_protocol_not_tcp.inc too (but maybe in a separate PR to the dev branch)

}
while (pKsp = *ppKsp++) {
if (!memcmp(KSPNAME, pKsp->Name, sizeof(KSPNAME)))
goto FoundProv;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

goto? really?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I basically got this from the sample code, but of course I can avoid using goto in this case.

if platform.system() == 'Windows':
command = 'cl /MD {0}.c /link odbc32.lib /out:ksp_app.exe'.format(app_name)
batchfile = 'build_app.bat'
create_batch_file('x86', batchfile, command)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to write the commands to a batch file and then executing the whole thing? Why can't you just run the command one by one in the script?

Copy link
Contributor Author

@yitam yitam Aug 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

because I need to invoke vcvarsall.bat first with the appropriate option (for x64 or x86) in order to use the c compiler cl.exe

def configure_KSP(app_name):
print('Compiling ', app_name)
if platform.system() == 'Windows':
command = 'cl /MD {0}.c /link odbc32.lib /out:ksp_app.exe'.format(app_name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not a big deal, but would be nice if the order of these arguments are consistent with the ones in compile_KSP_windows

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed the ODBC sample codes on how to compile the source code. Besides, one is to compile the source into a DLL and the other is to create an *.exe, so the switches are different.

os.system(batchfile)
os.remove(batchfile)
else:
os.system('gcc -o ksp_app -fshort-wchar {0}.c -lodbc -ldl'.format(app_name))
Copy link
Contributor

@yukiwongky yukiwongky Aug 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make the ordering of the arguments consistent with the ones in conpile_KSP, but make sure -lodbc comes after the c file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again, I basically followed the examples given in the ODBC sample code

args = parser.parse_args()

ksp_name = args.KSPNAME
app_name = args.APPNAME
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these variable names are a little confusing. These variables correspond to the name of the C file that is used to compile, but the variable names sound like they are the names of the dlls or executables. Maybe rename it to something like ksp_src_name and app_src_name.

Another not major thing at all is you may want to consider making the names to args.KSPNAME + '.c' so you don't need to append the '.c' everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the arguments to be KSPSRC and APPSRC respectively

# ksp_app.c (or any equivalent)
# msodbcsql.h (odbc header file)
#
# Execution: Run with command line with required options
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional options?

if (task == set_up) {
printf("Setting up KSP...\n");
setKSPLibrary(stmt);
populateTestTable(dbc, stmt);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why we're creating and populating a table here? wouldn't it be better to make and populate the tables on fly in the tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, but as of today I can only insert data into the test table with encryption using sqlsrv...
For now the KSP tests show that they can retrieve data from a pre-populated table with encryption

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hmmm this may change very soon though once the ODBC driver is ready. We shall see

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No we will keep using a test table because we want to show that we can retrieve data encrypted by a 3rd party (here in this case it's odbc )

SQLExecDirect(stmt, "DROP TABLE CustomKSPTestTable", SQL_NTS);
SQLExecDirect(stmt, "DROP COLUMN ENCRYPTION KEY CustomCEK", SQL_NTS);
SQLExecDirect(stmt, "DROP COLUMN MASTER KEY CustomCMK", SQL_NTS);
printf("Removed table, CEK, and CMK\n");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the database is dropped at the end, don't need this part

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I agree but I provided this as a handy way to remove the KSP related entities in case the user doesn't want to drop the entire database

if ($version < 13)
{
die( "skip - feature not supported in this version of SQL Server." );
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we not check for ODBC version too? at least exclude 11 and lower.

.travis.yml Outdated
- PDOSQLSRV_DBNAME=msphpsql_pdosqlsrv
- PDOSQLSRV_DBNAME=msphpsql_pdosqlsrv
- TEST_PHP_SQL_UID=sa
- TEST_PHP_SQL_PWD=Password12@

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yml file should be synced with dev branch

@@ -74,9 +79,6 @@ RUN sed -i -e 's/TARGET_DATABASE/msphpsql_sqlsrv/g' MsSetup.inc
RUN sed -i -e 's/TARGET_USERNAME/'"$TEST_PHP_SQL_UID"'/g' MsSetup.inc
RUN sed -i -e 's/TARGET_PASSWORD/'"$TEST_PHP_SQL_PWD"'/g' MsSetup.inc

WORKDIR $PHPSQLDIR
RUN chmod +x ./entrypoint.sh
CMD /bin/bash ./entrypoint.sh

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the deleted lines are actually the latest dev branch, Docker file should be the latest on dev

@coveralls
Copy link

Coverage Status

Coverage decreased (-48.8%) to 25.227% when pulling 23af044 on yitam:KSProvider into 2b70355 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 74.233% when pulling c96090d on yitam:KSProvider into 2b70355 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 74.233% when pulling 39bb678 on yitam:KSProvider into 2b70355 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.2%) to 74.233% when pulling 39bb678 on yitam:KSProvider into 2b70355 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.4%) to 74.385% when pulling 69b5599 on yitam:KSProvider into 2b70355 on Microsoft:AlwaysEncrypted.

@coveralls
Copy link

Coverage Status

Coverage increased (+0.4%) to 74.385% when pulling d0781c0 on yitam:KSProvider into 2b70355 on Microsoft:AlwaysEncrypted.

@yitam yitam merged commit 9675958 into microsoft:AlwaysEncrypted Aug 31, 2017
@yitam yitam deleted the KSProvider branch August 31, 2017 19:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants