
The configuration was changed in b25f10ff
to consider the DDSSecurity
tag deprecated (there is no other), which gave rise to warnings in test
output.
Signed-off-by: Erik Boasson <eb@ilities.com>
893 lines
50 KiB
C
893 lines
50 KiB
C
/*
|
|
* Copyright(c) 2006 to 2020 ADLINK Technology Limited and others
|
|
*
|
|
* This program and the accompanying materials are made available under the
|
|
* terms of the Eclipse Public License v. 2.0 which is available at
|
|
* http://www.eclipse.org/legal/epl-2.0, or the Eclipse Distribution License
|
|
* v. 1.0 which is available at
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
*
|
|
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
|
|
#include "dds/dds.h"
|
|
#include "CUnit/Test.h"
|
|
#include "CUnit/Theory.h"
|
|
|
|
#include "dds/version.h"
|
|
#include "dds/ddsrt/cdtors.h"
|
|
#include "dds/ddsrt/environ.h"
|
|
#include "dds/ddsrt/process.h"
|
|
#include "dds/ddsrt/heap.h"
|
|
#include "dds/ddsrt/io.h"
|
|
#include "dds/ddsrt/string.h"
|
|
#include "dds/ddsi/ddsi_security_omg.h"
|
|
#include "dds/ddsi/q_config.h"
|
|
#include "dds/ddsi/ddsi_domaingv.h"
|
|
#include "dds/ddsi/q_misc.h"
|
|
#include "dds/ddsi/ddsi_xqos.h"
|
|
#include "dds__entity.h"
|
|
|
|
#include "dds/security/dds_security_api.h"
|
|
|
|
#include "common/config_env.h"
|
|
#include "common/access_control_wrapper.h"
|
|
#include "common/cryptography_wrapper.h"
|
|
#include "common/security_config_test_utils.h"
|
|
#include "common/test_identity.h"
|
|
#include "common/test_utils.h"
|
|
#include "common/cert_utils.h"
|
|
#include "SecurityCoreTests.h"
|
|
|
|
static const char *config =
|
|
"${CYCLONEDDS_URI}${CYCLONEDDS_URI:+,}"
|
|
"<Domain id=\"any\">"
|
|
" <Discovery>"
|
|
" <ExternalDomainId>0</ExternalDomainId>"
|
|
" <Tag>\\${CYCLONEDDS_PID}</Tag>"
|
|
" </Discovery>"
|
|
" <Security>"
|
|
" <Authentication>"
|
|
" <Library finalizeFunction=\"finalize_test_authentication_wrapped\" initFunction=\"init_test_authentication_wrapped\" path=\"" WRAPPERLIB_PATH("dds_security_authentication_wrapper") "\"/>"
|
|
" <IdentityCertificate>data:,${TEST_IDENTITY_CERTIFICATE}</IdentityCertificate>"
|
|
" <PrivateKey>data:,${TEST_IDENTITY_PRIVATE_KEY}</PrivateKey>"
|
|
" <IdentityCA>data:,${TEST_IDENTITY_CA_CERTIFICATE}</IdentityCA>"
|
|
" </Authentication>"
|
|
" <AccessControl>"
|
|
" <Library initFunction=\"${ACCESS_CONTROL_INIT:-init_test_access_control_wrapped}\" finalizeFunction=\"${ACCESS_CONTROL_FINI:-finalize_test_access_control_wrapped}\" path=\"" WRAPPERLIB_PATH("dds_security_access_control_wrapper") "\"/>"
|
|
" ${INCL_GOV:+<Governance><![CDATA[}${TEST_GOVERNANCE}${INCL_GOV:+]]></Governance>}"
|
|
" ${INCL_PERM_CA:+<PermissionsCA>}${TEST_PERMISSIONS_CA}${INCL_PERM_CA:+</PermissionsCA>}"
|
|
" ${INCL_PERM:+<Permissions><![CDATA[}${TEST_PERMISSIONS}${INCL_PERM:+]]></Permissions>}"
|
|
" </AccessControl>"
|
|
" <Cryptographic>"
|
|
" <Library initFunction=\"${CRYPTO_INIT:-init_test_cryptography_wrapped}\" finalizeFunction=\"${CRYPTO_INIT:-finalize_test_cryptography_wrapped}\" path=\"" WRAPPERLIB_PATH("dds_security_cryptography_wrapper") "\"/>"
|
|
" </Cryptographic>"
|
|
" </Security>"
|
|
"</Domain>";
|
|
|
|
#define MAX_DOMAINS 10
|
|
#define DDS_DOMAINID 0
|
|
|
|
static dds_entity_t g_domain[MAX_DOMAINS];
|
|
static dds_entity_t g_participant[MAX_DOMAINS];
|
|
static uint32_t g_topic_nr = 0;
|
|
|
|
static void access_control_init(
|
|
size_t n_nodes,
|
|
const char * id_certs[], const char * id_keys[], const char * id_ca[], bool exp_pp_fail[],
|
|
const char * ac_init_fns[], const char * ac_fini_fns[],
|
|
bool incl_gov[], const char * gov[],
|
|
bool incl_perm[], const char * perm[],
|
|
bool incl_ca[], const char * ca[])
|
|
{
|
|
CU_ASSERT_FATAL (n_nodes <= MAX_DOMAINS);
|
|
for (size_t i = 0; i < n_nodes; i++)
|
|
{
|
|
print_test_msg ("init domain %"PRIuSIZE"\n", i);
|
|
struct kvp config_vars[] = {
|
|
{ "TEST_IDENTITY_CERTIFICATE", id_certs[i], 1 },
|
|
{ "TEST_IDENTITY_PRIVATE_KEY", id_keys[i], 1 },
|
|
{ "TEST_IDENTITY_CA_CERTIFICATE", id_ca[i], 1 },
|
|
{ "ACCESS_CONTROL_INIT", ac_init_fns ? ac_init_fns[i] : NULL, 1 },
|
|
{ "ACCESS_CONTROL_FINI", ac_fini_fns ? ac_fini_fns[i] : NULL, 1 },
|
|
{ "INCL_GOV", incl_gov[i] ? "1" : "", 2 },
|
|
{ "INCL_PERM", incl_perm[i] ? "1" : "", 2 },
|
|
{ "INCL_PERM_CA", incl_ca[i] ? "1" : "", 2 },
|
|
{ "TEST_GOVERNANCE", gov[i], 1 },
|
|
{ "TEST_PERMISSIONS", perm[i], 1 },
|
|
{ "TEST_PERMISSIONS_CA", ca[i], 1 },
|
|
{ NULL, NULL, 0 }
|
|
};
|
|
char *conf = ddsrt_expand_vars_sh (config, &expand_lookup_vars_env, config_vars);
|
|
CU_ASSERT_EQUAL_FATAL (expand_lookup_unmatched (config_vars), 0);
|
|
g_domain[i] = dds_create_domain (DDS_DOMAINID + (dds_domainid_t)i, conf);
|
|
dds_free (conf);
|
|
g_participant[i] = dds_create_participant (DDS_DOMAINID + (dds_domainid_t)i, NULL, NULL);
|
|
CU_ASSERT_EQUAL_FATAL (exp_pp_fail[i], g_participant[i] <= 0);
|
|
}
|
|
}
|
|
|
|
static void access_control_fini(size_t n, void * res[], size_t nres)
|
|
{
|
|
for (size_t i = 0; i < n; i++)
|
|
CU_ASSERT_EQUAL_FATAL (dds_delete (g_domain[i]), DDS_RETCODE_OK);
|
|
if (res != NULL)
|
|
{
|
|
for (size_t i = 0; i < nres; i++)
|
|
ddsrt_free (res[i]);
|
|
}
|
|
}
|
|
|
|
#define GOV_F PF_F COMMON_ETC_PATH("default_governance.p7s")
|
|
#define GOV_FNE PF_F COMMON_ETC_PATH("default_governance_non_existing.p7s")
|
|
#define GOV_DI PF_D COMMON_ETC_PATH("default_governance.p7s")
|
|
#define PERM_F PF_F COMMON_ETC_PATH("default_permissions.p7s")
|
|
#define PERM_FNE PF_F COMMON_ETC_PATH("default_permissions_non_existing.p7s")
|
|
#define PERM_DI PF_D COMMON_ETC_PATH("default_permissions.p7s")
|
|
#define CA_F PF_F COMMON_ETC_PATH("default_permissions_ca.pem")
|
|
#define CA_FNE PF_F COMMON_ETC_PATH("default_permissions_ca_non_existing.pem")
|
|
#define CA_DI PF_D COMMON_ETC_PATH("default_permissions_ca.pem")
|
|
#define CA_D PF_D TEST_PERMISSIONS_CA_CERTIFICATE
|
|
|
|
CU_TheoryDataPoints(ddssec_access_control, config_parameters_file) = {
|
|
CU_DataPoints(const char *,
|
|
/* */"existing files",
|
|
/* | */"non-existing files",
|
|
/* | | */"non-existing governance file",
|
|
/* | | | */"non-existing permissions file",
|
|
/* | | | | */"non-existing permissions ca file",
|
|
/* | | | | | */"empty governance",
|
|
/* | | | | | | */"empty permissions",
|
|
/* | | | | | | | */"empty permissions ca",
|
|
/* | | | | | | | | */"all empty",
|
|
/* | | | | | | | | | */"invalid governance uri type",
|
|
/* | | | | | | | | | | */"permissions ca type data",
|
|
/* | | | | | | | | | | | */"no governance element",
|
|
/* | | | | | | | | | | | | */"no permissions element",
|
|
/* | | | | | | | | | | | | | */"no permissions ca element"),
|
|
CU_DataPoints(const char *, GOV_F, GOV_FNE, GOV_FNE, GOV_F, GOV_F, "", GOV_F, GOV_F, "", GOV_DI, GOV_F, "", GOV_F, GOV_F), // Governance config
|
|
CU_DataPoints(const char *, PERM_F, PERM_FNE, PERM_F, PERM_FNE, PERM_F, PERM_F, "", PERM_F, "", PERM_F, PERM_F, PERM_F, "", PERM_F), // Permissions config
|
|
CU_DataPoints(const char *, CA_F, CA_FNE, CA_F, CA_F, CA_FNE, CA_F, CA_F, "", "", CA_F, CA_D, CA_F, CA_F, ""), // Permissions CA
|
|
CU_DataPoints(bool, true, true, true, true, true, true, true, true, true, true, true, false, false, false), // include empty config elements
|
|
CU_DataPoints(bool, false, true, true, true, true, true, true, true, false, true, false, true, true, true) // expect failure
|
|
};
|
|
/* Testing configuration parameters for the access control security plugin,
|
|
using configuration from file. The test cases include using non-existing
|
|
files, empty configuration files, mixing configudation from file and inline
|
|
in the cyclone XML configuration. */
|
|
CU_Theory((const char * test_descr, const char * gov, const char * perm, const char * ca, bool incl_empty_els, bool exp_fail),
|
|
ddssec_access_control, config_parameters_file)
|
|
{
|
|
print_test_msg ("running test config_parameters_file: %s\n", test_descr);
|
|
bool has_gov = incl_empty_els || strlen (gov);
|
|
bool has_perm = incl_empty_els || strlen (perm);
|
|
bool has_ca = incl_empty_els || strlen (ca);
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { TEST_IDENTITY1_CERTIFICATE, TEST_IDENTITY1_CERTIFICATE },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { TEST_IDENTITY_CA1_CERTIFICATE, TEST_IDENTITY_CA1_CERTIFICATE },
|
|
(bool []) { exp_fail, exp_fail }, NULL, NULL,
|
|
(bool []) { has_gov, has_gov }, (const char *[]) { gov, gov },
|
|
(bool []) { has_perm, has_perm }, (const char *[]) { perm, perm },
|
|
(bool []) { has_ca, has_ca }, (const char *[]) { ca, ca });
|
|
access_control_fini (2, NULL, 0);
|
|
}
|
|
|
|
#define S(n) (n)
|
|
#define M(n) (S(n)*60)
|
|
#define H(n) (M(n)*60)
|
|
#define D(n) (H(n)*24)
|
|
CU_TheoryDataPoints(ddssec_access_control, permissions_expiry) = {
|
|
CU_DataPoints(const char *,
|
|
/* */"valid 1 minute from now",
|
|
/* | */"valid -1 minute until now",
|
|
/* | | */"1s valid, create pp after 1100ms",
|
|
/* | | | */"node 2 permissions expired",
|
|
/* | | | | */"node 1 4s valid, write/read for 10s",
|
|
/* | | | | | */"node 2 4s valid, write/read for 10s"),
|
|
CU_DataPoints(int32_t, 0, -M(1), 0, 0, 0, 0), /* node 1 permissions not before (offset from local time) */
|
|
CU_DataPoints(int32_t, M(1), 0, S(1), D(1), S(4), D(1)), /* node 1 permissions not after (offset from local time) */
|
|
CU_DataPoints(int32_t, 0, -M(1), 0, -D(1), 0, 0), /* node 2 permissions not before (offset from local time) */
|
|
CU_DataPoints(int32_t, M(1), 0, S(1), 0, D(1), S(4)), /* node 2 permissions not after (offset from local time) */
|
|
CU_DataPoints(uint32_t, 0, 0, 1100, 0, 0, 0), /* delay (ms) after generating permissions */
|
|
CU_DataPoints(bool, false, true, true, false, false, false), /* expect pp 1 create failure */
|
|
CU_DataPoints(bool, false, true, true, true, false, false), /* expect pp 2 create failure */
|
|
CU_DataPoints(uint32_t, 1, 0, 0, 0, 10000, 10000), /* write/read data during x ms */
|
|
CU_DataPoints(bool, false, false, false, false, true, true), /* expect read data failure */
|
|
};
|
|
#undef S
|
|
#undef D
|
|
#undef H
|
|
#undef M
|
|
/* Testing expiry of the (signed) permissions XML. Test cases include using
|
|
permissions config that is valid for 1 minute, was valid in the past minute,
|
|
expires before data is written, expires during writing data. */
|
|
CU_Theory(
|
|
(const char * test_descr,
|
|
int32_t perm1_not_before, int32_t perm1_not_after, int32_t perm2_not_before, int32_t perm2_not_after,
|
|
uint32_t delay_perm, bool exp_pp1_fail, bool exp_pp2_fail, uint32_t write_read_dur, bool exp_read_fail),
|
|
ddssec_access_control, permissions_expiry, .timeout=30)
|
|
{
|
|
print_test_msg ("running test permissions_expiry: %s\n", test_descr);
|
|
|
|
char topic_name[100];
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, topic_name, sizeof (topic_name));
|
|
|
|
/* create ca and id1/id2 certs that will not expire during this test */
|
|
char *ca, *id1, *id2, *id1_subj, *id2_subj;
|
|
ca = generate_ca ("ca1", TEST_IDENTITY_CA1_PRIVATE_KEY, 0, 3600);
|
|
id1 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id1", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id1_subj);
|
|
id2 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id2", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id2_subj);
|
|
|
|
/* localtime will be converted to gmtime in get_permissions_grant */
|
|
dds_time_t now = dds_time ();
|
|
char * rules_xml = get_permissions_rules (NULL, topic_name, topic_name, NULL, NULL);
|
|
char * grants[] = {
|
|
get_permissions_grant ("id1", id1_subj, now + DDS_SECS(perm1_not_before), now + DDS_SECS(perm1_not_after), rules_xml, NULL),
|
|
get_permissions_grant ("id2", id2_subj, now + DDS_SECS(perm2_not_before), now + DDS_SECS(perm2_not_after), rules_xml, NULL) };
|
|
char * perm_config = get_permissions_config (grants, 2, true);
|
|
dds_sleepfor (DDS_MSECS (delay_perm));
|
|
|
|
const char * def_gov = PF_F COMMON_ETC_PATH("default_governance.p7s");
|
|
const char * def_perm_ca = PF_F COMMON_ETC_PATH("default_permissions_ca.pem");
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { id1, id2 },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { ca, ca },
|
|
(bool []) { exp_pp1_fail, exp_pp2_fail }, NULL, NULL,
|
|
(bool []) { true, true }, (const char *[]) { def_gov, def_gov },
|
|
(bool []) { true, true }, (const char *[]) { perm_config, perm_config },
|
|
(bool []) { true, true }, (const char *[]) { def_perm_ca, def_perm_ca });
|
|
|
|
if (write_read_dur > 0)
|
|
{
|
|
dds_entity_t wr = 0, rd = 0;
|
|
dds_entity_t pub, sub;
|
|
dds_entity_t topic0, topic1;
|
|
rd_wr_init (g_participant[0], &pub, &topic0, &wr, g_participant[1], &sub, &topic1, &rd, topic_name);
|
|
sync_writer_to_readers(g_participant[0], wr, 1, dds_time() + DDS_SECS(2));
|
|
write_read_for (wr, g_participant[1], rd, DDS_MSECS (write_read_dur), false, exp_read_fail);
|
|
}
|
|
|
|
access_control_fini (2, (void * []) { rules_xml, grants[0], grants[1], perm_config, ca, id1_subj, id2_subj, id1, id2 }, 9);
|
|
}
|
|
|
|
|
|
#define N_WR 3
|
|
#define N_NODES (1 + N_WR)
|
|
#define PERM_EXP_BASE 3
|
|
#define PERM_EXP_INCR 2
|
|
/* Tests permissions configuration expiry using multiple writers, to validate
|
|
that a reader keeps receiving data from writers that have valid permissions config */
|
|
CU_Test(ddssec_access_control, permissions_expiry_multiple, .timeout=20)
|
|
{
|
|
char topic_name[100];
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, topic_name, sizeof (topic_name));
|
|
|
|
dds_time_t t_perm = dds_time ();
|
|
char *ca = generate_ca ("ca1", TEST_IDENTITY_CA1_PRIVATE_KEY, 0, 3600);
|
|
char *rules_xml = get_permissions_rules (NULL, topic_name, topic_name, NULL, NULL);
|
|
|
|
// 1st node used as reader, other nodes as writer
|
|
print_test_msg ("creating permissions grants\n");
|
|
const char *id[N_NODES], *pk[N_NODES], *ca_list[N_NODES], *gov[N_NODES], *perm_conf[N_NODES], *perm_ca[N_NODES];
|
|
char * id_subj[N_NODES], *grants[N_NODES];
|
|
bool exp_fail[N_NODES], incl_el[N_NODES];
|
|
|
|
for (int i = 0; i < N_NODES; i++)
|
|
{
|
|
char *id_name;
|
|
ddsrt_asprintf (&id_name, "id_%d", i);
|
|
pk[i] = TEST_IDENTITY1_PRIVATE_KEY;
|
|
ca_list[i] = ca;
|
|
id[i] = generate_identity (ca_list[i], TEST_IDENTITY_CA1_PRIVATE_KEY, id_name, pk[i], 0, 3600, &id_subj[i]);
|
|
exp_fail[i] = false;
|
|
gov[i] = PF_F COMMON_ETC_PATH ("default_governance.p7s");
|
|
perm_ca[i] = PF_F COMMON_ETC_PATH ("default_permissions_ca.pem");
|
|
incl_el[i] = true;
|
|
dds_duration_t v = DDS_SECS(i == 0 ? 3600 : PERM_EXP_BASE + PERM_EXP_INCR * i); /* reader should not expire */
|
|
dds_time_t t_exp = ddsrt_time_add_duration (t_perm, v);
|
|
if (i >= 1)
|
|
print_test_msg ("w[%d] grant expires at %d.%06d\n", i - 1, (int32_t) (t_exp / DDS_NSECS_IN_SEC), (int32_t) (t_exp % DDS_NSECS_IN_SEC) / 1000);
|
|
grants[i] = get_permissions_grant (id_name, id_subj[i], t_perm, t_exp, rules_xml, NULL);
|
|
ddsrt_free (id_name);
|
|
}
|
|
|
|
char * perm_config_str = get_permissions_config (grants, N_NODES, true);
|
|
for (int i = 0; i < N_NODES; i++)
|
|
perm_conf[i] = perm_config_str;
|
|
|
|
access_control_init (
|
|
N_NODES,
|
|
id, pk, ca_list, exp_fail, NULL, NULL,
|
|
incl_el, gov, incl_el, perm_conf, incl_el, perm_ca);
|
|
|
|
// create 1 reader
|
|
dds_qos_t * rdqos = get_default_test_qos ();
|
|
dds_entity_t sub = dds_create_subscriber (g_participant[0], NULL, NULL);
|
|
CU_ASSERT_FATAL (sub > 0);
|
|
dds_entity_t sub_tp = dds_create_topic (g_participant[0], &SecurityCoreTests_Type1_desc, topic_name, NULL, NULL);
|
|
CU_ASSERT_FATAL (sub_tp > 0);
|
|
dds_entity_t rd = dds_create_reader (sub, sub_tp, rdqos, NULL);
|
|
CU_ASSERT_FATAL (rd > 0);
|
|
dds_set_status_mask (rd, DDS_SUBSCRIPTION_MATCHED_STATUS);
|
|
dds_delete_qos (rdqos);
|
|
|
|
// create N_WR writers
|
|
dds_qos_t * wrqos = get_default_test_qos ();
|
|
dds_entity_t wr[N_WR];
|
|
for (int i = 0; i < N_WR; i++)
|
|
{
|
|
dds_entity_t pub = dds_create_publisher (g_participant[i + 1], NULL, NULL);
|
|
CU_ASSERT_FATAL (pub > 0);
|
|
dds_entity_t pub_tp = dds_create_topic (g_participant[i + 1], &SecurityCoreTests_Type1_desc, topic_name, NULL, NULL);
|
|
CU_ASSERT_FATAL (pub_tp > 0);
|
|
wr[i] = dds_create_writer (pub, pub_tp, wrqos, NULL);
|
|
CU_ASSERT_FATAL (wr[i] > 0);
|
|
dds_set_status_mask (wr[i], DDS_PUBLICATION_MATCHED_STATUS);
|
|
}
|
|
dds_delete_qos (wrqos);
|
|
|
|
// match deadline follows from expiration times
|
|
const dds_time_t sync_abstimeout = t_perm + DDS_SECS (PERM_EXP_BASE + 1);
|
|
for (int i = 0; i < N_WR; i++)
|
|
sync_writer_to_readers (g_participant[i + 1], wr[i], 1, sync_abstimeout);
|
|
sync_reader_to_writers (g_participant[0], rd, N_WR, sync_abstimeout);
|
|
|
|
// write data
|
|
SecurityCoreTests_Type1 sample = { 1, 1 };
|
|
dds_return_t ret;
|
|
dds_entity_t ws = dds_create_waitset (g_participant[0]);
|
|
dds_entity_t gcond = dds_create_guardcondition (g_participant[0]);
|
|
dds_set_guardcondition (gcond, false);
|
|
dds_waitset_attach (ws, gcond, 0);
|
|
|
|
dds_set_status_mask (rd, DDS_DATA_AVAILABLE_STATUS);
|
|
for (int run = 0; run < N_WR; run++)
|
|
{
|
|
// wait until 1s after next writer pp permission expires
|
|
dds_waitset_wait_until (ws, NULL, 0, t_perm + DDS_SECS (PERM_EXP_BASE + PERM_EXP_INCR * run + 1));
|
|
print_test_msg ("run %d\n", run);
|
|
for (int w = 0; w < N_WR; w++)
|
|
{
|
|
sample.id = w;
|
|
ret = dds_write (wr[w], &sample);
|
|
print_test_msg ("write %d\n", w);
|
|
CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK);
|
|
}
|
|
}
|
|
|
|
// wait until last pp's permissions are expired
|
|
dds_waitset_wait_until (ws, NULL, 0, t_perm + DDS_SECS (PERM_EXP_BASE + PERM_EXP_INCR * N_WR + 1));
|
|
|
|
// check received data
|
|
SecurityCoreTests_Type1 * data = ddsrt_calloc (N_WR, sizeof (*data));
|
|
dds_sample_info_t rd_info[N_WR];
|
|
static void * rd_samples[N_WR];
|
|
for (int i = 0; i < N_WR; i++)
|
|
rd_samples[i] = &data[i];
|
|
|
|
for (int w = 0; w < N_WR; w++)
|
|
{
|
|
sample.id = w;
|
|
dds_instance_handle_t ih = dds_lookup_instance(rd, &sample);
|
|
CU_ASSERT_NOT_EQUAL_FATAL(ih, DDS_HANDLE_NIL);
|
|
ret = dds_take_instance (rd, rd_samples, rd_info, N_WR, N_WR, ih);
|
|
print_test_msg ("samples from writer %d: %d\n", w, ret);
|
|
CU_ASSERT_EQUAL_FATAL (ret, w + 1);
|
|
print_test_msg ("writer %d instance state: %d\n", w, rd_info[w].instance_state);
|
|
CU_ASSERT_EQUAL_FATAL (rd_info[w].instance_state, DDS_NOT_ALIVE_DISPOSED_INSTANCE_STATE);
|
|
print_test_msg ("writer %d valid data: %d\n", w, rd_info[w].valid_data);
|
|
CU_ASSERT_EQUAL_FATAL (rd_info[w].valid_data, true);
|
|
}
|
|
|
|
access_control_fini (N_NODES, (void * []) { ca, rules_xml, perm_config_str, data }, 4);
|
|
for (int i = 0; i < N_NODES; i++)
|
|
{
|
|
ddsrt_free (grants[i]);
|
|
ddsrt_free (id_subj[i]);
|
|
ddsrt_free ((char *)id[i]);
|
|
}
|
|
}
|
|
#undef N_WR
|
|
#undef N_NODES
|
|
#undef PERM_EXP_BASE
|
|
#undef PERM_EXP_INCR
|
|
|
|
#define na false
|
|
CU_TheoryDataPoints(ddssec_access_control, hooks) = {
|
|
CU_DataPoints(const char *,
|
|
/* */"init_test_access_control_local_participant_not_allowed",
|
|
/* | */"init_test_access_control_local_permissions_not_allowed",
|
|
/* | | */"init_test_access_control_local_topic_not_allowed",
|
|
/* | | | */"init_test_access_control_local_writer_not_allowed",
|
|
/* | | | | */"init_test_access_control_local_reader_not_allowed",
|
|
/* | | | | | */"init_test_access_control_remote_permissions_not_allowed",
|
|
/* | | | | | | */"init_test_access_control_remote_participant_not_allowed",
|
|
/* | | | | | | | */"init_test_access_control_remote_topic_not_allowed",
|
|
/* | | | | | | | | */"init_test_access_control_remote_writer_not_allowed",
|
|
/* | | | | | | | | | */"init_test_access_control_remote_reader_not_allowed",
|
|
/* | | | | | | | | | | */"init_test_access_control_remote_reader_relay_only"),
|
|
CU_DataPoints(bool, true, true, false, false, false, false, false, false, false, false, false), // exp_pp_fail
|
|
CU_DataPoints(bool, na, na, true, false, false, false, false, false, false, false, false), // exp_local_topic_fail
|
|
CU_DataPoints(bool, na, na, false, false, false, false, false, false, false, false, false), // exp_remote_topic_fail
|
|
CU_DataPoints(bool, na, na, na, true, false, false, false, false, false, false, false), // exp_wr_fail
|
|
CU_DataPoints(bool, na, na, na, false, true, false, false, false, false, false, false), // exp_rd_fail
|
|
CU_DataPoints(bool, na, na, na, na, na, true, true, true, false, true, true), // exp_wr_rd_sync_fail
|
|
CU_DataPoints(bool, na, na, na, false, na, true, true, true, true, false, false), // exp_rd_wr_sync_fail
|
|
};
|
|
#undef na
|
|
/* Test that the security implementation in DDSI is correctly handling denial of
|
|
creating enities, e.g. local participant not allowed, local writer not allowed,
|
|
remote topic not allowed, etc. This test is initializing the wrapper plugin in a
|
|
not-allowed mode to force denial of a specified entity. */
|
|
CU_Theory(
|
|
(const char * init_fn, bool exp_pp_fail, bool exp_local_topic_fail, bool exp_remote_topic_fail, bool exp_wr_fail, bool exp_rd_fail, bool exp_wr_rd_sync_fail, bool exp_rd_wr_sync_fail),
|
|
ddssec_access_control, hooks, .timeout=60)
|
|
{
|
|
for (int i = 0; i <= 1; i++)
|
|
{
|
|
bool discovery_protection = (i == 0);
|
|
print_test_msg ("running test access_control_hooks: %s with discovery protection %s\n", init_fn, discovery_protection ? "enabled" : "disabled");
|
|
|
|
char * gov_topic_rule = get_governance_topic_rule ("*", discovery_protection, false, true, true, PK_N, BPK_N);
|
|
char * gov_config = get_governance_config (false, true, PK_E, PK_N, PK_N, gov_topic_rule, true);
|
|
|
|
const char * def_perm = PF_F COMMON_ETC_PATH("default_permissions.p7s");
|
|
const char * def_perm_ca = PF_F COMMON_ETC_PATH("default_permissions_ca.pem");
|
|
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { TEST_IDENTITY1_CERTIFICATE, TEST_IDENTITY1_CERTIFICATE },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { TEST_IDENTITY_CA1_CERTIFICATE, TEST_IDENTITY_CA1_CERTIFICATE },
|
|
(bool []) { exp_pp_fail, false },
|
|
(const char *[]) { init_fn, "init_test_access_control_wrapped" }, (const char *[]) { "finalize_test_access_control_not_allowed", "finalize_test_access_control_wrapped" },
|
|
(bool []) { true, true, true }, (const char *[]) { gov_config, gov_config },
|
|
(bool []) { true, true, true }, (const char *[]) { def_perm, def_perm },
|
|
(bool []) { true, true, true }, (const char *[]) { def_perm_ca, def_perm_ca });
|
|
|
|
if (!exp_pp_fail)
|
|
{
|
|
const dds_time_t sync_abstimeout = dds_time () + DDS_SECS (2);
|
|
|
|
dds_entity_t lwr = 0, rwr = 0, lrd = 0, rrd = 0;
|
|
dds_entity_t ltopic[2], rtopic[2];
|
|
dds_entity_t lpub, lsub, rpub, rsub;
|
|
char topic_name[100];
|
|
|
|
// Local writer, remote reader
|
|
create_topic_name (AC_WRAPPER_TOPIC_PREFIX, g_topic_nr++, topic_name, sizeof (topic_name));
|
|
rd_wr_init_fail (
|
|
g_participant[0], &lpub, <opic[0], &lwr,
|
|
g_participant[1], &rsub, &rtopic[0], &rrd,
|
|
topic_name, exp_local_topic_fail, exp_wr_fail, exp_remote_topic_fail, false);
|
|
|
|
// Local reader, remote writer
|
|
create_topic_name (AC_WRAPPER_TOPIC_PREFIX, g_topic_nr++, topic_name, sizeof (topic_name));
|
|
rd_wr_init_fail (
|
|
g_participant[1], &rpub, &rtopic[1], &rwr,
|
|
g_participant[0], &lsub, <opic[1], &lrd,
|
|
topic_name, exp_remote_topic_fail, false, exp_local_topic_fail, exp_rd_fail);
|
|
|
|
if (!exp_local_topic_fail && !exp_remote_topic_fail && !exp_wr_fail)
|
|
sync_writer_to_readers (g_participant[0], lwr, exp_wr_rd_sync_fail ? 0 : 1, sync_abstimeout);
|
|
if (!exp_local_topic_fail && !exp_remote_topic_fail && !exp_rd_fail)
|
|
sync_reader_to_writers (g_participant[0], lrd, exp_rd_wr_sync_fail ? 0 : 1, sync_abstimeout);
|
|
}
|
|
|
|
access_control_fini (2, (void * []) { gov_topic_rule, gov_config }, 2);
|
|
}
|
|
}
|
|
|
|
#define na false
|
|
CU_TheoryDataPoints(ddssec_access_control, join_access_control) = {
|
|
CU_DataPoints(const char *,
|
|
/* */"no join access control",
|
|
/* | */"join access control pp1, valid",
|
|
/* | | */"join access control pp1 and pp2, valid",
|
|
/* | | | */"join access control pp1, invalid",
|
|
/* | | | | */"join access control pp1 and pp2, invalid"),
|
|
CU_DataPoints(bool, false, true, true, true, true), /* join access control pp 1 enabled */
|
|
CU_DataPoints(bool, false, false, true, false, true), /* join access control pp 2 enabled */
|
|
CU_DataPoints(bool, false, false, false, true, true), /* permissions pp 1 invalid */
|
|
CU_DataPoints(bool, false, false, false, false, true), /* permissions pp 2 invalid */
|
|
CU_DataPoints(bool, false, false, false, true, true), /* expect pp 1 create failure */
|
|
CU_DataPoints(bool, false, false, false, false, true), /* expect pp 2 create failure */
|
|
CU_DataPoints(bool, false, false, false, na, na), /* expect handshake failure */
|
|
};
|
|
#undef na
|
|
/* Testing handshake result using join access control setting enabled/disabled and
|
|
valid/invalid permissions for 2 participants. */
|
|
CU_Theory(
|
|
(const char * test_descr, bool join_ac_pp1, bool join_ac_pp2, bool perm_inv_pp1, bool perm_inv_pp2, bool exp_pp1_fail, bool exp_pp2_fail, bool exp_hs_fail),
|
|
ddssec_access_control, join_access_control, .timeout=30)
|
|
{
|
|
print_test_msg ("running test join_access_control: %s\n", test_descr);
|
|
|
|
char topic_name[100];
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, topic_name, sizeof (topic_name));
|
|
|
|
/* create ca and id1/id2 certs that will not expire during this test */
|
|
char *ca, *id1, *id2, *id1_subj, *id2_subj;
|
|
ca = generate_ca ("ca1", TEST_IDENTITY_CA1_PRIVATE_KEY, 0, 3600);
|
|
id1 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id1", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id1_subj);
|
|
id2 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id2", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id2_subj);
|
|
|
|
dds_time_t now = dds_time ();
|
|
char * rules1_xml = get_permissions_rules (perm_inv_pp1 ? "99" : NULL, topic_name, topic_name, NULL, NULL);
|
|
char * rules2_xml = get_permissions_rules (perm_inv_pp2 ? "99" : NULL, topic_name, topic_name, NULL, NULL);
|
|
char * grants[] = {
|
|
get_permissions_grant ("id1", id1_subj, now, now + DDS_SECS(3600), rules1_xml, NULL),
|
|
get_permissions_grant ("id2", id2_subj, now, now + DDS_SECS(3600), rules2_xml, NULL) };
|
|
char * perm_config = get_permissions_config (grants, 2, true);
|
|
|
|
char * gov_topic_rule = get_governance_topic_rule ("*", false, false, true, true, PK_N, BPK_N);
|
|
char * gov_config_pp1 = get_governance_config (false, join_ac_pp1, PK_N, PK_N, PK_N, gov_topic_rule, true);
|
|
char * gov_config_pp2 = get_governance_config (false, join_ac_pp2, PK_N, PK_N, PK_N, gov_topic_rule, true);
|
|
const char * def_perm_ca = PF_F COMMON_ETC_PATH("default_permissions_ca.pem");
|
|
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { id1, id2 },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { ca, ca },
|
|
(bool []) { exp_pp1_fail, exp_pp2_fail }, NULL, NULL,
|
|
(bool []) { true, true }, (const char *[]) { gov_config_pp1, gov_config_pp2 },
|
|
(bool []) { true, true }, (const char *[]) { perm_config, perm_config },
|
|
(bool []) { true, true }, (const char *[]) { def_perm_ca, def_perm_ca });
|
|
|
|
if (!exp_pp1_fail && !exp_pp2_fail)
|
|
validate_handshake (DDS_DOMAINID, exp_hs_fail, NULL, NULL, NULL, DDS_SECS(2));
|
|
|
|
access_control_fini (2, (void * []) { gov_config_pp1, gov_config_pp2, gov_topic_rule, rules1_xml, rules2_xml, grants[0], grants[1], perm_config, ca, id1_subj, id2_subj, id1, id2 }, 13);
|
|
}
|
|
|
|
#define na false
|
|
CU_TheoryDataPoints(ddssec_access_control, discovery_liveliness_protection) = {
|
|
CU_DataPoints(const char *,
|
|
/* */"disabled",
|
|
/* | */"enabled, protection kind none",
|
|
/* | | */"disabled, protection kind encrypt",
|
|
/* | | | */"enabled, protection kind encrypt",
|
|
/* | | | | */"enabled, protection kind sign",
|
|
/* | | | | | */"enabled, protection kind encrypt-with-origin_auth",
|
|
/* | | | | | | */"enabled for node 1, disabled for node 2",
|
|
/* | | | | | | | */"node 1 and node 2 different protection kinds"),
|
|
CU_DataPoints(bool, false, true, false, true, true, true, true, true), /* enable_discovery_protection for pp 1 */
|
|
CU_DataPoints(bool, false, true, false, true, true, true, false, true), /* enable_discovery_protection for pp 2 */
|
|
CU_DataPoints(DDS_Security_ProtectionKind, PK_N, PK_N, PK_E, PK_E, PK_S, PK_EOA, PK_E, PK_E), /* discovery_protection_kind pp 1 */
|
|
CU_DataPoints(DDS_Security_ProtectionKind, PK_N, PK_N, PK_E, PK_E, PK_S, PK_EOA, PK_N, PK_S), /* discovery_protection_kind pp 2 */
|
|
CU_DataPoints(bool, false, false, false, false, false, false, true, true), /* expect rd-wr match fail */
|
|
CU_DataPoints(bool, false, false, true, true, true, true, true, true), /* expect SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER of pp 1 to have a crypto handle */
|
|
CU_DataPoints(bool, na, na, true, true, true, true, false, false), /* expect encode_datawriter_submessage for SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER of pp 1 */
|
|
};
|
|
#undef na
|
|
|
|
enum test_discovery_liveliness
|
|
{
|
|
TEST_DISCOVERY,
|
|
TEST_LIVELINESS
|
|
};
|
|
|
|
static void test_discovery_liveliness_protection(enum test_discovery_liveliness kind,
|
|
bool enable_protection_pp1, bool enable_protection_pp2,
|
|
DDS_Security_ProtectionKind protection_kind_pp1, DDS_Security_ProtectionKind protection_kind_pp2,
|
|
bool exp_rd_wr_match_fail, bool exp_secure_pub_wr_handle, bool exp_secure_pub_wr_encode_decode)
|
|
{
|
|
bool dp = kind == TEST_DISCOVERY, lp = kind == TEST_LIVELINESS;
|
|
char topic_name[100];
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, topic_name, sizeof (topic_name));
|
|
|
|
/* create ca and id1/id2 certs that will not expire during this test */
|
|
char *ca, *id1, *id2, *id1_subj, *id2_subj;
|
|
ca = generate_ca ("ca1", TEST_IDENTITY_CA1_PRIVATE_KEY, 0, 3600);
|
|
id1 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id1", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id1_subj);
|
|
id2 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id2", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id2_subj);
|
|
|
|
char * grants[] = {
|
|
get_permissions_default_grant ("id1", id1_subj, topic_name),
|
|
get_permissions_default_grant ("id2", id2_subj, topic_name) };
|
|
char * perm_config = get_permissions_config (grants, 2, true);
|
|
|
|
char * gov_topic_rule1 = get_governance_topic_rule (topic_name, dp && enable_protection_pp1, lp && enable_protection_pp1, true, true, PK_E, BPK_N);
|
|
char * gov_topic_rule2 = get_governance_topic_rule (topic_name, dp && enable_protection_pp2, lp && enable_protection_pp2, true, true, PK_E, BPK_N);
|
|
char * gov_config1 = get_governance_config (false, true, dp ? protection_kind_pp1 : PK_N, lp ? protection_kind_pp1 : PK_N, PK_E, gov_topic_rule1, true);
|
|
char * gov_config2 = get_governance_config (false, true, dp ? protection_kind_pp2 : PK_N, lp ? protection_kind_pp2 : PK_N, PK_E, gov_topic_rule2, true);
|
|
const char * def_perm_ca = PF_F COMMON_ETC_PATH("default_permissions_ca.pem");
|
|
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { id1, id2 },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { ca, ca },
|
|
(bool []) { false, false }, NULL, NULL,
|
|
(bool []) { true, true }, (const char *[]) { gov_config1, gov_config2 },
|
|
(bool []) { true, true }, (const char *[]) { perm_config, perm_config },
|
|
(bool []) { true, true }, (const char *[]) { def_perm_ca, def_perm_ca });
|
|
validate_handshake (DDS_DOMAINID, false, NULL, NULL, NULL, DDS_SECS(2));
|
|
|
|
dds_entity_t pub, sub, pub_tp, sub_tp, wr, rd;
|
|
rd_wr_init (g_participant[0], &pub, &pub_tp, &wr, g_participant[1], &sub, &sub_tp, &rd, topic_name);
|
|
sync_writer_to_readers (g_participant[0], wr, exp_rd_wr_match_fail ? 0 : 1, dds_time() + DDS_SECS(2));
|
|
if (!exp_rd_wr_match_fail)
|
|
write_read_for (wr, g_participant[1], rd, DDS_MSECS (100), false, false);
|
|
|
|
unsigned builtin_wr = dp ? NN_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER : NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER;
|
|
const char * builtin_wr_descr = dp ? "SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER" : "P2P_BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER";
|
|
DDS_Security_DatawriterCryptoHandle secure_pub_wr_handle = get_builtin_writer_crypto_handle (g_participant[0], builtin_wr);
|
|
print_test_msg ("crypto handle for %s: %ld\n", builtin_wr_descr, secure_pub_wr_handle);
|
|
CU_ASSERT_EQUAL_FATAL (exp_secure_pub_wr_handle, secure_pub_wr_handle != 0);
|
|
|
|
struct dds_security_cryptography_impl * crypto_context_pub = get_cryptography_context (g_participant[0]);
|
|
CU_ASSERT_FATAL (crypto_context_pub != NULL);
|
|
|
|
struct crypto_encode_decode_data *log = get_encode_decode_log (crypto_context_pub, ENCODE_DATAWRITER_SUBMESSAGE, secure_pub_wr_handle);
|
|
CU_ASSERT_EQUAL_FATAL (exp_secure_pub_wr_handle && exp_secure_pub_wr_encode_decode, log != NULL);
|
|
if (log != NULL)
|
|
{
|
|
print_test_msg ("encode_datawriter_submessage count for %s: %u\n", builtin_wr_descr, log->count);
|
|
CU_ASSERT_FATAL (log->count > 0);
|
|
ddsrt_free (log);
|
|
}
|
|
|
|
access_control_fini (2, (void * []) { gov_config1, gov_config2, gov_topic_rule1, gov_topic_rule2, grants[0], grants[1], perm_config, ca, id1_subj, id2_subj, id1, id2 }, 12);
|
|
}
|
|
/* Testing discovery and liveliness protection by checking that encode_datawriter_submessage
|
|
is called for SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER and/or P2P_BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER
|
|
depending on the discovery and liveliness protection settings in security configuration. */
|
|
CU_Theory(
|
|
(const char * test_descr, bool enable_discovery_protection_pp1, bool enable_discovery_protection_pp2,
|
|
DDS_Security_ProtectionKind discovery_protection_kind_pp1, DDS_Security_ProtectionKind discovery_protection_kind_pp2,
|
|
bool exp_rd_wr_match_fail, bool exp_secure_pub_wr_handle, bool exp_secure_pub_wr_encode_decode),
|
|
ddssec_access_control, discovery_liveliness_protection, .timeout=40)
|
|
{
|
|
enum test_discovery_liveliness kinds[2] = { TEST_DISCOVERY, TEST_LIVELINESS };
|
|
for (size_t i = 0; i < sizeof (kinds) / sizeof (kinds[0]); i++)
|
|
{
|
|
print_test_msg ("running test %s_protection: %s\n", kinds[i] == TEST_DISCOVERY ? "discovery" : "liveliness", test_descr);
|
|
test_discovery_liveliness_protection (kinds[i], enable_discovery_protection_pp1, enable_discovery_protection_pp2,
|
|
discovery_protection_kind_pp1, discovery_protection_kind_pp2, exp_rd_wr_match_fail, exp_secure_pub_wr_handle, exp_secure_pub_wr_encode_decode);
|
|
}
|
|
}
|
|
|
|
static void test_encoding_mismatch(
|
|
bool exp_hs_fail, bool exp_rd_wr_fail,
|
|
DDS_Security_ProtectionKind rtps_pk1, DDS_Security_ProtectionKind rtps_pk2,
|
|
DDS_Security_ProtectionKind discovery_pk1, DDS_Security_ProtectionKind discovery_pk2,
|
|
DDS_Security_ProtectionKind liveliness_pk1, DDS_Security_ProtectionKind liveliness_pk2,
|
|
DDS_Security_ProtectionKind metadata_pk1, DDS_Security_ProtectionKind metadata_pk2,
|
|
DDS_Security_BasicProtectionKind payload_pk1, DDS_Security_BasicProtectionKind payload_pk2)
|
|
{
|
|
char topic_name[100];
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, topic_name, sizeof (topic_name));
|
|
|
|
/* create ca and id1/id2 certs that will not expire during this test */
|
|
char *ca, *id1, *id2, *id1_subj, *id2_subj;
|
|
ca = generate_ca ("ca1", TEST_IDENTITY_CA1_PRIVATE_KEY, 0, 3600);
|
|
id1 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id1", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id1_subj);
|
|
id2 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id2", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id2_subj);
|
|
|
|
char * grants[] = {
|
|
get_permissions_default_grant ("id1", id1_subj, topic_name),
|
|
get_permissions_default_grant ("id2", id2_subj, topic_name) };
|
|
char * perm_config = get_permissions_config (grants, 2, true);
|
|
|
|
char * gov_topic_rule1 = get_governance_topic_rule (topic_name, true, true, true, true, metadata_pk1, payload_pk1);
|
|
char * gov_topic_rule2 = get_governance_topic_rule (topic_name, true, true, true, true, metadata_pk2, payload_pk2);
|
|
char * gov_config1 = get_governance_config (false, true, discovery_pk1, liveliness_pk1, rtps_pk1, gov_topic_rule1, true);
|
|
char * gov_config2 = get_governance_config (false, true, discovery_pk2, liveliness_pk2, rtps_pk2, gov_topic_rule2, true);
|
|
const char * def_perm_ca = PF_F COMMON_ETC_PATH("default_permissions_ca.pem");
|
|
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { id1, id2 },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { ca, ca },
|
|
(bool []) { false, false }, NULL, NULL,
|
|
(bool []) { true, true }, (const char *[]) { gov_config1, gov_config2 },
|
|
(bool []) { true, true }, (const char *[]) { perm_config, perm_config },
|
|
(bool []) { true, true }, (const char *[]) { def_perm_ca, def_perm_ca });
|
|
|
|
struct Handshake *hs_list;
|
|
int nhs;
|
|
validate_handshake (DDS_DOMAINID, false, NULL, &hs_list, &nhs, DDS_SECS(2));
|
|
CU_ASSERT_EQUAL_FATAL (exp_hs_fail, nhs < 1);
|
|
handshake_list_fini (hs_list, nhs);
|
|
|
|
if (!exp_hs_fail)
|
|
{
|
|
dds_entity_t pub, sub, pub_tp, sub_tp, wr, rd;
|
|
rd_wr_init (g_participant[0], &pub, &pub_tp, &wr, g_participant[1], &sub, &sub_tp, &rd, topic_name);
|
|
sync_writer_to_readers (g_participant[0], wr, exp_rd_wr_fail ? 0 : 1, dds_time() + DDS_SECS(1));
|
|
}
|
|
|
|
access_control_fini (2, (void * []) { gov_config1, gov_config2, gov_topic_rule1, gov_topic_rule2, grants[0], grants[1], perm_config, ca, id1_subj, id2_subj, id1, id2 }, 12);
|
|
}
|
|
|
|
/* Testing handshake result for any combination of protection kind values for rtps, discovery,
|
|
liveliness, metadata (submsg) and payload encoding. In all cases where there is an encoding
|
|
mismatch, the security handshake is expect to fail */
|
|
static DDS_Security_ProtectionKind pk[] = { PK_N, PK_S, PK_E, PK_SOA, PK_EOA };
|
|
static DDS_Security_BasicProtectionKind bpk[] = { BPK_N, BPK_S, BPK_E };
|
|
|
|
CU_Test(ddssec_access_control, encoding_mismatch_rtps, .timeout=30)
|
|
{
|
|
for (size_t pk1 = 0; pk1 < sizeof (pk) / sizeof (pk[0]); pk1++)
|
|
for (size_t pk2 = pk1 + 1; pk2 < sizeof (pk) / sizeof (pk[0]); pk2++)
|
|
test_encoding_mismatch (pk1 != pk2, false, pk[pk1], pk[pk2], PK_N, PK_N, PK_N, PK_N, PK_N, PK_N, BPK_N, BPK_N);
|
|
}
|
|
|
|
CU_Test(ddssec_access_control, encoding_mismatch_discovery, .timeout=30)
|
|
{
|
|
for (size_t pk1 = 0; pk1 < sizeof (pk) / sizeof (pk[0]); pk1++)
|
|
for (size_t pk2 = pk1 + 1; pk2 < sizeof (pk) / sizeof (pk[0]); pk2++)
|
|
test_encoding_mismatch (pk1 != pk2, false, PK_N, PK_N, pk[pk1], pk[pk2], PK_N, PK_N, PK_N, PK_N, BPK_N, BPK_N);
|
|
}
|
|
|
|
CU_Test(ddssec_access_control, encoding_mismatch_liveliness, .timeout=30)
|
|
{
|
|
for (size_t pk1 = 0; pk1 < sizeof (pk) / sizeof (pk[0]); pk1++)
|
|
for (size_t pk2 = pk1 + 1; pk2 < sizeof (pk) / sizeof (pk[0]); pk2++)
|
|
test_encoding_mismatch (pk1 != pk2, false, PK_N, PK_N, PK_N, PK_N, pk[pk1], pk[pk2], PK_N, PK_N, BPK_N, BPK_N);
|
|
}
|
|
|
|
CU_Test(ddssec_access_control, encoding_mismatch_metadata, .timeout=30)
|
|
{
|
|
for (size_t pk1 = 0; pk1 < sizeof (pk) / sizeof (pk[0]); pk1++)
|
|
for (size_t pk2 = pk1 + 1; pk2 < sizeof (pk) / sizeof (pk[0]); pk2++)
|
|
test_encoding_mismatch (false, pk1 != pk2, PK_N, PK_N, PK_N, PK_N, PK_N, PK_N, pk[pk1], pk[pk2], BPK_N, BPK_N);
|
|
}
|
|
|
|
CU_Test(ddssec_access_control, encoding_mismatch_payload, .timeout=30)
|
|
{
|
|
for (size_t pk1 = 0; pk1 < sizeof (bpk) / sizeof (bpk[0]); pk1++)
|
|
for (size_t pk2 = pk1 + 1; pk2 < sizeof (bpk) / sizeof (bpk[0]); pk2++)
|
|
test_encoding_mismatch (false, pk1 != pk2, PK_N, PK_N, PK_N, PK_N, PK_N, PK_N, PK_N, PK_N, bpk[pk1], bpk[pk2]);
|
|
}
|
|
|
|
|
|
static void test_readwrite_protection (
|
|
bool allow_pub, bool allow_sub, bool deny_pub, bool deny_sub, bool write_ac, bool read_ac,
|
|
bool exp_pub_pp_fail, bool exp_pub_tp_fail, bool exp_wr_fail,
|
|
bool exp_sub_pp_fail, bool exp_sub_tp_fail, bool exp_rd_fail,
|
|
bool exp_sync_fail, const char * default_policy)
|
|
{
|
|
print_test_msg ("running test readwrite_protection: \n");
|
|
print_test_msg ("- allow_pub=%d | allow_sub=%d | deny_pub=%d | deny_sub=%d | write_ac=%d | read_ac=%d | default_policy=%s\n", allow_pub, allow_sub, deny_pub, deny_sub, write_ac, read_ac, default_policy);
|
|
print_test_msg ("- exp_pub_pp_fail=%d | exp_pub_tp_fail=%d | exp_wr_fail=%d | exp_sub_pp_fail=%d | exp_sub_tp_fail=%d | exp_rd_fail=%d | exp_sync_fail=%d\n", exp_pub_pp_fail, exp_pub_tp_fail, exp_wr_fail, exp_sub_pp_fail, exp_sub_tp_fail, exp_rd_fail, exp_sync_fail);
|
|
|
|
char topic_name[100];
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, topic_name, sizeof (topic_name));
|
|
|
|
/* create ca and id1/id2 certs that will not expire during this test */
|
|
char *ca, *id1, *id2, *id1_subj, *id2_subj;
|
|
ca = generate_ca ("ca1", TEST_IDENTITY_CA1_PRIVATE_KEY, 0, 3600);
|
|
id1 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id1", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id1_subj);
|
|
id2 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id2", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id2_subj);
|
|
|
|
dds_time_t now = dds_time ();
|
|
char * rules_xml = get_permissions_rules (NULL, allow_pub ? topic_name : NULL, allow_sub ? topic_name : NULL, deny_pub ? topic_name : NULL, deny_sub ? topic_name : NULL);
|
|
char * grants[] = {
|
|
get_permissions_grant ("id1", id1_subj, now, now + DDS_SECS(3600), rules_xml, default_policy),
|
|
get_permissions_grant ("id2", id2_subj, now, now + DDS_SECS(3600), rules_xml, default_policy) };
|
|
char * perm_config = get_permissions_config (grants, 2, true);
|
|
|
|
char * gov_topic_rule = get_governance_topic_rule (topic_name, false, false, read_ac, write_ac, PK_E, BPK_E);
|
|
char * gov_config = get_governance_config (false, true, PK_N, PK_N, PK_N, gov_topic_rule, true);
|
|
const char * def_perm_ca = PF_F COMMON_ETC_PATH("default_permissions_ca.pem");
|
|
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { id1, id2 },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { ca, ca },
|
|
(bool []) { exp_pub_pp_fail, exp_sub_pp_fail }, NULL, NULL,
|
|
(bool []) { true, true }, (const char *[]) { gov_config, gov_config },
|
|
(bool []) { true, true }, (const char *[]) { perm_config, perm_config },
|
|
(bool []) { true, true }, (const char *[]) { def_perm_ca, def_perm_ca });
|
|
|
|
if (!exp_pub_pp_fail && !exp_sub_pp_fail)
|
|
{
|
|
dds_entity_t pub, sub, pub_tp, sub_tp, wr, rd;
|
|
validate_handshake_nofail (DDS_DOMAINID, DDS_SECS(2));
|
|
rd_wr_init_fail (g_participant[0], &pub, &pub_tp, &wr, g_participant[1], &sub, &sub_tp, &rd, topic_name, exp_pub_tp_fail, exp_wr_fail, exp_sub_tp_fail, exp_rd_fail);
|
|
if (!exp_pub_tp_fail && !exp_wr_fail && !exp_sub_tp_fail && !exp_rd_fail)
|
|
sync_writer_to_readers (g_participant[0], wr, exp_sync_fail ? 0 : 1, dds_time() + DDS_SECS(1));
|
|
}
|
|
|
|
access_control_fini (2, (void * []) { gov_config, gov_topic_rule, rules_xml, grants[0], grants[1], perm_config, ca, id1_subj, id2_subj, id1, id2 }, 11);
|
|
}
|
|
/* Test read/write access control by running test cases with different combinations
|
|
of allow and deny rules for publishing and subscribing on a topic, and check correct
|
|
working of the default policy. */
|
|
CU_Test(ddssec_access_control, readwrite_protection, .timeout=60)
|
|
{
|
|
for (int allow_pub = 0; allow_pub <= 1; allow_pub++)
|
|
for (int allow_sub = 0; allow_sub <= 1; allow_sub++)
|
|
for (int deny_pub = 0; deny_pub <= 1 && !(allow_pub && deny_pub); deny_pub++)
|
|
for (int deny_sub = 0; deny_sub <= 1 && !(allow_sub && deny_sub); deny_sub++)
|
|
for (int write_ac = 0; write_ac <= 1; write_ac++)
|
|
for (int read_ac = 0; read_ac <= 1; read_ac++)
|
|
for (int default_deny = 0; default_deny <= 1; default_deny++)
|
|
{
|
|
/* creating local writer/reader fails if write_ac/read_ac enabled and no allow-pub/sub or a deny-pub/sub rule exists */
|
|
bool exp_wr_fail = write_ac && !allow_pub && (deny_pub || default_deny);
|
|
bool exp_rd_fail = read_ac && !allow_sub && (deny_sub || default_deny);
|
|
/* if both read_ac and write_ac are enabled, and pub and sub not allowed, topic creation should fail */
|
|
bool exp_tp_fail = write_ac && read_ac && !allow_pub && !allow_sub && (deny_pub || deny_sub || default_deny);
|
|
/* participant creation should fail under same conditions as topic creation (as opposed to the DDS Security spec,
|
|
table 63, that states that participant creation fails when there is not any topic that has enable_read/write_ac
|
|
set to false and join_ac is enabled; it seems that the allow_rule condition is missing there) */
|
|
bool exp_pp_fail = write_ac && read_ac && !allow_pub && !allow_sub && default_deny;
|
|
/* join-ac is enabled in this test, so check_remote_pp fails (and therefore the reader/writer sync) in case not any allow rule exists */
|
|
bool exp_sync_fail = !allow_pub && !allow_sub && default_deny;
|
|
|
|
test_readwrite_protection (allow_pub, allow_sub, deny_pub, deny_sub, write_ac, read_ac,
|
|
exp_pp_fail, exp_tp_fail, exp_wr_fail, exp_pp_fail, exp_tp_fail, exp_rd_fail, exp_sync_fail, default_deny ? "DENY" : "ALLOW");
|
|
}
|
|
}
|
|
|
|
/* Check that communication for a topic that is allowed in the permissions config
|
|
keeps working in case the publisher also creates a writer for a non-allowed topic */
|
|
CU_Test(ddssec_access_control, denied_topic)
|
|
{
|
|
char topic_name[100], denied_topic_name[100];
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, topic_name, sizeof (topic_name));
|
|
create_topic_name ("ddssec_access_control_", g_topic_nr++, denied_topic_name, sizeof (denied_topic_name));
|
|
|
|
char *ca, *id1, *id2, *id1_subj, *id2_subj;
|
|
ca = generate_ca ("ca1", TEST_IDENTITY_CA1_PRIVATE_KEY, 0, 3600);
|
|
id1 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id1", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id1_subj);
|
|
id2 = generate_identity (ca, TEST_IDENTITY_CA1_PRIVATE_KEY, "id2", TEST_IDENTITY1_PRIVATE_KEY, 0, 3600, &id2_subj);
|
|
|
|
dds_time_t now = dds_time ();
|
|
char * sub_rules_xml = get_permissions_rules (NULL, NULL, NULL, denied_topic_name, denied_topic_name);
|
|
char * grants_pub[] = { get_permissions_grant ("id1", id1_subj, now, now + DDS_SECS(3600), NULL, "ALLOW") };
|
|
char * grants_sub[] = { get_permissions_grant ("id2", id2_subj, now, now + DDS_SECS(3600), sub_rules_xml, "ALLOW") };
|
|
char * perm_config_pub = get_permissions_config (grants_pub, 1, true);
|
|
char * perm_config_sub = get_permissions_config (grants_sub, 1, true);
|
|
|
|
char * gov_topic_rule = get_governance_topic_rule (NULL, true, true, true, true, PK_E, BPK_E);
|
|
char * gov_config = get_governance_config (false, true, PK_E, PK_E, PK_E, gov_topic_rule, true);
|
|
const char * def_perm_ca = PF_F COMMON_ETC_PATH("default_permissions_ca.pem");
|
|
|
|
access_control_init (
|
|
2,
|
|
(const char *[]) { id1, id2 },
|
|
(const char *[]) { TEST_IDENTITY1_PRIVATE_KEY, TEST_IDENTITY1_PRIVATE_KEY },
|
|
(const char *[]) { ca, ca },
|
|
(bool []) { false, false },
|
|
NULL, NULL,
|
|
(bool []) { true, true }, (const char *[]) { gov_config, gov_config },
|
|
(bool []) { true, true }, (const char *[]) { perm_config_pub, perm_config_sub },
|
|
(bool []) { true, true }, (const char *[]) { def_perm_ca, def_perm_ca });
|
|
|
|
dds_entity_t pub, sub, pub_tp, sub_tp, wr, rd;
|
|
rd_wr_init (g_participant[0], &pub, &pub_tp, &wr, g_participant[1], &sub, &sub_tp, &rd, topic_name);
|
|
|
|
const dds_time_t sync_abstimeout = dds_time () + DDS_SECS (2);
|
|
sync_writer_to_readers (g_participant[0], wr, 1, sync_abstimeout);
|
|
sync_reader_to_writers (g_participant[1], rd, 1, sync_abstimeout);
|
|
|
|
/* Create a topic that is denied in the subscriber pp security config */
|
|
dds_entity_t denied_pub_tp = dds_create_topic (g_participant[0], &SecurityCoreTests_Type1_desc, denied_topic_name, NULL, NULL);
|
|
CU_ASSERT_FATAL (denied_pub_tp > 0);
|
|
dds_qos_t * qos = get_default_test_qos ();
|
|
dds_entity_t denied_tp_wr = dds_create_writer (pub, denied_pub_tp, qos, NULL);
|
|
CU_ASSERT_FATAL (denied_tp_wr > 0);
|
|
|
|
/* Check that creating denied topic for subscriber fails */
|
|
dds_entity_t denied_sub_tp = dds_create_topic (g_participant[1], &SecurityCoreTests_Type1_desc, denied_topic_name, NULL, NULL);
|
|
CU_ASSERT_FATAL (denied_sub_tp == DDS_RETCODE_NOT_ALLOWED_BY_SECURITY);
|
|
|
|
/* Check if communication for allowed topic is still working */
|
|
write_read_for (wr, g_participant[1], rd, DDS_MSECS (10), false, false);
|
|
|
|
dds_delete_qos (qos);
|
|
access_control_fini (2, (void * []) { gov_config, gov_topic_rule, sub_rules_xml, grants_pub[0], grants_sub[0], perm_config_pub, perm_config_sub, ca, id1_subj, id2_subj, id1, id2 }, 12);
|
|
}
|