diff --git a/CMakeLists.txt b/CMakeLists.txt index 66dcaf66..8bdb780c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,7 +23,7 @@ cmake_minimum_required(VERSION 3.10) option(BUILD_LIB "Build the library itself" ON) option(BUILD_SHARED_LIBS "Build using shared libraries" ON) -option(BUILD_DOCS_API "Enable API documentation building" ON) +option(BUILD_DOCS_API "Enable API documentation building" OFF) option(BUILD_DOCS_MAN "Enable manpage building" OFF) option(BUILD_TESTING "Enable building unit tests" ON) option(TEST_MEMCHECK "Enable test runtime memory checking" ON) diff --git a/README.md b/README.md index 2ea40b30..209b757b 100644 --- a/README.md +++ b/README.md @@ -93,25 +93,35 @@ git submodule update --init --recursive This will take about a minute to build and run the unit tests, there should be 100% success. +Note: On earlier versions of CMake (<3.20), `./build.sh check` target may not run correctly. + #### Optional Additional Build Targets + Code Coverage ``` ./build.sh coverage +``` -# To open coverage report in a browser... -xdg-open build/default/coverage/index.html +The output HTML can be opened in a browser using: +``` +xdg-open build/default/coverage-html/index.html ``` Doxygen Documentation ``` ./build.sh prep -DBUILD_DOCS_API=ON ./build.sh docs +``` -# To open in a browser... +The output HTML can be opened in a browser using: +``` xdg-open build/default/docs/api/html/index.html ``` -Note: On earlier versions of CMake (<3.20), `./build.sh check` target may not run correctly. +To check for misspelling in the Doxygen output use the following, substituting the word/phrase you are looking for in the grep command +``` +xmlstarlet tr build/default/docs/api/xml/combine.xslt build/default/docs/api/xml/index.xml | xmlstarlet tr docs/api/spellcheck.xsl | cat -n | grep -E 'bsl' +``` ## Testing with the Mock BPA diff --git a/mock-bpa-test/helpers/runner.py b/mock-bpa-test/helpers/runner.py index b65f83a6..9b1e0fef 100644 --- a/mock-bpa-test/helpers/runner.py +++ b/mock-bpa-test/helpers/runner.py @@ -30,8 +30,8 @@ from typing import List import queue +# Set up logging LOGGER = logging.getLogger(__name__) -''' Logger for this module. ''' def compose_args(args: List[str]) -> List[str]: @@ -145,14 +145,29 @@ def _write_stdin(self, stream): stream.flush() LOGGER.debug('Stopping stdin thread') - def wait_for_line(self, timeout=5): + def wait_for_line(self, timeout:float=5) -> str: + ''' Wait for any received stdout line. + + :param timeout: The total time to wait for this line. + :return The matching line. + :raise TimeoutError: If the line was not seen in time. + ''' try: text = self._stdout_lines.get(timeout=timeout) except queue.Empty: raise TimeoutError('no lines received before timeout') return text - def wait_for_text(self, pattern, timeout=5): + def wait_for_text(self, pattern:str, timeout:float=5) -> str: + ''' Iterate through the received stdout lines until a specific + full matching line is seen. + + :param pattern: The pattern which must match the full line. + Use prefix or suffix ".*" as needed. + :param timeout: The total time to wait for this line. + :return The matching line. + :raise TimeoutError: If the line was not seen in time. + ''' expr = re.compile(pattern) LOGGER.debug('Waiting for pattern "%s" ...', pattern) @@ -170,5 +185,10 @@ def wait_for_text(self, pattern, timeout=5): if expr.match(text) is not None: return text - def send_stdin(self, text): + def send_stdin(self, text:str): + ''' Send an exact line of text to the process stdin. + + :param text: The line to send, which should include a newline + at the endd. + ''' self._stdin_lines.put(text) diff --git a/mock-bpa-test/requirements_tests.py b/mock-bpa-test/requirements_tests.py index 5114a193..83a71029 100644 --- a/mock-bpa-test/requirements_tests.py +++ b/mock-bpa-test/requirements_tests.py @@ -286,14 +286,14 @@ def __init__(self): # Bundle with a BIB targeting primary block input_data=[ [7, 0, 0, [2, [1, 2]], [2, [2, 1]], [2, [2, 1]], [0, 40], 1000000], - [11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92dda8ec93df80b41620df5bc639d0c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')], + [11, 2, 0, 0, bytes.fromhex('810001018202820201828201078203008181820158405d9bdd1e2f043cf971588111f2fe1b847666cfacb7fb403c2468ef92dda8ec93df80b41620df5bc6c355e1cce6217e17d3b8c5560edc14aba3d005196b046e')], [1, 1, 0, 0, bytes.fromhex('526561647920746F2067656E657261746520612033322D62797465207061796C6F6164')] ], # No output because it was deleted, logs to indicate deletion. expected_output=(NO_OUTPUT, DELETION), policy_config='0x62', is_implemented=True, - is_working=False, + is_working=True, expect_success=False, input_data_format=DataFormat.BUNDLEARRAY, expected_output_format=DataFormat.NONE diff --git a/mock-bpa-test/test_bpa.py b/mock-bpa-test/test_bpa.py index 127826fd..c8da8cdc 100644 --- a/mock-bpa-test/test_bpa.py +++ b/mock-bpa-test/test_bpa.py @@ -174,7 +174,17 @@ def _single_test(self, testcase: _TestCase): self._wait_for(self._ul_sock) LOGGER.info('\nTransferred data:\n%s\n', binascii.hexlify(tx_data)) - self.fail('Validate output') + + LOGGER.warning('Check log output to validate reason for no data!!') + + # Currently hard-coded for test case 19 but no other instances of DataFormat.NONE + case_19_str = r".*Delete bundle due to failed security operation" + + LOGGER.debug("Searching test runner logger for failure string: %s", case_19_str) + found = self._agent.wait_for_text(case_19_str) + LOGGER.debug("\nFOUND OCCURENCE: %s", found) + self.assertTrue(found != "") + elif (testcase.expected_output_format == DataFormat.ERR): self._ul_sock.send(tx_data) LOGGER.debug('waiting') @@ -183,7 +193,16 @@ def _single_test(self, testcase: _TestCase): self._wait_for(self._ul_sock) LOGGER.info('\nTransferred data:\n%s\n', binascii.hexlify(tx_data)) - self.fail('TODO handle err codes') + + LOGGER.warning('Check log output to validate expected error') + + # TBD - this logic is not used yet + err_case_str = r"tbd" + + LOGGER.debug("Searching test runner logger for error string: %s", err_case_str) + found = self._agent.wait_for_text(err_case_str) + LOGGER.debug("\nFOUND OCCURENCE: %s", found) + self.assertTrue(found != "") # Below utilizes setattr to add methods to a child class of the TestAgent, which will in-turn give us unit tests diff --git a/src/BPSecLib_Private.h b/src/BPSecLib_Private.h index 1615a46c..bdce3c41 100644 --- a/src/BPSecLib_Private.h +++ b/src/BPSecLib_Private.h @@ -583,6 +583,13 @@ int BSL_BundleCtx_CreateBlock(BSL_BundleRef_t *bundle, uint64_t block_type_code, */ int BSL_BundleCtx_RemoveBlock(BSL_BundleRef_t *bundle, uint64_t block_num); +/** @brief Requests dropping of bundle + * + * @param[in] bundle Context bundle + * @return 0 on success, negative on failure. + */ +int BSL_BundleCtx_DeleteBundle(BSL_BundleRef_t *bundle); + /** @brief Requests the re-allocation of a block's BTSD, useful for BCB. * * @note Uses semantics similar to @c memcpy(). @@ -1225,4 +1232,4 @@ struct BSL_SecCtxDesc_s BSL_SecCtx_Execute_f execute; }; -#endif /* BSL_BPSECLIB_PRIVATE_H_ */ +#endif /* BSL_BPSECLIB_PRIVATE_H_ */ \ No newline at end of file diff --git a/src/BPSecLib_Public.h b/src/BPSecLib_Public.h index e74f333b..ca372b9e 100644 --- a/src/BPSecLib_Public.h +++ b/src/BPSecLib_Public.h @@ -188,6 +188,9 @@ typedef struct /// @brief Host BPA function to reallocate a canonical block's BTSD, keeping existing data in-place. int (*block_realloc_btsd_fn)(BSL_BundleRef_t *bundle_ref, uint64_t block_num, size_t bytesize); + /// @brief Host BPA function to delete Bundle + int (*bundle_delete_fn)(BSL_BundleRef_t *bundle_ref); + /// @brief Host BPA function to encode an EID to CBOR. int (*eid_to_cbor)(void *encoder, const BSL_HostEID_t *eid); diff --git a/src/backend/HostInterface.c b/src/backend/HostInterface.c index 6f5d730a..c5493d0d 100644 --- a/src/backend/HostInterface.c +++ b/src/backend/HostInterface.c @@ -114,6 +114,14 @@ int BSL_BundleCtx_RemoveBlock(BSL_BundleRef_t *bundle, uint64_t block_num) return (result == 0) ? BSL_SUCCESS : BSL_ERR_HOST_CALLBACK_FAILED; } +int BSL_BundleCtx_DeleteBundle(BSL_BundleRef_t *bundle) +{ + CHK_ARG_NONNULL(bundle); + CHK_PRECONDITION(HostDescriptorTable.bundle_delete_fn != NULL); + int result = HostDescriptorTable.bundle_delete_fn(bundle); + return (result == 0) ? BSL_SUCCESS : BSL_ERR_HOST_CALLBACK_FAILED; +} + int BSL_BundleCtx_ReallocBTSD(BSL_BundleRef_t *bundle, uint64_t block_num, size_t bytesize) { CHK_ARG_NONNULL(bundle); diff --git a/src/backend/PublicInterfaceImpl.c b/src/backend/PublicInterfaceImpl.c index 0cdca189..5ab72465 100644 --- a/src/backend/PublicInterfaceImpl.c +++ b/src/backend/PublicInterfaceImpl.c @@ -227,7 +227,7 @@ int BSL_API_ApplySecurity(const BSL_LibCtx_t *bsl, BSL_SecurityResponseSet_t *re } case BSL_POLICYACTION_DROP_BUNDLE: { - BSL_LOG_WARNING("Dropping bundle due to block target num %lu security failure", + BSL_LOG_WARNING("Deleting bundle due to block target num %lu security failure", policy_actions->sec_operations[oper_index].target_block_num); must_drop = true; break; @@ -247,7 +247,9 @@ int BSL_API_ApplySecurity(const BSL_LibCtx_t *bsl, BSL_SecurityResponseSet_t *re if (must_drop) { - BSL_LOG_ERR("TODO Drop bundle using host interface"); + // Drop the bundle and return operation error + BSL_LOG_WARNING("***** Delete bundle due to failed security operation *******"); + BSL_BundleCtx_DeleteBundle(bundle); } // TODO CHK_POSTCONDITION diff --git a/src/mock_bpa/bsl_mock_bpa.c b/src/mock_bpa/bsl_mock_bpa.c index be03b685..41722516 100644 --- a/src/mock_bpa/bsl_mock_bpa.c +++ b/src/mock_bpa/bsl_mock_bpa.c @@ -246,6 +246,21 @@ int MockBPA_RemoveBlock(BSL_BundleRef_t *bundle_ref, uint64_t block_num) return 0; } +int MockBPA_DeleteBundle(BSL_BundleRef_t *bundle_ref) +{ + if (!bundle_ref || !bundle_ref->data) + { + return -1; + } + + MockBPA_Bundle_t *bundle = bundle_ref->data; + + // Mark the bundle for deletion + bundle->retain = false; + + return 0; +} + int bsl_mock_bpa_init(void) { uint8_t *state = BSL_MALLOC(999); @@ -259,6 +274,7 @@ int bsl_mock_bpa_init(void) .bundle_get_block_ids = MockBPA_GetBlockNums, .block_create_fn = MockBPA_CreateBlock, .block_remove_fn = MockBPA_RemoveBlock, + .bundle_delete_fn = MockBPA_DeleteBundle, .block_realloc_btsd_fn = MockBPA_ReallocBTSD, // Old-style callbacks diff --git a/src/mock_bpa/bsl_mock_bpa.h b/src/mock_bpa/bsl_mock_bpa.h index dcbc7fa0..048871e7 100644 --- a/src/mock_bpa/bsl_mock_bpa.h +++ b/src/mock_bpa/bsl_mock_bpa.h @@ -76,6 +76,7 @@ typedef struct MockBPA_CanonicalBlock_s typedef struct MockBPA_Bundle_s { uint64_t id; + bool retain; MockBPA_PrimaryBlock_t primary_block; MockBPA_CanonicalBlock_t blocks[MockBPA_BUNDLE_MAXBLOCKS]; size_t block_count; @@ -91,6 +92,7 @@ int MockBPA_GetBlockMetadata(const BSL_BundleRef_t *bundle_ref, uint64_t block_n int MockBPA_ReallocBTSD(BSL_BundleRef_t *bundle_ref, uint64_t block_num, size_t bytesize); int MockBPA_CreateBlock(BSL_BundleRef_t *bundle_ref, uint64_t block_type_code, uint64_t *result_block_num); int MockBPA_RemoveBlock(BSL_BundleRef_t *bundle_ref, uint64_t block_num); +int MockBPA_DeleteBundle(BSL_BundleRef_t *bundle_ref); /** Register this mock BPA for the current process. * @return Zero if successful. diff --git a/src/mock_bpa/bsl_mock_bpa_policy_config.h b/src/mock_bpa/bsl_mock_bpa_policy_config.h index 945449ce..d6a3f059 100644 --- a/src/mock_bpa/bsl_mock_bpa_policy_config.h +++ b/src/mock_bpa/bsl_mock_bpa_policy_config.h @@ -60,23 +60,16 @@ extern "C" { * | | | | * Policy Action: 00 - nothing, 01 - drop block, -| | | | * 10 - drop bundle, 11: undefined -| | | | -<<<<<<< HEAD * | | | * Target Block Type: 00 - primary, 01 payload -| | | * 10 - bib, 11 - bundle age -| | | * | | -======= * Target Block Type: -| | | ->>>>>>> main * Policy Location: 0 - CLOUT, 1 - CLIN -| | * | * Sec Block Type: 0 - BIB, 1 - BCB -| -<<<<<<< HEAD * - * -======= * @endcode ->>>>>>> main */ typedef uint32_t bsl_mock_policy_configuration_t; @@ -87,6 +80,7 @@ void mock_bpa_handle_policy_config_from_json(const char *pp_cfg_file_path, BSLP_ int mock_bpa_key_registry_init(const char *pp_cfg_file_path); int mock_bpa_hexchar_to_int(char c); + int mock_bpa_hexstring_to_bytes(const char *hexstr, uint8_t *out, size_t out_size); int bsl_mock_bpa_rfc9173_bcb_cek(unsigned char *buf, int len); diff --git a/src/mock_bpa/mock_bpa.c b/src/mock_bpa/mock_bpa.c index ea4e8108..2d48a1f4 100644 --- a/src/mock_bpa/mock_bpa.c +++ b/src/mock_bpa/mock_bpa.c @@ -204,6 +204,14 @@ static void *work_over_rx(void *arg _U_) continue; } + MockBPA_Bundle_t *bundle = item.bundle_ref.data; + if (!bundle->retain) + { + BSL_LOG_ERR("bundle was marked to delete by BSL"); + mock_bpa_ctr_deinit(&item); + continue; + } + // loopback data_queue_push(deliver, item); } @@ -240,6 +248,14 @@ static void *work_under_rx(void *arg _U_) continue; } + MockBPA_Bundle_t *bundle = item.bundle_ref.data; + if (!bundle->retain) + { + BSL_LOG_ERR("bundle was marked to delete by BSL"); + mock_bpa_ctr_deinit(&item); + continue; + } + // loopback data_queue_push(forward, item); } @@ -269,6 +285,14 @@ static void *work_deliver(void *arg _U_) continue; } + MockBPA_Bundle_t *bundle = item.bundle_ref.data; + if (!bundle->retain) + { + BSL_LOG_ERR("bundle was marked to delete by BSL"); + mock_bpa_ctr_deinit(&item); + continue; + } + mock_bpa_encode(&item); data_queue_push(over_tx, item); { @@ -306,6 +330,14 @@ static void *work_forward(void *arg _U_) continue; } + MockBPA_Bundle_t *bundle = item.bundle_ref.data; + if (!bundle->retain) + { + BSL_LOG_ERR("bundle was marked to delete by BSL"); + mock_bpa_ctr_deinit(&item); + continue; + } + mock_bpa_encode(&item); data_queue_push(under_tx, item); { diff --git a/src/mock_bpa/mock_bpa_ctr.c b/src/mock_bpa/mock_bpa_ctr.c index 4dc78ab7..deccb0dd 100644 --- a/src/mock_bpa/mock_bpa_ctr.c +++ b/src/mock_bpa/mock_bpa_ctr.c @@ -31,6 +31,11 @@ void mock_bpa_ctr_init(mock_bpa_ctr_t *ctr) memset(ctr, 0, sizeof(*ctr)); BSL_Data_Init(&(ctr->encoded)); ctr->bundle_ref.data = calloc(1, sizeof(MockBPA_Bundle_t)); + // TODO : Just make a MockBPA_Bundle_Init function. + // HostEID_t's are initialized deeper into the decode function. + + MockBPA_Bundle_t *bundle = ctr->bundle_ref.data; + bundle->retain = true; } void mock_bpa_ctr_init_move(mock_bpa_ctr_t *ctr, mock_bpa_ctr_t *src)