diff --git a/.github/composite-actions/build-modified-postgres/action.yml b/.github/composite-actions/build-modified-postgres/action.yml index 06baec2d30..7168cad269 100644 --- a/.github/composite-actions/build-modified-postgres/action.yml +++ b/.github/composite-actions/build-modified-postgres/action.yml @@ -42,7 +42,7 @@ runs: if [[ ${{inputs.tap_tests}} == "yes" ]]; then ./configure CC='ccache gcc' --prefix=$HOME/${{ inputs.install_dir }}/ --with-python PYTHON=/usr/bin/python3.8 --enable-cassert CFLAGS="-ggdb" --with-libxml --with-uuid=ossp --with-icu --enable-tap-tests --with-gssapi else - ./configure CC='ccache gcc' --prefix=$HOME/${{ inputs.install_dir }}/ --with-python PYTHON=/usr/bin/python3.8 --enable-cassert CFLAGS="-ggdb" --with-libxml --with-uuid=ossp --with-icu + ./configure CC='ccache gcc' --prefix=$HOME/${{ inputs.install_dir }}/ --with-python PYTHON=/usr/bin/python3.8 --enable-cassert CFLAGS="-ggdb" --with-libxml --with-uuid=ossp --with-icu --with-openssl fi make -j 4 2>error.txt make install diff --git a/contrib/babelfishpg_tds/src/backend/tds/tdssecure.c b/contrib/babelfishpg_tds/src/backend/tds/tdssecure.c index 6b3c0307f8..e952d04806 100644 --- a/contrib/babelfishpg_tds/src/backend/tds/tdssecure.c +++ b/contrib/babelfishpg_tds/src/backend/tds/tdssecure.c @@ -40,6 +40,10 @@ int tds_ssl_min_protocol_version; int tds_ssl_max_protocol_version; + +static TdsSecureSocketApi tds_secure_raw_read = secure_raw_read; +static TdsSecureSocketApiConst tds_secure_raw_write = secure_raw_write; + #ifdef USE_SSL /* @@ -61,6 +65,8 @@ typedef union TDSPacketHeader { static int pkt_bytes_read = 0; static TDSPacketHeader pkt_data = {0}; +static bool unit_testing = false; + /* * SslRead - TDS secure read function, similar to my_sock_read */ @@ -71,7 +77,7 @@ SslRead(BIO * h, char *buf, int size) if (buf != NULL) { - res = secure_raw_read(((Port *) BIO_get_data(h)), buf, size); + res = tds_secure_raw_read(((Port *) BIO_get_data(h)), buf, size); BIO_clear_retry_flags(h); if (res <= 0) { @@ -126,7 +132,7 @@ SslHandShakeRead(BIO * h, char *buf, int size) } if (unlikely(pkt_data.header.pkt_type != TDS_PRELOGIN)) - ereport(FATAL, + ereport(unit_testing ? ERROR : FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating connection due to unexpected ssl packet header"))); @@ -163,6 +169,35 @@ SslHandShakeRead(BIO * h, char *buf, int size) return res; } + +ssize_t +test_ssl_handshake_read(BIO * h, char *buf, int size, TdsSecureSocketApi mock_socket_read, int ReadPointer) +{ + + /* + * Accessing SslHandShakeRead() function directly from our testing functions created in the test_ssl_read.c file is not possible due to its static nature + * This intermediary function serves as a gateway, allowing us to invoke the SslHandShakeRead() function from our testing function + */ + + int res; + tds_secure_raw_read = mock_socket_read; + unit_testing = true; + pkt_bytes_read = ReadPointer; + PG_TRY(); + { + res = SslHandShakeRead(h, buf, size); + } + PG_FINALLY(); + { + unit_testing = false; + tds_secure_raw_read = secure_raw_read; + } + PG_END_TRY(); + + return res; + } + + /* * SslWrite - Tds secure write function, similar to my_sock_write. */ @@ -171,7 +206,7 @@ SslWrite(BIO * h, const char *buf, int size) { int res = 0; - res = secure_raw_write(((Port *) BIO_get_data(h)), buf, size); + res = tds_secure_raw_write(((Port *) BIO_get_data(h)), buf, size); BIO_clear_retry_flags(h); if (res <= 0) { @@ -245,6 +280,34 @@ SslHandShakeWrite(BIO * h, const char *buf, int size) return (res - TDS_PACKET_HEADER_SIZE); } + +ssize_t +test_ssl_handshake_write(BIO * h, char *buf, int size, TdsSecureSocketApiConst mock_socket_write) +{ + + /* + * Accessing SslHandShakeWrite() function directly from our testing functions created in the test_ssl_write.c file is not possible due to its static nature + * This intermediary function serves as a gateway, allowing us to invoke the SslHandShakeWrite() function from our testing function + */ + + int res; + tds_secure_raw_write = mock_socket_write; + unit_testing = true; + PG_TRY(); + { + res = SslHandShakeWrite(h, buf, size); + } + PG_FINALLY(); + { + unit_testing = false; + tds_secure_raw_write = secure_raw_write; + } + PG_END_TRY(); + + return res; +} + + /* * TdsBioSecureSocket - Similar to my_BIO_s_socket * Used to setup, TDS listener read and write API @@ -337,7 +400,7 @@ tds_secure_read(Port *port, void *ptr, size_t len) else #endif { - n = secure_raw_read(port, ptr, len); + n = tds_secure_raw_read(port, ptr, len); waitfor = WL_SOCKET_READABLE; } @@ -422,7 +485,7 @@ tds_secure_write(Port *port, void *ptr, size_t len) else #endif { - n = secure_raw_write(port, ptr, len); + n = tds_secure_raw_write(port, ptr, len); waitfor = WL_SOCKET_WRITEABLE; } diff --git a/contrib/babelfishpg_tds/src/include/tds_secure.h b/contrib/babelfishpg_tds/src/include/tds_secure.h index 0ec721b57c..11c114d5d6 100644 --- a/contrib/babelfishpg_tds/src/include/tds_secure.h +++ b/contrib/babelfishpg_tds/src/include/tds_secure.h @@ -38,6 +38,9 @@ #endif #endif +typedef ssize_t (*TdsSecureSocketApi) (Port *port, void *ptr, size_t len); +typedef ssize_t (*TdsSecureSocketApiConst) (Port *port, const void *ptr, size_t len); + BIO_METHOD *TdsBioSecureSocket(BIO_METHOD * my_bio_methods); extern int tds_ssl_min_protocol_version; @@ -60,3 +63,6 @@ ssize_t /* function defined in tdssecure.c and called from tdslogin.c */ void TdsFreeSslStruct(Port *port); + +extern ssize_t test_ssl_handshake_read(BIO * h, char *buf, int size, TdsSecureSocketApi mock_socket_read, int ReadPointer); +extern ssize_t test_ssl_handshake_write(BIO * h, char *buf, int size, TdsSecureSocketApiConst mock_socket_write); diff --git a/contrib/babelfishpg_unit/Makefile b/contrib/babelfishpg_unit/Makefile index 214f50ed4d..a898c06e9c 100644 --- a/contrib/babelfishpg_unit/Makefile +++ b/contrib/babelfishpg_unit/Makefile @@ -5,6 +5,7 @@ OBJS = $(SRCS:.c=.o) # object files # source code files SRCS = babelfishpg_unit.c test_money.c \ + test_ssl_read.c test_ssl_write.c # for posgres build PG_CONFIG = pg_config diff --git a/contrib/babelfishpg_unit/babelfishpg_unit.c b/contrib/babelfishpg_unit/babelfishpg_unit.c index 6be1192628..8144401111 100644 --- a/contrib/babelfishpg_unit/babelfishpg_unit.c +++ b/contrib/babelfishpg_unit/babelfishpg_unit.c @@ -77,6 +77,15 @@ TestInfo tests[]= {&test_fixeddecimal_int2_gt, true, "GreaterThanCheck_FIXEDDECIMAL_INT2", "babelfish_money_datatype"}, {&test_fixeddecimal_int2_ne, true, "NotEqualToCheck_FIXEDDECIMAL_INT2", "babelfish_money_datatype"}, {&test_fixeddecimal_int2_cmp, true, "Comparison_FIXEDDECIMAL_INT2", "babelfish_money_datatype"}, + +#ifdef USE_SSL + {&test_ssl_handshakeRead, true, "Testing_SSL_HANDSHAKE_READ", "babelfishpg_tds_ssl_read"}, + {&test_ssl_handshakeRead_oversize, true, "TestingOverSize_SSL_HANDSHAKE_READ", "babelfishpg_tds_ssl_read"}, + {&test_ssl_handshakeRead_pkt_type, true, "TestingPacketType_SSL_HANDSHAKE_READ", "babelfishpg_tds_ssl_read"}, + + {&test_ssl_handshakeWrite, true, "Testing_SSL_HANDSHAKE_WRITE", "babelfishpg_tds_ssl_write"}, + {&test_ssl_handshakeWrite_sizeCheck, true, "TestingSizeCheck_SSL_HANDSHAKE_WRITE", "babelfishpg_tds_ssl_write"}, +#endif }; diff --git a/contrib/babelfishpg_unit/babelfishpg_unit.h b/contrib/babelfishpg_unit/babelfishpg_unit.h index 2bcf272fdb..6f84ab9863 100644 --- a/contrib/babelfishpg_unit/babelfishpg_unit.h +++ b/contrib/babelfishpg_unit/babelfishpg_unit.h @@ -57,3 +57,12 @@ extern TestResult *test_fixeddecimal_int2_le(void); extern TestResult *test_fixeddecimal_int2_gt(void); extern TestResult *test_fixeddecimal_int2_ne(void); extern TestResult *test_fixeddecimal_int2_cmp(void); + +#ifdef USE_SSL +extern TestResult *test_ssl_handshakeRead(void); +extern TestResult *test_ssl_handshakeRead_pkt_type(void); +extern TestResult *test_ssl_handshakeRead_oversize(void); + +extern TestResult *test_ssl_handshakeWrite(void); +extern TestResult *test_ssl_handshakeWrite_sizeCheck(void); +#endif diff --git a/contrib/babelfishpg_unit/test_ssl_read.c b/contrib/babelfishpg_unit/test_ssl_read.c new file mode 100644 index 0000000000..f933644c70 --- /dev/null +++ b/contrib/babelfishpg_unit/test_ssl_read.c @@ -0,0 +1,173 @@ +#include "babelfishpg_unit.h" +#include "../babelfishpg_tds/src/include/tds_secure.h" + +#ifdef USE_SSL +static char *prelogin_request; +static int ReadPointer = 0; + + +static ssize_t +mock_socket_read(Port *port, void *ptr, size_t len) +{ + + /* + * Mock function of tds_secure_raw_read() present in tdssecure.c file + */ + + int i; + for (i = ReadPointer; i < ReadPointer + len; i++) + sscanf(&prelogin_request[i * 2], "%2hhx", (unsigned char *)ptr + i); + ReadPointer += len; + return len; +} + + +TestResult* +test_ssl_handshakeRead(void) +{ + + /* + * We will generate a prelogin request message and pass it to the handshake read function for processing + * By comparing the number of bytes read against the expected value and comparing actual bytes, we can verify the accuracy of the read operation + */ + + BIO *h = NULL; + char *buf = NULL; + char *expected_str; + int expected; + int obtained; + + TestResult* testResult = palloc0(sizeof(TestResult)); + testResult->result = true; + + h = BIO_new(BIO_s_mem()); + ReadPointer = 0; + + prelogin_request = strdup("1201000F0000010011A25E4571"); + buf = malloc(strlen(prelogin_request)/2); + expected = strlen(prelogin_request)/2 - 8; + + expected_str = (char *)malloc(strlen(prelogin_request)/2); + for (int i = 0; i < expected; i++) + sscanf(&prelogin_request [i * 2], "%2hhx", (char *)expected_str + i); + + obtained = test_ssl_handshake_read(h, buf, expected, mock_socket_read, ReadPointer); + + expected_str[expected] = '\0'; + buf[obtained] = '\0'; + + /* + * if number of bytes are same then we will compare actual data + */ + if(expected == obtained) + { + TEST_ASSERT_TESTCASE(strcmp(buf, expected_str) == 0, testResult); + } + TEST_ASSERT(strcmp(buf, expected_str) == 0, testResult); + + free(buf); + free(prelogin_request); + free(expected_str); + return testResult; + +} + +TestResult* +test_ssl_handshakeRead_oversize(void) +{ + + /* + * In this scenario, we will send a message size that exceeds the total packet length + * We will examine whether the function effectively detects the oversized message and generates the appropriate response + */ + + BIO *h = NULL; + char *buf = NULL; + + int expected; + int obtained; + + TestResult* testResult = palloc0(sizeof(TestResult)); + testResult->result = true; + + prelogin_request = strdup("1201000B00000100115461A23E"); + buf = malloc(strlen(prelogin_request)/2); + + h = BIO_new(BIO_s_mem()); + ReadPointer = 0; + + expected = strlen(prelogin_request)/2 - 8; + obtained = test_ssl_handshake_read(h, buf, expected, mock_socket_read, ReadPointer); + + TEST_ASSERT_TESTCASE(expected != obtained, testResult); + TEST_ASSERT(expected != obtained, testResult); + if(testResult->result == true) + { + strncpy(testResult->message, "SSL packet expand more than one TDS packet", MAX_TEST_MESSAGE_LENGTH); + } + + free(buf); + free(prelogin_request); + ReadPointer = 0; + + return testResult; + +} + + +TestResult* +test_ssl_handshakeRead_pkt_type(void) +{ + + /* + * We will generate a message other than the prelogin request by modifying the packet type field within the packet. + * We will verify if the function correctly identifies and handles messages other than the prelogin request + * ensuring appropriate error handling and response generation. + */ + + BIO *h = NULL; + char *buf = NULL; + + ErrorData *errorData; + MemoryContext oldcontext; + + TestResult* testResult = palloc0(sizeof(TestResult)); + testResult->result = false; + + prelogin_request = strdup("100100090000010011"); + buf = malloc(strlen(prelogin_request)/2); + + h = BIO_new(BIO_s_mem()); + ReadPointer = 0; + + oldcontext = CurrentMemoryContext; + PG_TRY(); + { + test_ssl_handshake_read(h, buf, 0, mock_socket_read, ReadPointer); + } + PG_CATCH(); + { + MemoryContextSwitchTo(oldcontext); + errorData = CopyErrorData(); + FlushErrorState(); + snprintf(testResult->message, MAX_TEST_MESSAGE_LENGTH, "%s", testResult->message, errorData->message); + testResult->result = true; + FreeErrorData(errorData); + } + PG_END_TRY(); + + // If the error doesn't occurr, then the following message gets displayed + if(testResult->result == false) + { + strncpy(testResult->message, "Error doesn't occur since packet type is PRE_LOGIN", MAX_TEST_MESSAGE_LENGTH); + } + + free(buf); + free(prelogin_request); + ReadPointer = 0; + + return testResult; + +} + +#endif diff --git a/contrib/babelfishpg_unit/test_ssl_write.c b/contrib/babelfishpg_unit/test_ssl_write.c new file mode 100644 index 0000000000..0bb751cb20 --- /dev/null +++ b/contrib/babelfishpg_unit/test_ssl_write.c @@ -0,0 +1,106 @@ +#include "babelfishpg_unit.h" +#include "../babelfishpg_tds/src/include/tds_secure.h" + +#ifdef USE_SSL +static char *prelogin; + +static ssize_t +mock_socket_write(Port *port, const void *ptr, size_t len) +{ + + /* + * Mock function of tds_secure_raw_write() present in tdssecure.c file + */ + + const unsigned char *ptr_u8 = (const unsigned char *) ptr; + signed char *prelogin_ptr = (signed char *) prelogin; + int i; + for (i = 0; i < len; i++) + sscanf((const char *) &ptr_u8[i * 2], "%2hhx", (signed char *) &prelogin_ptr[i]); + return len; +} + + +TestResult* +test_ssl_handshakeWrite(void) +{ + + /* + * We will generate a message and pass it to the handshake write function + * We will be comparing the number of bytes written against the expected value and comparing the actual hexadecimal text + */ + + BIO *h = NULL; + char *buf = "011103"; + char *expected_str; + char *obtained_str; + + int expected; + int obtained; + + TestResult* testResult = palloc0(sizeof(TestResult)); + testResult->result = true; + + h = BIO_new(BIO_s_mem()); + + prelogin = malloc(strlen(buf) + 8); + expected = strlen(buf); + expected_str = (char *)malloc(expected + 1); + strncpy(expected_str, buf, expected); + expected_str[expected] = '\0'; + + obtained = test_ssl_handshake_write(h, buf, expected, mock_socket_write); + + obtained_str = (char *)malloc(obtained + 1); + strncpy(obtained_str, buf, obtained); + obtained_str[obtained] = '\0'; + + /* + * if number of bytes are same then we will compare actual data + */ + if(expected == obtained) + { + TEST_ASSERT_TESTCASE(strcmp(obtained_str, expected_str) == 0, testResult); + } + TEST_ASSERT(strcmp(obtained_str, expected_str) == 0, testResult); + + free(expected_str); + free(obtained_str); + return testResult; + +} + + +TestResult* +test_ssl_handshakeWrite_sizeCheck(void) +{ + + /* + * We will send a message of size less than zero + * We will evaluate whether the function correctly detects and handles this condition. + */ + + BIO *h = NULL; + char *buf = NULL; + + int expected; + int obtained; + + TestResult* testResult = palloc0(sizeof(TestResult)); + testResult->result = true; + + h = BIO_new(BIO_s_mem()); + + expected = -1; + obtained = test_ssl_handshake_write(h, buf, expected, mock_socket_write); + + TEST_ASSERT_TESTCASE(expected == obtained, testResult); + if(testResult->result == true) + { + snprintf(testResult->message, MAX_TEST_MESSAGE_LENGTH, "%s There is nothing to write", testResult->message); + } + + return testResult; +} + +#endif