diff --git a/README.md b/README.md index af882d9..8e134cb 100644 --- a/README.md +++ b/README.md @@ -16,14 +16,43 @@ For more information on KMIP, check out the For more information on libkmip, check out the project [Documentation][docs]. -## Installation +## Build + +Build with CMake: + +```bash +cmake -S . -B cmake-build-debug +cmake --build cmake-build-debug -j +``` + +## Run Demos + +Demo binaries are created in `cmake-build-debug/libkmip/src/`. +For example: + +```bash +./cmake-build-debug/libkmip/src/demo_query +``` + +## Run Tests (ASAN) -You can install libkmip from source using `make`: +An AddressSanitizer + LeakSanitizer test target is available for +`libkmip/src/tests.c`: +```bash +cmake --build cmake-build-debug --target run_tests_asan ``` -$ cd libkmip -$ make -$ make install + +This command builds and runs `kmip_tests_asan` with leak detection enabled. + +## Installation + +You can also install libkmip from source using `make`: + +```bash +cd libkmip +make +make install ``` See [Installation][install] for more information. diff --git a/libkmip/include/kmip.h b/libkmip/include/kmip.h index a39c293..82cc5cb 100644 --- a/libkmip/include/kmip.h +++ b/libkmip/include/kmip.h @@ -1726,6 +1726,7 @@ typedef int64 intptr; void kmip_free_symmetric_key (KMIP *, SymmetricKey *); void kmip_free_public_key (KMIP *, PublicKey *); void kmip_free_private_key (KMIP *, PrivateKey *); + void kmip_free_secret_data (KMIP *, SecretData *); void kmip_free_key_wrapping_specification (KMIP *, KeyWrappingSpecification *); void kmip_free_create_request_payload (KMIP *, CreateRequestPayload *); void kmip_free_create_response_payload (KMIP *, CreateResponsePayload *); diff --git a/libkmip/src/CMakeLists.txt b/libkmip/src/CMakeLists.txt index 2cc92cf..0eca72d 100644 --- a/libkmip/src/CMakeLists.txt +++ b/libkmip/src/CMakeLists.txt @@ -45,3 +45,41 @@ add_demo(get) add_demo(locate) add_demo(register) add_demo(query) + +# ASAN-enabled test runner for libkmip/src/tests.c. +add_executable( + kmip_tests_asan + tests.c + kmip_bio.c + kmip.c + kmip_locate.c + kmip_memset.c +) + +target_include_directories( + kmip_tests_asan PRIVATE + ${KMIP_SOURCE_DIR}/libkmip/include/ +) + +target_link_libraries(kmip_tests_asan OpenSSL::SSL OpenSSL::Crypto) + +target_compile_options( + kmip_tests_asan PRIVATE + -fsanitize=address + -fno-omit-frame-pointer + -include + stdio.h +) + +target_link_options( + kmip_tests_asan PRIVATE + -fsanitize=address +) + +add_custom_target( + run_tests_asan + COMMAND ${CMAKE_COMMAND} -E env ASAN_OPTIONS=detect_leaks=1:abort_on_error=1:halt_on_error=1 $ + DEPENDS kmip_tests_asan + USES_TERMINAL +) + diff --git a/libkmip/src/kmip.c b/libkmip/src/kmip.c index d9d775c..a4697ac 100644 --- a/libkmip/src/kmip.c +++ b/libkmip/src/kmip.c @@ -5804,6 +5804,24 @@ kmip_free_private_key (KMIP *ctx, PrivateKey *value) return; } +void +kmip_free_secret_data (KMIP *ctx, SecretData *value) +{ + if (value != NULL) + { + if (value->key_block != NULL) + { + kmip_free_key_block (ctx, value->key_block); + ctx->free_func (ctx->state, value->key_block); + value->key_block = NULL; + } + + value->secret_data_type = 0; + } + + return; +} + void kmip_free_key_wrapping_specification (KMIP *ctx, KeyWrappingSpecification *value) { @@ -6030,6 +6048,10 @@ kmip_free_get_response_payload (KMIP *ctx, GetResponsePayload *value) kmip_free_private_key (ctx, (PrivateKey *)value->object); break; + case KMIP_OBJTYPE_SECRET_DATA: + kmip_free_secret_data (ctx, (SecretData *)value->object); + break; + default: /* NOTE (ph) Hitting this case means that we don't know */ /* what the actual type, size, or value of */ @@ -6139,6 +6161,54 @@ kmip_free_destroy_response_payload (KMIP *ctx, DestroyResponsePayload *value) return; } +void +kmip_free_revoke_request_payload (KMIP *ctx, RevokeRequestPayload *value) +{ + if (value != NULL) + { + if (value->unique_identifier != NULL) + { + kmip_free_text_string (ctx, value->unique_identifier); + ctx->free_func (ctx->state, value->unique_identifier); + value->unique_identifier = NULL; + } + + if (value->revocation_reason != NULL) + { + if (value->revocation_reason->message != NULL) + { + kmip_free_text_string (ctx, value->revocation_reason->message); + ctx->free_func (ctx->state, value->revocation_reason->message); + value->revocation_reason->message = NULL; + } + + value->revocation_reason->reason = 0; + ctx->free_func (ctx->state, value->revocation_reason); + value->revocation_reason = NULL; + } + + value->compromise_occurence_date = 0; + } + + return; +} + +void +kmip_free_revoke_response_payload (KMIP *ctx, RevokeResponsePayload *value) +{ + if (value != NULL) + { + if (value->unique_identifier != NULL) + { + kmip_free_text_string (ctx, value->unique_identifier); + ctx->free_func (ctx->state, value->unique_identifier); + value->unique_identifier = NULL; + } + } + + return; +} + void kmip_free_request_batch_item (KMIP *ctx, RequestBatchItem *value) { @@ -6187,6 +6257,10 @@ kmip_free_request_batch_item (KMIP *ctx, RequestBatchItem *value) kmip_free_locate_request_payload (ctx, (LocateRequestPayload *)value->request_payload); break; + case KMIP_OP_REVOKE: + kmip_free_revoke_request_payload (ctx, (RevokeRequestPayload *)value->request_payload); + break; + default: /* NOTE (ph) Hitting this case means that we don't know */ /* what the actual type, size, or value of */ @@ -6272,6 +6346,10 @@ kmip_free_response_batch_item (KMIP *ctx, ResponseBatchItem *value) kmip_free_locate_response_payload (ctx, (LocateResponsePayload *)value->response_payload); break; + case KMIP_OP_REVOKE: + kmip_free_revoke_response_payload (ctx, (RevokeResponsePayload *)value->response_payload); + break; + default: /* NOTE (ph) Hitting this case means that we don't know */ /* what the actual type, size, or value of */ @@ -6777,14 +6855,47 @@ kmip_free_objects (KMIP *ctx, ObjectTypes *value) void kmip_free_server_information (KMIP *ctx, ServerInformation *value) { - kmip_free_text_string (ctx, value->server_name); - kmip_free_text_string (ctx, value->server_serial_number); - kmip_free_text_string (ctx, value->server_version); - kmip_free_text_string (ctx, value->server_load); - kmip_free_text_string (ctx, value->product_name); - kmip_free_text_string (ctx, value->build_level); - kmip_free_text_string (ctx, value->build_date); - kmip_free_text_string (ctx, value->cluster_info); + if (ctx == NULL || value == NULL) + return; + +#define FREE_TEXT_FIELD(field) \ + if (value->field != NULL) \ + { \ + kmip_free_text_string (ctx, value->field); \ + ctx->free_func (ctx->state, value->field); \ + value->field = NULL; \ + } + + FREE_TEXT_FIELD (server_name) + FREE_TEXT_FIELD (server_serial_number) + FREE_TEXT_FIELD (server_version) + FREE_TEXT_FIELD (server_load) + FREE_TEXT_FIELD (product_name) + FREE_TEXT_FIELD (build_level) + FREE_TEXT_FIELD (build_date) + FREE_TEXT_FIELD (cluster_info) + +#undef FREE_TEXT_FIELD + + if (value->alternative_failover_endpoints != NULL) + { + AltEndpoints *alt = value->alternative_failover_endpoints; + if (alt->endpoint_list != NULL) + { + LinkedListItem *item = kmip_linked_list_pop (alt->endpoint_list); + while (item != NULL) + { + kmip_free_text_string (ctx, (TextString *)item->data); + ctx->free_func (ctx->state, item->data); + ctx->free_func (ctx->state, item); + item = kmip_linked_list_pop (alt->endpoint_list); + } + ctx->free_func (ctx->state, alt->endpoint_list); + alt->endpoint_list = NULL; + } + ctx->free_func (ctx->state, value->alternative_failover_endpoints); + value->alternative_failover_endpoints = NULL; + } } /* @@ -12374,6 +12485,10 @@ kmip_encode_response_batch_item (KMIP *ctx, const ResponseBatchItem *value) result = kmip_encode_register_response_payload (ctx, (RegisterResponsePayload *)value->response_payload); break; + case KMIP_OP_GET: + result = kmip_encode_get_response_payload (ctx, (GetResponsePayload *)value->response_payload); + break; + case KMIP_OP_GET_ATTRIBUTES: result = kmip_encode_get_attribute_response_payload (ctx, (GetAttributeResponsePayload *)value->response_payload); break; diff --git a/libkmip/src/tests.c b/libkmip/src/tests.c index d01ef23..d4e5b17 100644 --- a/libkmip/src/tests.c +++ b/libkmip/src/tests.c @@ -5547,6 +5547,150 @@ test_decode_get_response_payload (TestTracker *tracker) return (result); } +int +test_decode_get_response_payload_secret_data (TestTracker *tracker) +{ + TRACK_TEST (tracker); + + uint8 encoding[256] = { 0 }; + struct kmip encode_ctx = { 0 }; + kmip_init (&encode_ctx, encoding, ARRAY_LENGTH (encoding), KMIP_1_0); + + struct text_string uuid = { 0 }; + uuid.value = "secret-data-id"; + uuid.size = 14; + + uint8 key_material[4] = { 0xDE, 0xAD, 0xBE, 0xEF }; + struct byte_string key_bytes = { 0 }; + key_bytes.value = key_material; + key_bytes.size = ARRAY_LENGTH (key_material); + + struct key_value kv = { 0 }; + kv.key_material = &key_bytes; + + struct key_block kb = { 0 }; + kb.key_format_type = KMIP_KEYFORMAT_RAW; + kb.key_value = &kv; + kb.cryptographic_algorithm = KMIP_CRYPTOALG_AES; + kb.cryptographic_length = 128; + + struct secret_data secret = { 0 }; + secret.secret_data_type = PASSWORD; + secret.key_block = &kb; + + int result = 0; + uint8 *length_index = NULL; + uint8 *value_index = NULL; + uint8 *curr_index = NULL; + + result = kmip_encode_int32_be (&encode_ctx, TAG_TYPE (KMIP_TAG_RESPONSE_PAYLOAD, KMIP_TYPE_STRUCTURE)); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + + length_index = encode_ctx.index; + value_index = encode_ctx.index += 4; + + result = kmip_encode_enum (&encode_ctx, KMIP_TAG_OBJECT_TYPE, KMIP_OBJTYPE_SECRET_DATA); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + result = kmip_encode_text_string (&encode_ctx, KMIP_TAG_UNIQUE_IDENTIFIER, &uuid); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + result = kmip_encode_int32_be (&encode_ctx, TAG_TYPE (KMIP_TAG_SECRET_DATA, KMIP_TYPE_STRUCTURE)); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + + uint8 *secret_length_index = encode_ctx.index; + uint8 *secret_value_index = encode_ctx.index += 4; + + result = kmip_encode_enum (&encode_ctx, KMIP_TAG_SECRET_DATA_TYPE, secret.secret_data_type); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + + result = kmip_encode_key_block (&encode_ctx, secret.key_block); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + + curr_index = encode_ctx.index; + encode_ctx.index = secret_length_index; + + result = kmip_encode_length (&encode_ctx, curr_index - secret_value_index); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + encode_ctx.index = curr_index; + + curr_index = encode_ctx.index; + encode_ctx.index = length_index; + + result = kmip_encode_length (&encode_ctx, curr_index - value_index); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + encode_ctx.index = curr_index; + + size_t decode_size = (size_t)(encode_ctx.index - encode_ctx.buffer); + + struct kmip decode_ctx = { 0 }; + kmip_init (&decode_ctx, encoding, decode_size, KMIP_1_0); + + struct get_response_payload observed = { 0 }; + result = kmip_decode_get_response_payload (&decode_ctx, &observed); + + int comparison = 0; + if (result == KMIP_OK) + { + SecretData *decoded_secret = (SecretData *)observed.object; + KeyValue *decoded_kv = (decoded_secret != NULL) ? (KeyValue *)decoded_secret->key_block->key_value : NULL; + ByteString *decoded_material = (decoded_kv != NULL) ? (ByteString *)decoded_kv->key_material : NULL; + comparison = (observed.object_type == KMIP_OBJTYPE_SECRET_DATA) && (observed.unique_identifier != NULL) + && (observed.unique_identifier->size == uuid.size) + && (memcmp (observed.unique_identifier->value, uuid.value, uuid.size) == 0) + && (decoded_secret != NULL) && (decoded_secret->secret_data_type == PASSWORD) + && (decoded_secret->key_block != NULL) && (decoded_kv != NULL) && (decoded_material != NULL) + && (decoded_material->size == ARRAY_LENGTH (key_material)) + && (memcmp (decoded_material->value, key_material, ARRAY_LENGTH (key_material)) + == 0); + } + + result = report_decoding_test_result (tracker, &decode_ctx, comparison, result, __func__); + + kmip_free_get_response_payload (&decode_ctx, &observed); + kmip_destroy (&decode_ctx); + kmip_destroy (&encode_ctx); + return (result); +} + int test_encode_destroy_request_payload (TestTracker *tracker) { @@ -6370,6 +6514,61 @@ test_decode_response_batch_item_get_payload (TestTracker *tracker) return (result); } +int +test_decode_response_batch_item_revoke_payload (TestTracker *tracker) +{ + TRACK_TEST (tracker); + + uint8 encoding[128] = { 0 }; + struct kmip encode_ctx = { 0 }; + kmip_init (&encode_ctx, encoding, ARRAY_LENGTH (encoding), KMIP_1_0); + + struct text_string uuid = { 0 }; + uuid.value = "revoke-id"; + uuid.size = 9; + + struct revoke_response_payload payload = { 0 }; + payload.unique_identifier = &uuid; + + struct response_batch_item item = { 0 }; + item.operation = KMIP_OP_REVOKE; + item.result_status = KMIP_STATUS_SUCCESS; + item.response_payload = &payload; + + int result = kmip_encode_response_batch_item (&encode_ctx, &item); + if (result != KMIP_OK) + { + result = report_result (tracker, result, KMIP_OK, __func__); + kmip_destroy (&encode_ctx); + return (result); + } + + size_t decode_size = (size_t)(encode_ctx.index - encode_ctx.buffer); + + struct kmip decode_ctx = { 0 }; + kmip_init (&decode_ctx, encoding, decode_size, KMIP_1_0); + + struct response_batch_item observed = { 0 }; + result = kmip_decode_response_batch_item (&decode_ctx, &observed); + + int comparison = 0; + if (result == KMIP_OK) + { + RevokeResponsePayload *decoded_payload = (RevokeResponsePayload *)observed.response_payload; + comparison = (observed.operation == KMIP_OP_REVOKE) && (observed.result_status == KMIP_STATUS_SUCCESS) + && (decoded_payload != NULL) && (decoded_payload->unique_identifier != NULL) + && (decoded_payload->unique_identifier->size == uuid.size) + && (memcmp (decoded_payload->unique_identifier->value, uuid.value, uuid.size) == 0); + } + + result = report_decoding_test_result (tracker, &decode_ctx, comparison, result, __func__); + + kmip_free_response_batch_item (&decode_ctx, &observed); + kmip_destroy (&decode_ctx); + kmip_destroy (&encode_ctx); + return (result); +} + int test_encode_request_message_get (TestTracker *tracker) { @@ -9788,6 +9987,7 @@ test_encode_query_request_payload (TestTracker *tracker) int result = kmip_encode_query_request_payload (&ctx, &qrp); result = report_encoding_test_result (tracker, &ctx, expected, observed, result, __func__); + kmip_destroy (&ctx); return (result); } @@ -10355,9 +10555,11 @@ run_tests (void) test_decode_create_response_payload_with_template_attribute (&tracker); test_decode_get_request_payload (&tracker); test_decode_get_response_payload (&tracker); + test_decode_get_response_payload_secret_data (&tracker); test_decode_destroy_request_payload (&tracker); test_decode_destroy_response_payload (&tracker); test_decode_response_batch_item_get_payload (&tracker); + test_decode_response_batch_item_revoke_payload (&tracker); test_decode_username_password_credential (&tracker); test_decode_credential_username_password_credential (&tracker); test_decode_authentication_username_password_credential (&tracker);