Add crypto token exchange test
Introduced a test that checks if all crypto tokens send by a node are received correctly by the remote node. To support this test, the crypto wrapper plugin is extended with a token_log mode, that stores all tokens that are exchanged after the security handshake is finished. Signed-off-by: Dennis Potman <dennis.potman@adlinktech.com>
This commit is contained in:
parent
82af49172d
commit
fd27604a26
7 changed files with 412 additions and 107 deletions
|
@ -34,7 +34,6 @@
|
||||||
#include "common/security_config_test_utils.h"
|
#include "common/security_config_test_utils.h"
|
||||||
#include "common/test_identity.h"
|
#include "common/test_identity.h"
|
||||||
#include "common/cert_utils.h"
|
#include "common/cert_utils.h"
|
||||||
#include "common/security_config_test_utils.h"
|
|
||||||
|
|
||||||
#define ID1 TEST_IDENTITY1_CERTIFICATE
|
#define ID1 TEST_IDENTITY1_CERTIFICATE
|
||||||
#define ID1K TEST_IDENTITY1_PRIVATE_KEY
|
#define ID1K TEST_IDENTITY1_PRIVATE_KEY
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include "CUnit/Test.h"
|
#include "CUnit/Test.h"
|
||||||
#include "dds/dds.h"
|
#include "dds/dds.h"
|
||||||
|
#include "dds/ddsrt/circlist.h"
|
||||||
#include "dds/ddsrt/heap.h"
|
#include "dds/ddsrt/heap.h"
|
||||||
#include "dds/ddsrt/sync.h"
|
#include "dds/ddsrt/sync.h"
|
||||||
#include "dds/ddsrt/string.h"
|
#include "dds/ddsrt/string.h"
|
||||||
|
@ -22,13 +23,17 @@
|
||||||
#include "dds/security/core/dds_security_utils.h"
|
#include "dds/security/core/dds_security_utils.h"
|
||||||
#include "cryptography_wrapper.h"
|
#include "cryptography_wrapper.h"
|
||||||
|
|
||||||
int init_crypto(const char *argument, void **context, struct ddsi_domaingv *gv);
|
#define CRYPTO_TOKEN_CLASS_ID "DDS:Crypto:AES_GCM_GMAC"
|
||||||
int finalize_crypto(void *context);
|
#define CRYPTO_TOKEN_PROPERTY_NAME "dds.cryp.keymat"
|
||||||
|
|
||||||
|
int32_t init_crypto(const char *argument, void **context, struct ddsi_domaingv *gv);
|
||||||
|
int32_t finalize_crypto(void *context);
|
||||||
|
|
||||||
enum crypto_plugin_mode {
|
enum crypto_plugin_mode {
|
||||||
PLUGIN_MODE_ALL_OK,
|
PLUGIN_MODE_ALL_OK,
|
||||||
PLUGIN_MODE_MISSING_FUNC,
|
PLUGIN_MODE_MISSING_FUNC,
|
||||||
PLUGIN_MODE_WRAPPED
|
PLUGIN_MODE_WRAPPED,
|
||||||
|
PLUGIN_MODE_TOKEN_LOG
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dds_security_crypto_key_exchange_impl {
|
struct dds_security_crypto_key_exchange_impl {
|
||||||
|
@ -67,9 +72,13 @@ struct dds_security_cryptography_impl {
|
||||||
const char * groupdata_secret;
|
const char * groupdata_secret;
|
||||||
const char * ep_secret;
|
const char * ep_secret;
|
||||||
const char * encrypted_secret;
|
const char * encrypted_secret;
|
||||||
|
ddsrt_mutex_t token_data_lock;
|
||||||
|
struct ddsrt_circlist token_data_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
static DDS_Security_ParticipantCryptoHandle g_local_participant_handle = 0;
|
static DDS_Security_ParticipantCryptoHandle g_local_participant_handle = 0;
|
||||||
|
static ddsrt_once_t lock_inited = DDSRT_ONCE_INIT;
|
||||||
|
static ddsrt_mutex_t g_print_token_lock;
|
||||||
|
|
||||||
void set_protection_kinds(
|
void set_protection_kinds(
|
||||||
struct dds_security_cryptography_impl * impl,
|
struct dds_security_cryptography_impl * impl,
|
||||||
|
@ -109,6 +118,144 @@ void set_entity_data_secret(struct dds_security_cryptography_impl * impl, const
|
||||||
impl->ep_secret = ep_secret;
|
impl->ep_secret = ep_secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool check_crypto_tokens(const DDS_Security_DataHolderSeq *tokens)
|
||||||
|
{
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
if (tokens->_length == 0 || tokens->_buffer == NULL)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (uint32_t i = 0; result && (i < tokens->_length); i++)
|
||||||
|
{
|
||||||
|
result = (tokens->_buffer[i].class_id != NULL &&
|
||||||
|
strcmp(CRYPTO_TOKEN_CLASS_ID, tokens->_buffer[i].class_id) == 0 &&
|
||||||
|
tokens->_buffer[i].binary_properties._length == 1 &&
|
||||||
|
tokens->_buffer[i].binary_properties._buffer != NULL &&
|
||||||
|
tokens->_buffer[i].binary_properties._buffer[0].name != NULL &&
|
||||||
|
strcmp(CRYPTO_TOKEN_PROPERTY_NAME, tokens->_buffer[i].binary_properties._buffer[0].name) == 0 &&
|
||||||
|
tokens->_buffer[i].binary_properties._buffer[0].value._length > 0 &&
|
||||||
|
tokens->_buffer[i].binary_properties._buffer[0].value._buffer != NULL);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *get_crypto_token_type_str (enum crypto_tokens_type type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case LOCAL_PARTICIPANT_TOKENS: return "LOCAL_PARTICIPANT_TOKENS";
|
||||||
|
case LOCAL_WRITER_TOKENS: return "LOCAL_WRITER_TOKENS";
|
||||||
|
case LOCAL_READER_TOKENS: return "LOCAL_READER_TOKENS";
|
||||||
|
case REMOTE_PARTICIPANT_TOKENS: return "REMOTE_PARTICIPANT_TOKENS";
|
||||||
|
case REMOTE_WRITER_TOKENS: return "REMOTE_WRITER_TOKENS";
|
||||||
|
case REMOTE_READER_TOKENS: return "REMOTE_READER_TOKENS";
|
||||||
|
default: assert (0); return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_tokens (enum crypto_tokens_type type, const DDS_Security_ParticipantCryptoHandle lch, const DDS_Security_ParticipantCryptoHandle rch,
|
||||||
|
const DDS_Security_ParticipantCryptoTokenSeq *tokens)
|
||||||
|
{
|
||||||
|
ddsrt_mutex_lock (&g_print_token_lock);
|
||||||
|
printf ("Token type %s, local %"PRIx64" / remote %"PRIx64", count: %u\n", get_crypto_token_type_str (type), lch, rch, tokens->_length);
|
||||||
|
for (uint32_t i = 0; i < tokens->_length; i++)
|
||||||
|
{
|
||||||
|
printf ("- token: ");
|
||||||
|
for (uint32_t j = 0; j < tokens->_buffer[i].binary_properties._buffer[0].value._length && j < 32; j++)
|
||||||
|
printf ("%02x", tokens->_buffer[i].binary_properties._buffer[0].value._buffer[j]);
|
||||||
|
printf ("\n");
|
||||||
|
}
|
||||||
|
printf ("\n");
|
||||||
|
ddsrt_mutex_unlock (&g_print_token_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_tokens (struct ddsrt_circlist *list, enum crypto_tokens_type type,
|
||||||
|
const DDS_Security_ParticipantCryptoHandle lch, const DDS_Security_ParticipantCryptoHandle rch,
|
||||||
|
const DDS_Security_ParticipantCryptoTokenSeq *tokens)
|
||||||
|
{
|
||||||
|
struct crypto_token_data *token_data = ddsrt_malloc (sizeof (*token_data));
|
||||||
|
token_data->type = type;
|
||||||
|
token_data->local_handle = lch;
|
||||||
|
token_data->remote_handle = rch;
|
||||||
|
token_data->n_tokens = tokens->_length;
|
||||||
|
assert (tokens->_length <= CRYPTO_TOKEN_MAXLEN);
|
||||||
|
for (uint32_t i = 0; i < tokens->_length; i++)
|
||||||
|
{
|
||||||
|
size_t len = tokens->_buffer[i].binary_properties._buffer[0].value._length;
|
||||||
|
assert (len <= CRYPTO_TOKEN_SIZE);
|
||||||
|
memcpy (token_data->data[i], tokens->_buffer[i].binary_properties._buffer[0].value._buffer, len);
|
||||||
|
token_data->data_len[i] = len;
|
||||||
|
}
|
||||||
|
ddsrt_circlist_append(list, &token_data->e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void store_tokens (struct dds_security_crypto_key_exchange_impl *impl, enum crypto_tokens_type type, const DDS_Security_ParticipantCryptoHandle lch,
|
||||||
|
const DDS_Security_ParticipantCryptoHandle rch, const DDS_Security_ParticipantCryptoTokenSeq *tokens)
|
||||||
|
{
|
||||||
|
if (!check_crypto_tokens ((const DDS_Security_DataHolderSeq *) tokens))
|
||||||
|
{
|
||||||
|
printf ("%d ERROR\n", type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ddsrt_mutex_lock (&impl->parent->token_data_lock);
|
||||||
|
add_tokens (&impl->parent->token_data_list, type, lch, rch, tokens);
|
||||||
|
ddsrt_mutex_unlock (&impl->parent->token_data_lock);
|
||||||
|
|
||||||
|
print_tokens (type, lch, rch, tokens);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ddsrt_circlist * get_crypto_tokens (struct dds_security_cryptography_impl * impl)
|
||||||
|
{
|
||||||
|
struct ddsrt_circlist *tokens = ddsrt_malloc (sizeof (*tokens));
|
||||||
|
ddsrt_circlist_init (tokens);
|
||||||
|
|
||||||
|
ddsrt_mutex_lock (&impl->token_data_lock);
|
||||||
|
struct ddsrt_circlist_elem *elem0 = ddsrt_circlist_oldest (&impl->token_data_list), *elem = elem0;
|
||||||
|
while (elem != NULL)
|
||||||
|
{
|
||||||
|
struct crypto_token_data *elem_data = DDSRT_FROM_CIRCLIST (struct crypto_token_data, e, elem);
|
||||||
|
struct crypto_token_data *token_data = ddsrt_malloc (sizeof (*token_data));
|
||||||
|
memcpy (token_data, elem_data, sizeof (*token_data));
|
||||||
|
ddsrt_circlist_append (tokens, &token_data->e);
|
||||||
|
elem = elem->next;
|
||||||
|
if (elem == elem0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ddsrt_mutex_unlock (&impl->token_data_lock);
|
||||||
|
|
||||||
|
return tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct crypto_token_data * find_crypto_token (struct dds_security_cryptography_impl * impl, enum crypto_tokens_type type, unsigned char * data, size_t data_len)
|
||||||
|
{
|
||||||
|
assert (data_len <= CRYPTO_TOKEN_SIZE);
|
||||||
|
ddsrt_mutex_lock (&impl->token_data_lock);
|
||||||
|
struct ddsrt_circlist_elem *elem0 = ddsrt_circlist_oldest (&impl->token_data_list), *elem = elem0;
|
||||||
|
while (elem != NULL)
|
||||||
|
{
|
||||||
|
struct crypto_token_data *elem_data = DDSRT_FROM_CIRCLIST (struct crypto_token_data, e, elem);
|
||||||
|
if (elem_data->type == type)
|
||||||
|
{
|
||||||
|
for (uint32_t i = 0; i < elem_data->n_tokens; i++)
|
||||||
|
{
|
||||||
|
size_t len = elem_data->data_len[i];
|
||||||
|
assert (len <= CRYPTO_TOKEN_SIZE);
|
||||||
|
if (!memcmp (data, elem_data->data[i], data_len < len ? data_len : len))
|
||||||
|
{
|
||||||
|
ddsrt_mutex_unlock (&impl->token_data_lock);
|
||||||
|
return elem_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
elem = elem->next;
|
||||||
|
if (elem == elem0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ddsrt_mutex_unlock (&impl->token_data_lock);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static unsigned char * find_buffer_match(const unsigned char *input, size_t input_len, const unsigned char *match, size_t match_len)
|
static unsigned char * find_buffer_match(const unsigned char *input, size_t input_len, const unsigned char *match, size_t match_len)
|
||||||
{
|
{
|
||||||
if (match_len <= input_len && match_len > 0 && input_len > 0)
|
if (match_len <= input_len && match_len > 0 && input_len > 0)
|
||||||
|
@ -171,8 +318,14 @@ static DDS_Security_boolean create_local_participant_crypto_tokens(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
return impl->instance->create_local_participant_crypto_tokens (impl->instance, local_participant_crypto_tokens,
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
|
{
|
||||||
|
DDS_Security_boolean ret = impl->instance->create_local_participant_crypto_tokens (impl->instance, local_participant_crypto_tokens,
|
||||||
local_participant_crypto, remote_participant_crypto, ex);
|
local_participant_crypto, remote_participant_crypto, ex);
|
||||||
|
if (ret && impl->parent->mode == PLUGIN_MODE_TOKEN_LOG)
|
||||||
|
store_tokens (impl, LOCAL_PARTICIPANT_TOKENS, local_participant_crypto, remote_participant_crypto, local_participant_crypto_tokens);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -189,8 +342,14 @@ static DDS_Security_boolean set_remote_participant_crypto_tokens(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
return impl->instance->set_remote_participant_crypto_tokens (impl->instance, check_handle (local_participant_crypto),
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
|
{
|
||||||
|
DDS_Security_boolean ret = impl->instance->set_remote_participant_crypto_tokens (impl->instance, check_handle (local_participant_crypto),
|
||||||
check_handle (remote_participant_crypto), remote_participant_tokens, ex);
|
check_handle (remote_participant_crypto), remote_participant_tokens, ex);
|
||||||
|
if (ret && impl->parent->mode == PLUGIN_MODE_TOKEN_LOG)
|
||||||
|
store_tokens (impl, REMOTE_PARTICIPANT_TOKENS, local_participant_crypto, remote_participant_crypto, remote_participant_tokens);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -207,8 +366,14 @@ static DDS_Security_boolean create_local_datawriter_crypto_tokens(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
return impl->instance->create_local_datawriter_crypto_tokens (impl->instance, local_datawriter_crypto_tokens,
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
|
{
|
||||||
|
DDS_Security_boolean ret = impl->instance->create_local_datawriter_crypto_tokens (impl->instance, local_datawriter_crypto_tokens,
|
||||||
check_handle (local_datawriter_crypto), check_handle (remote_datareader_crypto), ex);
|
check_handle (local_datawriter_crypto), check_handle (remote_datareader_crypto), ex);
|
||||||
|
if (ret && impl->parent->mode == PLUGIN_MODE_TOKEN_LOG)
|
||||||
|
store_tokens (impl, LOCAL_WRITER_TOKENS, local_datawriter_crypto, remote_datareader_crypto, local_datawriter_crypto_tokens);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -225,8 +390,14 @@ static DDS_Security_boolean set_remote_datawriter_crypto_tokens(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
return impl->instance->set_remote_datawriter_crypto_tokens (impl->instance, check_handle (local_datareader_crypto),
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
|
{
|
||||||
|
DDS_Security_boolean ret = impl->instance->set_remote_datawriter_crypto_tokens (impl->instance, check_handle (local_datareader_crypto),
|
||||||
check_handle (remote_datawriter_crypto), remote_datawriter_tokens, ex);
|
check_handle (remote_datawriter_crypto), remote_datawriter_tokens, ex);
|
||||||
|
if (ret && impl->parent->mode == PLUGIN_MODE_TOKEN_LOG)
|
||||||
|
store_tokens (impl, REMOTE_WRITER_TOKENS, local_datareader_crypto, remote_datawriter_crypto, remote_datawriter_tokens);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -243,8 +414,14 @@ static DDS_Security_boolean create_local_datareader_crypto_tokens(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
return impl->instance->create_local_datareader_crypto_tokens (impl->instance, local_datareader_cryto_tokens,
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
|
{
|
||||||
|
DDS_Security_boolean ret = impl->instance->create_local_datareader_crypto_tokens (impl->instance, local_datareader_cryto_tokens,
|
||||||
check_handle (local_datareader_crypto), check_handle (remote_datawriter_crypto), ex);
|
check_handle (local_datareader_crypto), check_handle (remote_datawriter_crypto), ex);
|
||||||
|
if (ret && impl->parent->mode == PLUGIN_MODE_TOKEN_LOG)
|
||||||
|
store_tokens (impl, LOCAL_READER_TOKENS, local_datareader_crypto, remote_datawriter_crypto, local_datareader_cryto_tokens);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -261,8 +438,14 @@ static DDS_Security_boolean set_remote_datareader_crypto_tokens(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
return impl->instance->set_remote_datareader_crypto_tokens (impl->instance, check_handle (local_datawriter_crypto),
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
|
{
|
||||||
|
DDS_Security_boolean ret = impl->instance->set_remote_datareader_crypto_tokens (impl->instance, check_handle (local_datawriter_crypto),
|
||||||
check_handle (remote_datareader_crypto), remote_datareader_tokens, ex);
|
check_handle (remote_datareader_crypto), remote_datareader_tokens, ex);
|
||||||
|
if (ret && impl->parent->mode == PLUGIN_MODE_TOKEN_LOG)
|
||||||
|
store_tokens (impl, REMOTE_READER_TOKENS, local_datawriter_crypto, remote_datareader_crypto, remote_datareader_tokens);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -277,6 +460,7 @@ static DDS_Security_boolean return_crypto_tokens(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->return_crypto_tokens (impl->instance, crypto_tokens, ex);
|
return impl->instance->return_crypto_tokens (impl->instance, crypto_tokens, ex);
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
|
@ -298,6 +482,7 @@ static DDS_Security_ParticipantCryptoHandle register_local_participant(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return check_handle (impl->instance->register_local_participant (impl->instance, check_handle (participant_identity),
|
return check_handle (impl->instance->register_local_participant (impl->instance, check_handle (participant_identity),
|
||||||
check_handle (participant_permissions), participant_properties, participant_security_attributes, ex));
|
check_handle (participant_permissions), participant_properties, participant_security_attributes, ex));
|
||||||
default:
|
default:
|
||||||
|
@ -317,6 +502,7 @@ static DDS_Security_ParticipantCryptoHandle register_matched_remote_participant(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return check_handle (impl->instance->register_matched_remote_participant (impl->instance, local_participant_crypto_handle,
|
return check_handle (impl->instance->register_matched_remote_participant (impl->instance, local_participant_crypto_handle,
|
||||||
remote_participant_identity, remote_participant_permissions, shared_secret, ex));
|
remote_participant_identity, remote_participant_permissions, shared_secret, ex));
|
||||||
default:
|
default:
|
||||||
|
@ -335,6 +521,7 @@ static DDS_Security_DatawriterCryptoHandle register_local_datawriter(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return check_handle (impl->instance->register_local_datawriter (impl->instance, check_handle (participant_crypto),
|
return check_handle (impl->instance->register_local_datawriter (impl->instance, check_handle (participant_crypto),
|
||||||
datawriter_properties, datawriter_security_attributes, ex));
|
datawriter_properties, datawriter_security_attributes, ex));
|
||||||
default:
|
default:
|
||||||
|
@ -354,6 +541,7 @@ static DDS_Security_DatareaderCryptoHandle register_matched_remote_datareader(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return check_handle (impl->instance->register_matched_remote_datareader (impl->instance, check_handle (local_datawriter_crypto_handle),
|
return check_handle (impl->instance->register_matched_remote_datareader (impl->instance, check_handle (local_datawriter_crypto_handle),
|
||||||
check_handle (remote_participant_crypto), check_handle (shared_secret), relay_only, ex));
|
check_handle (remote_participant_crypto), check_handle (shared_secret), relay_only, ex));
|
||||||
default:
|
default:
|
||||||
|
@ -372,6 +560,7 @@ static DDS_Security_DatareaderCryptoHandle register_local_datareader(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return check_handle (impl->instance->register_local_datareader (impl->instance, check_handle (participant_crypto),
|
return check_handle (impl->instance->register_local_datareader (impl->instance, check_handle (participant_crypto),
|
||||||
datareader_properties, datareader_security_attributes, ex));
|
datareader_properties, datareader_security_attributes, ex));
|
||||||
default:
|
default:
|
||||||
|
@ -390,6 +579,7 @@ static DDS_Security_DatawriterCryptoHandle register_matched_remote_datawriter(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return check_handle (impl->instance->register_matched_remote_datawriter (impl->instance, check_handle (local_datareader_crypto_handle),
|
return check_handle (impl->instance->register_matched_remote_datawriter (impl->instance, check_handle (local_datareader_crypto_handle),
|
||||||
check_handle (remote_participant_crypt), shared_secret, ex));
|
check_handle (remote_participant_crypt), shared_secret, ex));
|
||||||
default:
|
default:
|
||||||
|
@ -406,6 +596,7 @@ static DDS_Security_boolean unregister_participant(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->unregister_participant (impl->instance, check_handle (participant_crypto_handle), ex);
|
return impl->instance->unregister_participant (impl->instance, check_handle (participant_crypto_handle), ex);
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
|
@ -421,6 +612,7 @@ static DDS_Security_boolean unregister_datawriter(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->unregister_datawriter (impl->instance, check_handle (datawriter_crypto_handle), ex);
|
return impl->instance->unregister_datawriter (impl->instance, check_handle (datawriter_crypto_handle), ex);
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
|
@ -436,6 +628,7 @@ static DDS_Security_boolean unregister_datareader(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->unregister_datareader (impl->instance, check_handle (datareader_crypto_handle), ex);
|
return impl->instance->unregister_datareader (impl->instance, check_handle (datareader_crypto_handle), ex);
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
|
@ -457,6 +650,7 @@ static DDS_Security_boolean encode_serialized_payload(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
{
|
{
|
||||||
if (!impl->instance->encode_serialized_payload (impl->instance, encoded_buffer,
|
if (!impl->instance->encode_serialized_payload (impl->instance, encoded_buffer,
|
||||||
extra_inline_qos, plain_buffer, check_handle (sending_datawriter_crypto), ex))
|
extra_inline_qos, plain_buffer, check_handle (sending_datawriter_crypto), ex))
|
||||||
|
@ -511,6 +705,7 @@ static DDS_Security_boolean encode_datawriter_submessage(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
if (!impl->instance->encode_datawriter_submessage (impl->instance, encoded_rtps_submessage,
|
if (!impl->instance->encode_datawriter_submessage (impl->instance, encoded_rtps_submessage,
|
||||||
plain_rtps_submessage, check_handle (sending_datawriter_crypto), receiving_datareader_crypto_list, receiving_datareader_crypto_list_index, ex))
|
plain_rtps_submessage, check_handle (sending_datawriter_crypto), receiving_datareader_crypto_list, receiving_datareader_crypto_list_index, ex))
|
||||||
return false;
|
return false;
|
||||||
|
@ -558,6 +753,7 @@ static DDS_Security_boolean encode_datareader_submessage(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
if (!impl->instance->encode_datareader_submessage (impl->instance, encoded_rtps_submessage,
|
if (!impl->instance->encode_datareader_submessage (impl->instance, encoded_rtps_submessage,
|
||||||
plain_rtps_submessage, check_handle (sending_datareader_crypto), receiving_datawriter_crypto_list, ex))
|
plain_rtps_submessage, check_handle (sending_datareader_crypto), receiving_datawriter_crypto_list, ex))
|
||||||
return false;
|
return false;
|
||||||
|
@ -580,6 +776,7 @@ static DDS_Security_boolean encode_rtps_message(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
if (!impl->instance->encode_rtps_message (impl->instance, encoded_rtps_message,
|
if (!impl->instance->encode_rtps_message (impl->instance, encoded_rtps_message,
|
||||||
plain_rtps_message, check_handle (sending_participant_crypto), receiving_participant_crypto_list, receiving_participant_crypto_list_index, ex))
|
plain_rtps_message, check_handle (sending_participant_crypto), receiving_participant_crypto_list, receiving_participant_crypto_list_index, ex))
|
||||||
return false;
|
return false;
|
||||||
|
@ -611,6 +808,7 @@ static DDS_Security_boolean decode_rtps_message(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->decode_rtps_message (impl->instance, plain_buffer, encoded_buffer,
|
return impl->instance->decode_rtps_message (impl->instance, plain_buffer, encoded_buffer,
|
||||||
check_handle (receiving_participant_crypto), check_handle (sending_participant_crypto), ex);
|
check_handle (receiving_participant_crypto), check_handle (sending_participant_crypto), ex);
|
||||||
default:
|
default:
|
||||||
|
@ -632,6 +830,7 @@ static DDS_Security_boolean preprocess_secure_submsg(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->preprocess_secure_submsg (impl->instance, datawriter_crypto, datareader_crypto,
|
return impl->instance->preprocess_secure_submsg (impl->instance, datawriter_crypto, datareader_crypto,
|
||||||
secure_submessage_category, encoded_rtps_submessage, check_handle (receiving_participant_crypto), check_handle (sending_participant_crypto), ex);
|
secure_submessage_category, encoded_rtps_submessage, check_handle (receiving_participant_crypto), check_handle (sending_participant_crypto), ex);
|
||||||
default:
|
default:
|
||||||
|
@ -651,6 +850,7 @@ static DDS_Security_boolean decode_datawriter_submessage(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->decode_datawriter_submessage (impl->instance, plain_rtps_submessage,
|
return impl->instance->decode_datawriter_submessage (impl->instance, plain_rtps_submessage,
|
||||||
encoded_rtps_submessage, check_handle (receiving_datareader_crypto), check_handle (sending_datawriter_crypto), ex);
|
encoded_rtps_submessage, check_handle (receiving_datareader_crypto), check_handle (sending_datawriter_crypto), ex);
|
||||||
default:
|
default:
|
||||||
|
@ -670,6 +870,7 @@ static DDS_Security_boolean decode_datareader_submessage(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->decode_datareader_submessage (impl->instance, plain_rtps_submessage,
|
return impl->instance->decode_datareader_submessage (impl->instance, plain_rtps_submessage,
|
||||||
encoded_rtps_submessage, check_handle (receiving_datawriter_crypto), check_handle (sending_datareader_crypto), ex);
|
encoded_rtps_submessage, check_handle (receiving_datawriter_crypto), check_handle (sending_datareader_crypto), ex);
|
||||||
default:
|
default:
|
||||||
|
@ -690,6 +891,7 @@ static DDS_Security_boolean decode_serialized_payload(
|
||||||
switch (impl->parent->mode)
|
switch (impl->parent->mode)
|
||||||
{
|
{
|
||||||
case PLUGIN_MODE_WRAPPED:
|
case PLUGIN_MODE_WRAPPED:
|
||||||
|
case PLUGIN_MODE_TOKEN_LOG:
|
||||||
return impl->instance->decode_serialized_payload(impl->instance, plain_buffer, encoded_buffer,
|
return impl->instance->decode_serialized_payload(impl->instance, plain_buffer, encoded_buffer,
|
||||||
inline_qos, check_handle (receiving_datareader_crypto), check_handle (sending_datawriter_crypto), ex);
|
inline_qos, check_handle (receiving_datareader_crypto), check_handle (sending_datawriter_crypto), ex);
|
||||||
default:
|
default:
|
||||||
|
@ -815,3 +1017,41 @@ int finalize_test_cryptography_wrapped(void *context)
|
||||||
return finalize_test_cryptography_common(impl, true);
|
return finalize_test_cryptography_common(impl, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void init_print_lock(void)
|
||||||
|
{
|
||||||
|
ddsrt_mutex_init (&g_print_token_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t init_test_cryptography_store_tokens(const char *argument, void **context, struct ddsi_domaingv *gv)
|
||||||
|
{
|
||||||
|
struct dds_security_cryptography_impl *impl = init_test_cryptography_common(argument, true, gv);
|
||||||
|
if (!impl)
|
||||||
|
return DDS_SECURITY_FAILED;
|
||||||
|
impl->mode = PLUGIN_MODE_TOKEN_LOG;
|
||||||
|
ddsrt_once (&lock_inited, &init_print_lock);
|
||||||
|
ddsrt_mutex_init (&impl->token_data_lock);
|
||||||
|
ddsrt_circlist_init (&impl->token_data_list);
|
||||||
|
*context = impl;
|
||||||
|
return DDS_SECURITY_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t finalize_test_cryptography_store_tokens(void *context)
|
||||||
|
{
|
||||||
|
struct dds_security_cryptography_impl* impl = (struct dds_security_cryptography_impl*) context;
|
||||||
|
assert(impl->mode == PLUGIN_MODE_TOKEN_LOG);
|
||||||
|
ddsrt_mutex_lock (&impl->token_data_lock);
|
||||||
|
while (!ddsrt_circlist_isempty (&impl->token_data_list))
|
||||||
|
{
|
||||||
|
struct ddsrt_circlist_elem *list_elem = ddsrt_circlist_oldest (&impl->token_data_list);
|
||||||
|
struct crypto_token_data *token_data = DDSRT_FROM_CIRCLIST (struct crypto_token_data, e, list_elem);
|
||||||
|
ddsrt_circlist_remove (&impl->token_data_list, list_elem);
|
||||||
|
ddsrt_free (token_data);
|
||||||
|
}
|
||||||
|
ddsrt_mutex_unlock (&impl->token_data_lock);
|
||||||
|
ddsrt_mutex_destroy (&impl->token_data_lock);
|
||||||
|
/* don't detroy g_print_token_lock as this will result in multiple
|
||||||
|
calls to mutex_destroy for this lock in case of multiple domains */
|
||||||
|
|
||||||
|
return finalize_test_cryptography_common(impl, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,37 @@
|
||||||
#ifndef SECURITY_CORE_TEST_CRYPTO_WRAPPER_H_
|
#ifndef SECURITY_CORE_TEST_CRYPTO_WRAPPER_H_
|
||||||
#define SECURITY_CORE_TEST_CRYPTO_WRAPPER_H_
|
#define SECURITY_CORE_TEST_CRYPTO_WRAPPER_H_
|
||||||
|
|
||||||
|
#include "dds/ddsrt/circlist.h"
|
||||||
#include "dds/ddsi/ddsi_domaingv.h"
|
#include "dds/ddsi/ddsi_domaingv.h"
|
||||||
#include "dds/security/dds_security_api.h"
|
#include "dds/security/dds_security_api.h"
|
||||||
#include "dds/security/dds_security_api_defs.h"
|
#include "dds/security/dds_security_api_defs.h"
|
||||||
#include "dds/security/cryptography_wrapper_export.h"
|
#include "dds/security/cryptography_wrapper_export.h"
|
||||||
|
|
||||||
|
#define CRYPTO_TOKEN_MAXLEN 10
|
||||||
|
#define CRYPTO_TOKEN_SIZE 256
|
||||||
|
|
||||||
struct dds_security_cryptography_impl;
|
struct dds_security_cryptography_impl;
|
||||||
|
|
||||||
|
enum crypto_tokens_type {
|
||||||
|
LOCAL_PARTICIPANT_TOKENS,
|
||||||
|
LOCAL_WRITER_TOKENS,
|
||||||
|
LOCAL_READER_TOKENS,
|
||||||
|
REMOTE_PARTICIPANT_TOKENS,
|
||||||
|
REMOTE_WRITER_TOKENS,
|
||||||
|
REMOTE_READER_TOKENS,
|
||||||
|
TOKEN_TYPE_INVALID
|
||||||
|
};
|
||||||
|
|
||||||
|
struct crypto_token_data {
|
||||||
|
struct ddsrt_circlist_elem e;
|
||||||
|
enum crypto_tokens_type type;
|
||||||
|
DDS_Security_ParticipantCryptoHandle local_handle;
|
||||||
|
DDS_Security_ParticipantCryptoHandle remote_handle;
|
||||||
|
uint32_t n_tokens;
|
||||||
|
unsigned char data[CRYPTO_TOKEN_MAXLEN][CRYPTO_TOKEN_SIZE];
|
||||||
|
size_t data_len[CRYPTO_TOKEN_MAXLEN];
|
||||||
|
};
|
||||||
|
|
||||||
SECURITY_EXPORT void set_protection_kinds(
|
SECURITY_EXPORT void set_protection_kinds(
|
||||||
struct dds_security_cryptography_impl * impl,
|
struct dds_security_cryptography_impl * impl,
|
||||||
DDS_Security_ProtectionKind rtps_protection_kind,
|
DDS_Security_ProtectionKind rtps_protection_kind,
|
||||||
|
@ -34,6 +58,10 @@ SECURITY_EXPORT void set_disc_protection_kinds(
|
||||||
|
|
||||||
SECURITY_EXPORT void set_entity_data_secret(struct dds_security_cryptography_impl * impl, const char * pp_secret, const char * groupdata_secret, const char * ep_secret);
|
SECURITY_EXPORT void set_entity_data_secret(struct dds_security_cryptography_impl * impl, const char * pp_secret, const char * groupdata_secret, const char * ep_secret);
|
||||||
|
|
||||||
|
SECURITY_EXPORT const char *get_crypto_token_type_str (enum crypto_tokens_type type);
|
||||||
|
SECURITY_EXPORT struct ddsrt_circlist * get_crypto_tokens (struct dds_security_cryptography_impl * impl);
|
||||||
|
SECURITY_EXPORT struct crypto_token_data * find_crypto_token (struct dds_security_cryptography_impl * impl, enum crypto_tokens_type type, unsigned char * data, size_t data_len);
|
||||||
|
|
||||||
/* Init in all-ok mode: all functions return success without calling the actual plugin */
|
/* Init in all-ok mode: all functions return success without calling the actual plugin */
|
||||||
SECURITY_EXPORT int init_test_cryptography_all_ok(const char *argument, void **context, struct ddsi_domaingv *gv);
|
SECURITY_EXPORT int init_test_cryptography_all_ok(const char *argument, void **context, struct ddsi_domaingv *gv);
|
||||||
SECURITY_EXPORT int finalize_test_cryptography_all_ok(void *context);
|
SECURITY_EXPORT int finalize_test_cryptography_all_ok(void *context);
|
||||||
|
@ -46,4 +74,8 @@ SECURITY_EXPORT int finalize_test_cryptography_missing_func(void *context);
|
||||||
SECURITY_EXPORT int init_test_cryptography_wrapped(const char *argument, void **context, struct ddsi_domaingv *gv);
|
SECURITY_EXPORT int init_test_cryptography_wrapped(const char *argument, void **context, struct ddsi_domaingv *gv);
|
||||||
SECURITY_EXPORT int finalize_test_cryptography_wrapped(void *context);
|
SECURITY_EXPORT int finalize_test_cryptography_wrapped(void *context);
|
||||||
|
|
||||||
|
/* Init in store-token mode (stores all exchanged security tokens) */
|
||||||
|
SECURITY_EXPORT int32_t init_test_cryptography_store_tokens(const char *argument, void **context, struct ddsi_domaingv *gv);
|
||||||
|
SECURITY_EXPORT int32_t finalize_test_cryptography_store_tokens(void *context);
|
||||||
|
|
||||||
#endif /* SECURITY_CORE_TEST_CRYPTO_WRAPPER_H_ */
|
#endif /* SECURITY_CORE_TEST_CRYPTO_WRAPPER_H_ */
|
||||||
|
|
|
@ -18,6 +18,10 @@
|
||||||
#include "dds/ddsrt/string.h"
|
#include "dds/ddsrt/string.h"
|
||||||
#include "dds/ddsrt/threads.h"
|
#include "dds/ddsrt/threads.h"
|
||||||
#include "dds/ddsrt/heap.h"
|
#include "dds/ddsrt/heap.h"
|
||||||
|
#include "dds/ddsi/q_entity.h"
|
||||||
|
#include "dds/ddsi/ddsi_entity_index.h"
|
||||||
|
#include "dds/ddsi/ddsi_security_omg.h"
|
||||||
|
#include "dds__entity.h"
|
||||||
#include "dds/security/dds_security_api.h"
|
#include "dds/security/dds_security_api.h"
|
||||||
#include "authentication_wrapper.h"
|
#include "authentication_wrapper.h"
|
||||||
#include "test_utils.h"
|
#include "test_utils.h"
|
||||||
|
@ -449,3 +453,21 @@ void write_read_for(dds_entity_t wr, dds_entity_t pp_rd, dds_entity_t rd, dds_du
|
||||||
CU_ASSERT_EQUAL_FATAL (write_fail, exp_write_fail);
|
CU_ASSERT_EQUAL_FATAL (write_fail, exp_write_fail);
|
||||||
CU_ASSERT_EQUAL_FATAL (read_fail, exp_read_fail);
|
CU_ASSERT_EQUAL_FATAL (read_fail, exp_read_fail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct dds_security_cryptography_impl * get_crypto_context(dds_entity_t participant)
|
||||||
|
{
|
||||||
|
struct dds_entity *pp_entity = NULL;
|
||||||
|
struct participant *pp;
|
||||||
|
struct dds_security_cryptography_impl *context;
|
||||||
|
dds_return_t ret;
|
||||||
|
|
||||||
|
ret = dds_entity_lock (participant, DDS_KIND_PARTICIPANT, &pp_entity);
|
||||||
|
CU_ASSERT_EQUAL_FATAL (ret, 0);
|
||||||
|
thread_state_awake (lookup_thread_state(), &pp_entity->m_domain->gv);
|
||||||
|
pp = entidx_lookup_participant_guid (pp_entity->m_domain->gv.entity_index, &pp_entity->m_guid);
|
||||||
|
CU_ASSERT_FATAL (pp != NULL);
|
||||||
|
context = (struct dds_security_cryptography_impl *) q_omg_participant_get_cryptography (pp);
|
||||||
|
thread_state_asleep (lookup_thread_state ());
|
||||||
|
dds_entity_unlock (pp_entity);
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
|
@ -69,5 +69,6 @@ void rd_wr_init_fail(
|
||||||
bool exp_pubtp_fail, bool exp_wr_fail,
|
bool exp_pubtp_fail, bool exp_wr_fail,
|
||||||
bool exp_subtp_fail, bool exp_rd_fail);
|
bool exp_subtp_fail, bool exp_rd_fail);
|
||||||
void write_read_for(dds_entity_t wr, dds_entity_t pp_rd, dds_entity_t rd, dds_duration_t dur, bool exp_write_fail, bool exp_read_fail);
|
void write_read_for(dds_entity_t wr, dds_entity_t pp_rd, dds_entity_t rd, dds_duration_t dur, bool exp_write_fail, bool exp_read_fail);
|
||||||
|
struct dds_security_cryptography_impl * get_crypto_context(dds_entity_t participant);
|
||||||
|
|
||||||
#endif /* SECURITY_CORE_TEST_UTILS_H_ */
|
#endif /* SECURITY_CORE_TEST_UTILS_H_ */
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "dds/ddsrt/environ.h"
|
#include "dds/ddsrt/environ.h"
|
||||||
#include "dds/ddsrt/heap.h"
|
#include "dds/ddsrt/heap.h"
|
||||||
#include "dds/ddsrt/string.h"
|
#include "dds/ddsrt/string.h"
|
||||||
|
#include "dds/ddsrt/string.h"
|
||||||
#include "dds/ddsi/q_config.h"
|
#include "dds/ddsi/q_config.h"
|
||||||
#include "dds/ddsi/ddsi_domaingv.h"
|
#include "dds/ddsi/ddsi_domaingv.h"
|
||||||
#include "dds/ddsi/q_misc.h"
|
#include "dds/ddsi/q_misc.h"
|
||||||
|
@ -29,9 +30,11 @@
|
||||||
|
|
||||||
#include "common/config_env.h"
|
#include "common/config_env.h"
|
||||||
#include "common/authentication_wrapper.h"
|
#include "common/authentication_wrapper.h"
|
||||||
|
#include "common/cryptography_wrapper.h"
|
||||||
#include "common/plugin_wrapper_msg_q.h"
|
#include "common/plugin_wrapper_msg_q.h"
|
||||||
#include "common/test_utils.h"
|
#include "common/test_utils.h"
|
||||||
#include "common/test_identity.h"
|
#include "common/test_identity.h"
|
||||||
|
#include "common/security_config_test_utils.h"
|
||||||
|
|
||||||
static const char *config =
|
static const char *config =
|
||||||
"${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}"
|
"${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}"
|
||||||
|
@ -42,7 +45,7 @@ static const char *config =
|
||||||
" </Discovery>"
|
" </Discovery>"
|
||||||
" <DDSSecurity>"
|
" <DDSSecurity>"
|
||||||
" <Authentication>"
|
" <Authentication>"
|
||||||
" <Library finalizeFunction=\"finalize_test_authentication_wrapped\" initFunction=\"init_test_authentication_wrapped\" path=\"" WRAPPERLIB_PATH("dds_security_authentication_wrapper") "\"/>"
|
" <Library initFunction=\"${AUTH_INIT}\" finalizeFunction=\"${AUTH_FINI}\" path=\"" WRAPPERLIB_PATH("dds_security_authentication_wrapper") "\"/>"
|
||||||
" <IdentityCertificate>data:,"TEST_IDENTITY1_CERTIFICATE"</IdentityCertificate>"
|
" <IdentityCertificate>data:,"TEST_IDENTITY1_CERTIFICATE"</IdentityCertificate>"
|
||||||
" <PrivateKey>data:,"TEST_IDENTITY1_PRIVATE_KEY"</PrivateKey>"
|
" <PrivateKey>data:,"TEST_IDENTITY1_PRIVATE_KEY"</PrivateKey>"
|
||||||
" <IdentityCA>data:,"TEST_IDENTITY_CA1_CERTIFICATE"</IdentityCA>"
|
" <IdentityCA>data:,"TEST_IDENTITY_CA1_CERTIFICATE"</IdentityCA>"
|
||||||
|
@ -54,43 +57,120 @@ static const char *config =
|
||||||
" <Permissions>file:" COMMON_ETC_PATH("default_permissions.p7s") "</Permissions>"
|
" <Permissions>file:" COMMON_ETC_PATH("default_permissions.p7s") "</Permissions>"
|
||||||
" </AccessControl>"
|
" </AccessControl>"
|
||||||
" <Cryptographic>"
|
" <Cryptographic>"
|
||||||
" <Library finalizeFunction=\"finalize_crypto\" initFunction=\"init_crypto\"/>"
|
" <Library initFunction=\"${CRYPTO_INIT}\" finalizeFunction=\"${CRYPTO_FINI}\" path=\"" WRAPPERLIB_PATH("dds_security_cryptography_wrapper") "\"/>"
|
||||||
" </Cryptographic>"
|
" </Cryptographic>"
|
||||||
" </DDSSecurity>"
|
" </DDSSecurity>"
|
||||||
"</Domain>";
|
"</Domain>";
|
||||||
|
|
||||||
#define DDS_DOMAINID_PART1 0
|
#define DDS_DOMAINID1 0
|
||||||
#define DDS_DOMAINID_PART2 1
|
#define DDS_DOMAINID2 1
|
||||||
|
|
||||||
static dds_entity_t g_part1_domain = 0;
|
static dds_entity_t g_domain1 = 0;
|
||||||
static dds_entity_t g_part1_participant = 0;
|
static dds_entity_t g_participant1 = 0;
|
||||||
|
|
||||||
static dds_entity_t g_part2_domain = 0;
|
static dds_entity_t g_domain2 = 0;
|
||||||
static dds_entity_t g_part2_participant = 0;
|
static dds_entity_t g_participant2 = 0;
|
||||||
|
|
||||||
static void handshake_init(void)
|
static uint32_t g_topic_nr = 0;
|
||||||
|
static dds_entity_t g_pub = 0, g_pub_tp = 0, g_wr = 0, g_sub = 0, g_sub_tp = 0, g_rd = 0;
|
||||||
|
|
||||||
|
static void handshake_init(const char * auth_init, const char * auth_fini, const char * crypto_init, const char * crypto_fini)
|
||||||
{
|
{
|
||||||
char *conf_part1 = ddsrt_expand_envvars_sh (config, DDS_DOMAINID_PART1);
|
struct kvp config_vars[] = {
|
||||||
char *conf_part2 = ddsrt_expand_envvars_sh (config, DDS_DOMAINID_PART2);
|
{ "AUTH_INIT", auth_init, 1},
|
||||||
g_part1_domain = dds_create_domain (DDS_DOMAINID_PART1, conf_part1);
|
{ "AUTH_FINI", auth_fini, 1},
|
||||||
g_part2_domain = dds_create_domain (DDS_DOMAINID_PART2, conf_part2);
|
{ "CRYPTO_INIT", crypto_init, 1 },
|
||||||
dds_free (conf_part1);
|
{ "CRYPTO_FINI", crypto_fini, 1 },
|
||||||
dds_free (conf_part2);
|
{ NULL, NULL, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
CU_ASSERT_FATAL ((g_part1_participant = dds_create_participant (DDS_DOMAINID_PART1, NULL, NULL)) > 0);
|
char *conf = ddsrt_expand_vars_sh (config, &expand_lookup_vars_env, config_vars);
|
||||||
CU_ASSERT_FATAL ((g_part2_participant = dds_create_participant (DDS_DOMAINID_PART2, NULL, NULL)) > 0);
|
int32_t unmatched = expand_lookup_unmatched (config_vars);
|
||||||
|
CU_ASSERT_EQUAL_FATAL (unmatched, 0);
|
||||||
|
g_domain1 = dds_create_domain (DDS_DOMAINID1, conf);
|
||||||
|
g_domain2 = dds_create_domain (DDS_DOMAINID2, conf);
|
||||||
|
dds_free (conf);
|
||||||
|
|
||||||
|
g_participant1 = dds_create_participant (DDS_DOMAINID1, NULL, NULL);
|
||||||
|
CU_ASSERT_FATAL (g_participant1 > 0);
|
||||||
|
g_participant2 = dds_create_participant (DDS_DOMAINID2, NULL, NULL);
|
||||||
|
CU_ASSERT_FATAL (g_participant2 > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handshake_fini(void)
|
static void handshake_fini(void)
|
||||||
{
|
{
|
||||||
CU_ASSERT_EQUAL_FATAL (dds_delete (g_part1_participant), DDS_RETCODE_OK);
|
dds_return_t ret = dds_delete (g_domain1);
|
||||||
CU_ASSERT_EQUAL_FATAL (dds_delete (g_part2_participant), DDS_RETCODE_OK);
|
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
|
||||||
CU_ASSERT_EQUAL_FATAL (dds_delete (g_part1_domain), DDS_RETCODE_OK);
|
ret = dds_delete (g_domain2);
|
||||||
CU_ASSERT_EQUAL_FATAL (dds_delete (g_part2_domain), DDS_RETCODE_OK);
|
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
CU_Test(ddssec_handshake, happy_day, .init = handshake_init, .fini = handshake_fini)
|
CU_Test(ddssec_handshake, happy_day)
|
||||||
{
|
{
|
||||||
validate_handshake_nofail (DDS_DOMAINID_PART1);
|
handshake_init (
|
||||||
validate_handshake_nofail (DDS_DOMAINID_PART2);
|
"init_test_authentication_wrapped", "finalize_test_authentication_wrapped",
|
||||||
|
"init_test_cryptography_wrapped", "finalize_test_cryptography_wrapped");
|
||||||
|
validate_handshake_nofail (DDS_DOMAINID1);
|
||||||
|
validate_handshake_nofail (DDS_DOMAINID2);
|
||||||
|
handshake_fini ();
|
||||||
|
}
|
||||||
|
|
||||||
|
CU_Test(ddssec_handshake, check_tokens)
|
||||||
|
{
|
||||||
|
handshake_init (
|
||||||
|
"init_test_authentication_wrapped", "finalize_test_authentication_wrapped",
|
||||||
|
"init_test_cryptography_store_tokens", "finalize_test_cryptography_store_tokens");
|
||||||
|
validate_handshake_nofail (DDS_DOMAINID1);
|
||||||
|
validate_handshake_nofail (DDS_DOMAINID2);
|
||||||
|
|
||||||
|
char topic_name[100];
|
||||||
|
create_topic_name("ddssec_authentication_", g_topic_nr++, topic_name, sizeof (topic_name));
|
||||||
|
rd_wr_init (g_participant1, &g_pub, &g_pub_tp, &g_wr, g_participant2, &g_sub, &g_sub_tp, &g_rd, topic_name);
|
||||||
|
write_read_for (g_wr, g_participant2, g_rd, DDS_MSECS (100), false, false);
|
||||||
|
|
||||||
|
// Get subscriber and publisher crypto tokens
|
||||||
|
struct dds_security_cryptography_impl * crypto_context_pub = get_crypto_context (g_participant1);
|
||||||
|
CU_ASSERT_FATAL (crypto_context_pub != NULL);
|
||||||
|
struct ddsrt_circlist *pub_tokens = get_crypto_tokens (crypto_context_pub);
|
||||||
|
|
||||||
|
struct dds_security_cryptography_impl * crypto_context_sub = get_crypto_context (g_participant2);
|
||||||
|
CU_ASSERT_FATAL (crypto_context_sub != NULL);
|
||||||
|
struct ddsrt_circlist *sub_tokens = get_crypto_tokens (crypto_context_sub);
|
||||||
|
|
||||||
|
// Find all publisher tokens in subscribers token store
|
||||||
|
while (!ddsrt_circlist_isempty (pub_tokens))
|
||||||
|
{
|
||||||
|
struct ddsrt_circlist_elem *list_elem = ddsrt_circlist_oldest (pub_tokens);
|
||||||
|
struct crypto_token_data *token_data = DDSRT_FROM_CIRCLIST (struct crypto_token_data, e, list_elem);
|
||||||
|
enum crypto_tokens_type exp_type = TOKEN_TYPE_INVALID;
|
||||||
|
for (size_t n = 0; n < token_data->n_tokens; n++)
|
||||||
|
{
|
||||||
|
switch (token_data->type)
|
||||||
|
{
|
||||||
|
case LOCAL_PARTICIPANT_TOKENS: exp_type = REMOTE_PARTICIPANT_TOKENS; break;
|
||||||
|
case REMOTE_PARTICIPANT_TOKENS: exp_type = LOCAL_PARTICIPANT_TOKENS; break;
|
||||||
|
case LOCAL_WRITER_TOKENS: exp_type = REMOTE_WRITER_TOKENS; break;
|
||||||
|
case REMOTE_WRITER_TOKENS: exp_type = LOCAL_WRITER_TOKENS; break;
|
||||||
|
case LOCAL_READER_TOKENS: exp_type = REMOTE_READER_TOKENS; break;
|
||||||
|
case REMOTE_READER_TOKENS: exp_type = LOCAL_READER_TOKENS; break;
|
||||||
|
default: CU_FAIL ("Unexpected token type");
|
||||||
|
}
|
||||||
|
printf("- find token %s #%"PRIuSIZE", len %"PRIuSIZE"\n", get_crypto_token_type_str (token_data->type), n, token_data->data_len[n]);
|
||||||
|
struct crypto_token_data *st = find_crypto_token (crypto_context_sub, exp_type, token_data->data[n], token_data->data_len[n]);
|
||||||
|
CU_ASSERT_FATAL (st != NULL);
|
||||||
|
}
|
||||||
|
ddsrt_circlist_remove (pub_tokens, list_elem);
|
||||||
|
ddsrt_free (token_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
while (!ddsrt_circlist_isempty (sub_tokens))
|
||||||
|
{
|
||||||
|
struct ddsrt_circlist_elem *list_elem = ddsrt_circlist_oldest (sub_tokens);
|
||||||
|
ddsrt_circlist_remove (sub_tokens, list_elem);
|
||||||
|
ddsrt_free (list_elem);
|
||||||
|
}
|
||||||
|
ddsrt_free (sub_tokens);
|
||||||
|
ddsrt_free (pub_tokens);
|
||||||
|
handshake_fini ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,14 +27,11 @@
|
||||||
#include "dds/ddsi/ddsi_domaingv.h"
|
#include "dds/ddsi/ddsi_domaingv.h"
|
||||||
#include "dds/ddsi/q_misc.h"
|
#include "dds/ddsi/q_misc.h"
|
||||||
#include "dds/ddsi/ddsi_xqos.h"
|
#include "dds/ddsi/ddsi_xqos.h"
|
||||||
#include "dds/ddsi/q_entity.h"
|
|
||||||
#include "dds/ddsi/ddsi_entity_index.h"
|
|
||||||
#include "dds/ddsi/ddsi_security_omg.h"
|
|
||||||
#include "dds__entity.h"
|
|
||||||
#include "dds/security/dds_security_api.h"
|
#include "dds/security/dds_security_api.h"
|
||||||
|
|
||||||
#include "common/config_env.h"
|
#include "common/config_env.h"
|
||||||
#include "common/test_identity.h"
|
#include "common/test_identity.h"
|
||||||
|
#include "common/test_utils.h"
|
||||||
#include "common/security_config_test_utils.h"
|
#include "common/security_config_test_utils.h"
|
||||||
#include "common/cryptography_wrapper.h"
|
#include "common/cryptography_wrapper.h"
|
||||||
|
|
||||||
|
@ -119,25 +116,6 @@ typedef void (*set_crypto_params_fn)(struct dds_security_cryptography_impl *, co
|
||||||
typedef dds_entity_t (*pubsub_create_fn)(dds_entity_t, const dds_qos_t *qos, const dds_listener_t *listener);
|
typedef dds_entity_t (*pubsub_create_fn)(dds_entity_t, const dds_qos_t *qos, const dds_listener_t *listener);
|
||||||
typedef dds_entity_t (*ep_create_fn)(dds_entity_t, dds_entity_t, const dds_qos_t *qos, const dds_listener_t *listener);
|
typedef dds_entity_t (*ep_create_fn)(dds_entity_t, dds_entity_t, const dds_qos_t *qos, const dds_listener_t *listener);
|
||||||
|
|
||||||
|
|
||||||
static struct dds_security_cryptography_impl * get_crypto_context(dds_entity_t participant)
|
|
||||||
{
|
|
||||||
struct dds_entity *pp_entity = NULL;
|
|
||||||
struct participant *pp;
|
|
||||||
struct dds_security_cryptography_impl *context;
|
|
||||||
dds_return_t ret;
|
|
||||||
|
|
||||||
ret = dds_entity_lock (participant, DDS_KIND_PARTICIPANT, &pp_entity);
|
|
||||||
CU_ASSERT_EQUAL_FATAL (ret, 0);
|
|
||||||
thread_state_awake (lookup_thread_state(), &pp_entity->m_domain->gv);
|
|
||||||
pp = entidx_lookup_participant_guid (pp_entity->m_domain->gv.entity_index, &pp_entity->m_guid);
|
|
||||||
CU_ASSERT_FATAL (pp != NULL);
|
|
||||||
context = (struct dds_security_cryptography_impl *) q_omg_participant_get_cryptography (pp);
|
|
||||||
thread_state_asleep (lookup_thread_state ());
|
|
||||||
dds_entity_unlock (pp_entity);
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char * pk_to_str(DDS_Security_ProtectionKind pk)
|
static const char * pk_to_str(DDS_Security_ProtectionKind pk)
|
||||||
{
|
{
|
||||||
switch (pk)
|
switch (pk)
|
||||||
|
@ -181,14 +159,6 @@ static dds_qos_t *get_qos()
|
||||||
return qos;
|
return qos;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *create_topic_name(const char *prefix, uint32_t nr, char *name, size_t size)
|
|
||||||
{
|
|
||||||
ddsrt_pid_t pid = ddsrt_getpid ();
|
|
||||||
ddsrt_tid_t tid = ddsrt_gettid ();
|
|
||||||
(void)snprintf(name, size, "%s%d_pid%" PRIdPID "_tid%" PRIdTID "", prefix, nr, pid, tid);
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
static dds_entity_t create_pp (dds_domainid_t domain_id, const struct domain_sec_config * domain_config, set_crypto_params_fn set_crypto_params)
|
static dds_entity_t create_pp (dds_domainid_t domain_id, const struct domain_sec_config * domain_config, set_crypto_params_fn set_crypto_params)
|
||||||
{
|
{
|
||||||
dds_qos_t *qos = dds_create_qos ();
|
dds_qos_t *qos = dds_create_qos ();
|
||||||
|
@ -280,45 +250,6 @@ static void test_fini(size_t n_sub_domain, size_t n_pub_domain)
|
||||||
printf("Test finished\n");
|
printf("Test finished\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sync_writer_to_readers(dds_entity_t pub_participant, dds_entity_t writer, size_t n_exp_rd)
|
|
||||||
{
|
|
||||||
dds_attach_t triggered;
|
|
||||||
dds_return_t ret;
|
|
||||||
dds_entity_t waitset_wr = dds_create_waitset (pub_participant);
|
|
||||||
CU_ASSERT_FATAL (waitset_wr > 0);
|
|
||||||
dds_publication_matched_status_t pub_matched;
|
|
||||||
|
|
||||||
/* Sync writer to reader. */
|
|
||||||
ret = dds_waitset_attach (waitset_wr, writer, writer);
|
|
||||||
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
ret = dds_waitset_wait (waitset_wr, &triggered, 1, DDS_SECS(5));
|
|
||||||
CU_ASSERT_FATAL (ret >= 1);
|
|
||||||
CU_ASSERT_EQUAL_FATAL (writer, (dds_entity_t)(intptr_t) triggered);
|
|
||||||
ret = dds_get_publication_matched_status(writer, &pub_matched);
|
|
||||||
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
|
|
||||||
if (pub_matched.total_count >= n_exp_rd)
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
dds_delete (waitset_wr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reader_wait_for_data(dds_entity_t sub_participant, dds_entity_t reader)
|
|
||||||
{
|
|
||||||
dds_attach_t triggered;
|
|
||||||
dds_return_t ret;
|
|
||||||
dds_entity_t waitset_rd = dds_create_waitset (sub_participant);
|
|
||||||
CU_ASSERT_FATAL (waitset_rd > 0);
|
|
||||||
|
|
||||||
ret = dds_waitset_attach (waitset_rd, reader, reader);
|
|
||||||
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
|
|
||||||
ret = dds_waitset_wait (waitset_rd, &triggered, 1, DDS_SECS(5));
|
|
||||||
CU_ASSERT_EQUAL_FATAL (ret, 1);
|
|
||||||
CU_ASSERT_EQUAL_FATAL (reader, (dds_entity_t)(intptr_t)triggered);
|
|
||||||
dds_delete (waitset_rd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void create_eps (dds_entity_t **endpoints, dds_entity_t **topics, size_t n_dom, size_t n_pp, size_t n_eps, const char * topic_name, const dds_topic_descriptor_t *topic_descriptor,
|
static void create_eps (dds_entity_t **endpoints, dds_entity_t **topics, size_t n_dom, size_t n_pp, size_t n_eps, const char * topic_name, const dds_topic_descriptor_t *topic_descriptor,
|
||||||
const dds_entity_t * pps, const dds_qos_t * qos, ep_create_fn ep_create, unsigned status_mask)
|
const dds_entity_t * pps, const dds_qos_t * qos, ep_create_fn ep_create, unsigned status_mask)
|
||||||
{
|
{
|
||||||
|
@ -381,7 +312,7 @@ static void test_write_read(struct domain_sec_config *domain_config,
|
||||||
for (size_t w = 0; w < n_writers; w++)
|
for (size_t w = 0; w < n_writers; w++)
|
||||||
{
|
{
|
||||||
size_t wr_index = pp_index * n_writers + w;
|
size_t wr_index = pp_index * n_writers + w;
|
||||||
sync_writer_to_readers (g_pub_participants[pp_index], writers[wr_index], n_sub_domains * n_sub_participants * n_readers);
|
sync_writer_to_readers (g_pub_participants[pp_index], writers[wr_index], (uint32_t)(n_sub_domains * n_sub_participants * n_readers));
|
||||||
sample.id = (int32_t) wr_index;
|
sample.id = (int32_t) wr_index;
|
||||||
printf("writer %"PRId32" writing sample %d\n", writers[wr_index], sample.id);
|
printf("writer %"PRId32" writing sample %d\n", writers[wr_index], sample.id);
|
||||||
ret = dds_write (writers[wr_index], &sample);
|
ret = dds_write (writers[wr_index], &sample);
|
||||||
|
@ -404,7 +335,7 @@ static void test_write_read(struct domain_sec_config *domain_config,
|
||||||
ret = dds_take (readers[rd_index], samples, info, 1, 1);
|
ret = dds_take (readers[rd_index], samples, info, 1, 1);
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
{
|
{
|
||||||
reader_wait_for_data (g_sub_participants[pp_index], readers[rd_index]);
|
reader_wait_for_data (g_sub_participants[pp_index], readers[rd_index], DDS_SECS(5));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
printf("reader %"PRId32" received sample %d\n", readers[rd_index], rd_sample.id);
|
printf("reader %"PRId32" received sample %d\n", readers[rd_index], rd_sample.id);
|
||||||
|
@ -500,7 +431,7 @@ static void test_payload_secret(DDS_Security_ProtectionKind rtps_pk, DDS_Securit
|
||||||
{
|
{
|
||||||
if ((ret = dds_take (readers[0], samples, info, 1, 1)) == 0)
|
if ((ret = dds_take (readers[0], samples, info, 1, 1)) == 0)
|
||||||
{
|
{
|
||||||
reader_wait_for_data (g_sub_participants[0], readers[0]);
|
reader_wait_for_data (g_sub_participants[0], readers[0], DDS_SECS(5));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
CU_ASSERT_EQUAL_FATAL (ret, 1);
|
CU_ASSERT_EQUAL_FATAL (ret, 1);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue