diff --git a/CMakeLists.txt b/CMakeLists.txt index b4baafb5..4c8b448a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,7 @@ option(BUILD_UNITTEST "Enable building unit tests" ON) option(TEST_MEMCHECK "Enable test runtime memory checking" ON) option(BUILD_COVERAGE "Enable runtime coverage logging and reporting" OFF) option(BUILD_FUZZING "Enable building fuzzing executables" OFF) +option(ION_INTEGRATION "Build BSL with ION memory allocators" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -67,10 +68,54 @@ project(bsl VERSION ${PROJECT_VERSION} ) +# ION Integration - build with ION memory allocators +if(ION_INTEGRATION) + message(STATUS "Building BSL with ION memory allocators") + + # Define ION_INTEGRATION for C preprocessor + add_definitions(-DION_INTEGRATION) + + # Find ION installation + find_path(ION_INCLUDE_DIR ion.h + HINTS ${ION_ROOT}/include ${CMAKE_SOURCE_DIR}/../../ici/include /usr/local/include + REQUIRED) + find_library(ION_ICI_LIBRARY ici + HINTS ${ION_ROOT}/lib ${ION_ROOT}/.libs ${CMAKE_SOURCE_DIR}/../../ici/x86_64-linux/lib /usr/local/lib + REQUIRED) + + message(STATUS "ION headers: ${ION_INCLUDE_DIR}") + message(STATUS "ION libici: ${ION_ICI_LIBRARY}") + + # Add ionpatch.c as a static library + add_library(bsl_ionpatch STATIC + src/ION_integration/ionpatch.c) + target_include_directories(bsl_ionpatch PRIVATE ${ION_INCLUDE_DIR}) + target_link_libraries(bsl_ionpatch PRIVATE ${ION_ICI_LIBRARY}) + + # All BSL libraries must link against ionpatch + set(BSL_ION_DEPS bsl_ionpatch ${ION_ICI_LIBRARY}) +endif() + # Language options set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) set(CMAKE_C_EXTENSIONS OFF) +# Add platform flag BEFORE POSIX flags (required by ION's platform.h) +if(ION_INTEGRATION) + # Add _DEFAULT_SOURCE to make MAXPATHLEN and other system defs visible + add_definitions(-D_DEFAULT_SOURCE) + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + add_definitions(-Dlinux) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + add_definitions(-Ddarwin) + elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") + add_definitions(-Dfreebsd) + elseif(CMAKE_SYSTEM_NAME STREQUAL "SunOS") + add_definitions(-Dsolaris) + elseif(WIN32) + add_definitions(-Dmingw) + endif() +endif() # Force specific POSIX compatibility version add_definitions( -D_XOPEN_SOURCE diff --git a/build-for-ion.sh b/build-for-ion.sh new file mode 100755 index 00000000..c51b743f --- /dev/null +++ b/build-for-ion.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Build BSL with ION memory allocators +set -e + +# Detect ION installation +ION_ROOT="${ION_ROOT:-$(cd ../../ && pwd)}" +echo "Using ION_ROOT: $ION_ROOT" + +# Clean previous build +./build.sh clean + +# Rebuild all dependencies +./build.sh deps + +# Prepare with ION integration enabled +./build.sh prep \ + -DION_INTEGRATION=ON \ + -DION_ROOT="$ION_ROOT" \ + -DBUILD_TESTING=OFF + +# Build +./build.sh + +# Install to testroot +./build.sh install + +echo "BSL built successfully with ION memory allocators" +echo "Libraries installed to: $(pwd)/testroot/usr/lib" diff --git a/mock-bpa-test/policy_provider_test.json b/mock-bpa-test/policy_provider_test.json index 5b9fb280..6d452cb7 100644 --- a/mock-bpa-test/policy_provider_test.json +++ b/mock-bpa-test/policy_provider_test.json @@ -2,12 +2,14 @@ "policyrule_set": [ { "policyrule": { - "desc": "Integrity source rule", + "desc": "Integrity source rule for outgoing bundles", "filter": { "rule_id": "1", "role": "s", + "src": "ipn:2.*", + "dest": "ipn:3.*", "tgt": 1, - "loc": "clin", + "loc": "appin", "sc_id": 1 }, "spec": { @@ -47,12 +49,14 @@ }, { "policyrule": { - "desc": "Confidentiality source rule", + "desc": "Confidentiality source rule for outgoing bundles", "filter": { "rule_id": "2", "role": "s", + "src": "ipn:2.*", + "dest": "ipn:3.*", "tgt": 1, - "loc": "clin", + "loc": "appin", "sc_id": 2 }, "spec": { @@ -90,4 +94,4 @@ ] } }] -} \ No newline at end of file +} diff --git a/src/BPSecLib_Private.h b/src/BPSecLib_Private.h index 4dc4ee5a..6960028a 100644 --- a/src/BPSecLib_Private.h +++ b/src/BPSecLib_Private.h @@ -135,9 +135,13 @@ typedef enum return val; \ } /// Return from void functions if condition fails. +#ifndef CHKVOID #define CHKVOID(cond) CHKRET(cond, ) +#endif /// Return a null pointer if condition fails. +#ifndef CHKNULL #define CHKNULL(cond) CHKRET(cond, NULL) +#endif /// Return false if condition fails. #define CHKFALSE(cond) CHKRET(cond, false) /// Return the error value 1 if condition fails. diff --git a/src/BSLConfig.h.in b/src/BSLConfig.h.in index 0bdb33c1..0dd0c379 100644 --- a/src/BSLConfig.h.in +++ b/src/BSLConfig.h.in @@ -32,6 +32,16 @@ extern "C" { #endif +/* --------- Start of modifications for ION integration -----------*/ +#ifdef ION_INTEGRATION + /* Include ionpatch.h to redefine BSL memory allocators. + * This must come BEFORE the #ifndef guards for BSL_MALLOC, etc. + * Path is relative to the include directories set by CMake. + */ + #include "ION_integration/ionpatch.h" +#endif /* ION_INTEGRATION */ +/* ---------- End of modifications for ION integration ------------*/ + /** Part of POSIX.1-2008 */ #cmakedefine HAVE_CLOCK_GETTIME diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 800a9833..7653ce6a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -52,6 +52,9 @@ target_include_directories( $ $/include/bsl> ) +if(ION_INTEGRATION) + target_link_libraries(bsl_front PUBLIC ${BSL_ION_DEPS}) +endif() # Crypto Library set(BSL_CRYPTO_C @@ -71,6 +74,9 @@ target_link_libraries(bsl_crypto INTERFACE OpenSSL::Crypto) if(TARGET valgrind::valgrind) target_compile_definitions(bsl_crypto PRIVATE HAVE_VALGRIND) endif(TARGET valgrind::valgrind) +if(ION_INTEGRATION) + target_link_libraries(bsl_crypto PUBLIC ${BSL_ION_DEPS}) +endif() # Default Security Context library set(BSL_DEFAULT_SC_H @@ -95,6 +101,9 @@ set_target_properties(bsl_default_sc target_link_libraries(bsl_default_sc PUBLIC bsl_front) target_link_libraries(bsl_default_sc PUBLIC bsl_crypto) target_link_libraries(bsl_default_sc PUBLIC QCBOR::qcbor) +if(ION_INTEGRATION) + target_link_libraries(bsl_default_sc PUBLIC ${BSL_ION_DEPS}) +endif() # Example Policy Provider library set(BSL_SAMPLE_PP_H @@ -115,6 +124,9 @@ set_target_properties(bsl_sample_pp ) target_link_libraries(bsl_sample_pp PUBLIC bsl_front) target_link_libraries(bsl_sample_pp PUBLIC MLIB::mlib) +if(ION_INTEGRATION) + target_link_libraries(bsl_sample_pp PUBLIC ${BSL_ION_DEPS}) +endif() # Dynamic backend library set(BSL_DYNAMIC_H @@ -151,6 +163,9 @@ set_target_properties(bsl_dynamic target_link_libraries(bsl_dynamic PUBLIC bsl_front) target_link_libraries(bsl_dynamic PUBLIC MLIB::mlib) target_link_libraries(bsl_dynamic PUBLIC QCBOR::qcbor) +if(ION_INTEGRATION) + target_link_libraries(bsl_dynamic PUBLIC ${BSL_ION_DEPS}) +endif() add_subdirectory(mock_bpa) diff --git a/src/ION_integration/ionpatch.c b/src/ION_integration/ionpatch.c new file mode 100644 index 00000000..e56a5760 --- /dev/null +++ b/src/ION_integration/ionpatch.c @@ -0,0 +1,90 @@ +/* + * ionpatch.c: functions enabling the use of ION's + * private memory management system in + * the BPSec Library. + * + * Author: Scott Burleigh + * + * Copyright (c) 2025, California Institute of Technology. + * ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged. + */ + +#include +#include "ion.h" +#include "ionpatch.h" + +extern void *ion_malloc(const char *file, int line, size_t size) +{ +#if 0 + writeMemo("[i] ION BSL_MALLOC called."); +#endif + return allocFromIonMemory(file, line, size); +} + +extern void *ion_calloc(const char *file, int line, size_t ct, size_t size) +{ +#if 0 + writeMemo("[i] ION BSL_CALLOC called."); +#endif + return allocFromIonMemory(file, line, ct * size); +} + +extern void *ion_realloc(const char *file, int line, void *mem, size_t size) +{ + void *newMem; +#if 0 + writeMemo("[?] ION's dubious function for BSL_REALLOC invoked."); + printStackTrace(); +#endif + /* Note that this function is of no use to the + * BSL_BundleCtx_ReallocBTSD function, since + ION stores extension blocks in the SDR heap + rather than in system memory. + + Note also that the ION implementation of this + function does not work in the way that standard + realloc() works. The space immediately adjacent + to a block of ION working memory will typically + NOT be unoccupied, i.e, will NOT be available + to be silently appended to that block. + + So the content of the original block is instead + copied to a newly allocated block of the requested + size; the address of that new block is returned. */ + + if (size == 0) + { + if (mem) + { + releaseToIonMemory(file, line, mem); + } + + newMem = NULL; + } + else + { + newMem = allocFromIonMemory(file, line, size); + if (newMem == NULL) + { + newMem = mem; /* Retain orginal data. */ + } + else /* New memory block was obtained. */ + { + if (mem) + { + memcpy(newMem, mem, size); + releaseToIonMemory(file, line, mem); + } + } + } + + return newMem; +} + +extern void ion_free(const char *file, int line, void *mem) +{ +#if 0 + writeMemo("[i] ION BSL_FREE called."); +#endif + releaseToIonMemory(file, line, mem); +} diff --git a/src/ION_integration/ionpatch.h b/src/ION_integration/ionpatch.h new file mode 100644 index 00000000..d8c3c35a --- /dev/null +++ b/src/ION_integration/ionpatch.h @@ -0,0 +1,37 @@ +/* + ionpatch.h: private definitions supporting the use + of ION's private memory management system + in the BPSec Library. + + Author: Scott Burleigh + + Copyright (c) 2025, California Institute of Technology. + ALL RIGHTS RESERVED. U.S. Government Sponsorship acknowledged. + */ +#ifndef ION_INTEGRATION_IONPATCH_H_ +#define ION_INTEGRATION_IONPATCH_H_ + +#include + +/* Forward declarations of ION memory functions. + * We don't include ion.h here to avoid header dependency issues. + * The actual implementation in ionpatch.c will include ion.h. */ + +extern void *ion_malloc(const char *, int, size_t); +extern void *ion_calloc(const char *, int, size_t, size_t); +extern void *ion_realloc(const char *, int, void *, size_t); +extern void ion_free(const char *, int, void *); + +/* Undefine BSL's default memory allocators (from BSLConfig.h) so we + * can redefine them to use ION's memory management system instead. */ +#undef BSL_MALLOC +#undef BSL_CALLOC +#undef BSL_REALLOC +#undef BSL_FREE + +#define BSL_MALLOC(size) ion_malloc(__FILE__, __LINE__, size) +#define BSL_CALLOC(count, size) ion_calloc(__FILE__, __LINE__, count, size) +#define BSL_REALLOC(mem, size) ion_realloc(__FILE__, __LINE__, mem, size) +#define BSL_FREE(mem) ion_free(__FILE__, __LINE__, mem) + +#endif /* ION_INTEGRATION_IONPATCH_H_ */ diff --git a/src/backend/HostInterface.c b/src/backend/HostInterface.c index cf8ff3e9..04d7b4d4 100644 --- a/src/backend/HostInterface.c +++ b/src/backend/HostInterface.c @@ -134,6 +134,19 @@ BSL_SeqWriter_t *BSL_BundleCtx_WriteBTSD(BSL_BundleRef_t *bundle, uint64_t block { return NULL; } + + /* Ensure the BTSD buffer is large enough before writing */ + if (btsd_len > 0 && HostDescriptorTable.block_realloc_btsd_fn) + { + int realloc_result = HostDescriptorTable.block_realloc_btsd_fn(bundle, block_num, btsd_len); + if (realloc_result != 0) + { + BSL_LOG_ERR("Failed to realloc BTSD buffer: block=%llu size=%zu result=%d", + (unsigned long long)block_num, btsd_len, realloc_result); + return NULL; + } + } + return HostDescriptorTable.block_write_btsd_fn(bundle, block_num, btsd_len); } diff --git a/src/backend/PublicInterfaceImpl.c b/src/backend/PublicInterfaceImpl.c index e7601078..28381594 100644 --- a/src/backend/PublicInterfaceImpl.c +++ b/src/backend/PublicInterfaceImpl.c @@ -91,7 +91,11 @@ void BSL_PrimaryBlock_deinit(BSL_PrimaryBlock_t *obj) { ASSERT_ARG_NONNULL(obj); - BSL_FREE(obj->block_numbers); + // Unconditionally free - BSL_FREE will use correct allocator + if (obj->block_numbers) + { + BSL_FREE(obj->block_numbers); + } obj->block_numbers = NULL; BSL_Data_Deinit(&obj->encoded); @@ -153,6 +157,7 @@ int BSL_API_QuerySecurity(const BSL_LibCtx_t *bsl, BSL_SecurityActionSet_t *outp BSL_LOG_WARNING("Failed to get block number %" PRIu64, primary_block.block_numbers[ix]); continue; } + BSL_SecActionList_it_t act_it; for (BSL_SecActionList_it(act_it, output_action_set->actions); !BSL_SecActionList_end_p(act_it); BSL_SecActionList_next(act_it)) @@ -166,7 +171,6 @@ int BSL_API_QuerySecurity(const BSL_LibCtx_t *bsl, BSL_SecurityActionSet_t *outp continue; } - // ASB decoder needs the whole BTSD now BSL_Data_t btsd_copy; BSL_Data_InitBuffer(&btsd_copy, block.btsd_len); diff --git a/src/backend/SecurityContext.c b/src/backend/SecurityContext.c index 5887443c..ab6ba5d1 100644 --- a/src/backend/SecurityContext.c +++ b/src/backend/SecurityContext.c @@ -101,6 +101,10 @@ static int BSL_ExecBIBSource(BSL_SecCtx_Execute_f sec_context_fn, BSL_LibCtx_t * CHK_PROPERTY(created_block_num > 1); + // Store the created block number in the security operation so the + // security context can find it + sec_oper->sec_block_num = created_block_num; + const int bib_result = (*sec_context_fn)(lib, bundle, sec_oper, outcome); if (bib_result != 0) // || outcome->is_success == false) { @@ -269,6 +273,87 @@ static int BSL_ExecBIBAccept(BSL_SecCtx_Execute_f sec_context_fn, BSL_LibCtx_t * return auth_success ? BSL_SUCCESS : BSL_ERR_SECURITY_OPERATION_FAILED; } +/** Lightweight BCB verifier: validates ASB is decodable and parameters are + * present, but does NOT decrypt the payload. A verifier must not modify + * the bundle so that downstream acceptors can still process it. + */ +static int BSL_ExecBCBVerifier(BSL_LibCtx_t *lib, BSL_BundleRef_t *bundle, + BSL_SecOper_t *sec_oper) +{ + CHK_ARG_NONNULL(lib); + CHK_ARG_NONNULL(bundle); + CHK_PRECONDITION(BSL_SecOper_IsConsistent(sec_oper)); + + BSL_CanonicalBlock_t sec_blk = { 0 }; + if (BSL_BundleCtx_GetBlockMetadata(bundle, sec_oper->sec_block_num, &sec_blk) != BSL_SUCCESS) + { + BSL_LOG_ERR("BCB Verifier: could not get block metadata"); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_FAIL_COUNT, 1); + return BSL_ERR_HOST_CALLBACK_FAILED; + } + + /* Decode the ASB to validate it is well-formed. */ + BSL_Data_t btsd_copy; + BSL_Data_InitBuffer(&btsd_copy, sec_blk.btsd_len); + + BSL_SeqReader_t *btsd_read = BSL_BundleCtx_ReadBTSD(bundle, sec_blk.block_num); + BSL_SeqReader_Get(btsd_read, btsd_copy.ptr, &btsd_copy.len); + BSL_SeqReader_Destroy(btsd_read); + + BSL_AbsSecBlock_t abs_sec_block; + BSL_AbsSecBlock_InitEmpty(&abs_sec_block); + if (BSL_AbsSecBlock_DecodeFromCBOR(&abs_sec_block, &btsd_copy) != BSL_SUCCESS) + { + BSL_LOG_ERR("BCB Verifier: failed to parse ASB CBOR"); + BSL_AbsSecBlock_Deinit(&abs_sec_block); + BSL_Data_Deinit(&btsd_copy); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_FAIL_COUNT, 1); + return BSL_ERR_DECODING; + } + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_ASB_DECODE_BYTES, sec_blk.btsd_len); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_ASB_DECODE_COUNT, 1); + BSL_Data_Deinit(&btsd_copy); + + CHK_PROPERTY(BSL_AbsSecBlock_IsConsistent(&abs_sec_block)); + + /* Copy ASB params to the security operation for policy inspection. */ + for (size_t i = 0; i < BSLB_SecParamList_size(abs_sec_block.params); i++) + { + const BSL_SecParam_t *param = BSLB_SecParamList_cget(abs_sec_block.params, i); + CHK_PROPERTY(BSL_SecParam_IsConsistent(param)); + BSLB_SecParamList_push_back(sec_oper->_param_list, *param); + } + + /* Intentionally do NOT call the security context execute function. + * Decryption would modify the payload in-place, which a verifier + * must never do. We only confirm the ASB is decodable and contains + * the expected results for our target block. */ + bool found_result = false; + for (size_t i = 0; i < BSLB_SecResultList_size(abs_sec_block.results); i++) + { + BSL_SecResult_t *result = BSLB_SecResultList_get(abs_sec_block.results, i); + if (result->target_block_num == sec_oper->target_block_num) + { + found_result = true; + break; + } + } + + BSL_AbsSecBlock_Deinit(&abs_sec_block); + + if (!found_result) + { + BSL_LOG_ERR("BCB Verifier: no result found for target block %" PRIu64, + sec_oper->target_block_num); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_FAIL_COUNT, 1); + return BSL_ERR_SECURITY_OPERATION_FAILED; + } + + BSL_LOG_INFO("BCB Verify SUCCESS"); + BSL_TlmCounters_IncrementCounter(lib, BSL_TLM_SECOP_VERIFIER_COUNT, 1); + return BSL_SUCCESS; +} + static int BSL_ExecBCBAcceptor(BSL_SecCtx_Execute_f sec_context_fn, BSL_LibCtx_t *lib, BSL_BundleRef_t *bundle, BSL_SecOper_t *sec_oper, BSL_SecOutcome_t *outcome) { @@ -511,6 +596,10 @@ int BSL_SecCtx_ExecutePolicyActionSet(BSL_LibCtx_t *lib, BSL_SecurityResponseSet { errcode = BSL_ExecBCBSource(sec_ctx->execute, lib, bundle, sec_oper, outcome); } + else if (BSL_SecOper_IsRoleVerifier(sec_oper)) + { + errcode = BSL_ExecBCBVerifier(lib, bundle, sec_oper); + } else { errcode = BSL_ExecBCBAcceptor(sec_ctx->execute, lib, bundle, sec_oper, outcome);