/* * 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 #include #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/q_entity.h" #include "dds/ddsi/ddsi_entity_index.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:+,}" "" " " " 0" " \\${CYCLONEDDS_PID}" " " " " " " " " " data:,${TEST_IDENTITY_CERTIFICATE}" " data:,${TEST_IDENTITY_PRIVATE_KEY}" " data:,${TEST_IDENTITY_CA_CERTIFICATE}" " " " " " " " ${INCL_GOV:+}" " ${INCL_PERM_CA:+}${TEST_PERMISSIONS_CA}${INCL_PERM_CA:+}" " ${INCL_PERM:+}" " " " " " " " " " " ""; #define MAX_DOMAINS 10 #define DDS_DOMAINID 0 #define PF_F "file:" #define PF_D "data:," 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++) { 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) { for (size_t i = 0; i < n; i++) CU_ASSERT_EQUAL_FATAL (dds_delete (g_domain[i]), DDS_RETCODE_OK); } static DDS_Security_DatawriterCryptoHandle get_builtin_writer_crypto_handle(dds_entity_t participant, unsigned entityid) { DDS_Security_DatawriterCryptoHandle crypto_handle; struct dds_entity *pp_entity; struct participant *pp; struct writer *wr; CU_ASSERT_EQUAL_FATAL(dds_entity_pin(participant, &pp_entity), 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); wr = get_builtin_writer(pp, entityid); CU_ASSERT_FATAL(wr != NULL); assert(wr != NULL); /* for Clang's static analyzer */ crypto_handle = wr->sec_attr->crypto_handle; thread_state_asleep(lookup_thread_state()); dds_entity_unpin(pp_entity); return crypto_handle; } // static DDS_Security_DatawriterCryptoHandle get_writer_crypto_handle(dds_entity_t writer) // { // DDS_Security_DatawriterCryptoHandle crypto_handle; // struct dds_entity *wr_entity; // struct writer *wr; // CU_ASSERT_EQUAL_FATAL(dds_entity_pin(writer, &wr_entity), 0); // thread_state_awake(lookup_thread_state(), &wr_entity->m_domain->gv); // wr = entidx_lookup_writer_guid(wr_entity->m_domain->gv.entity_index, &wr_entity->m_guid); // CU_ASSERT_FATAL(wr != NULL); // assert(wr != NULL); /* for Clang's static analyzer */ // crypto_handle = wr->sec_attr->crypto_handle; // thread_state_asleep(lookup_thread_state()); // dds_entity_unpin(wr_entity); // return crypto_handle; // } #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 }; 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); } #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 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 * perm_topic = get_permissions_topic (topic_name); char * grants[] = { get_permissions_grant ("id1", id1_subj, NULL, now + DDS_SECS(perm1_not_before), now + DDS_SECS(perm1_not_after), perm_topic, perm_topic, NULL), get_permissions_grant ("id2", id2_subj, NULL, now + DDS_SECS(perm2_not_before), now + DDS_SECS(perm2_not_after), perm_topic, perm_topic, 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); write_read_for (wr, g_participant[1], rd, DDS_MSECS (write_read_dur), false, exp_read_fail); } access_control_fini (2); ddsrt_free (perm_topic); ddsrt_free (grants[0]); ddsrt_free (grants[1]); ddsrt_free (perm_config); ddsrt_free (ca); ddsrt_free (id1_subj); ddsrt_free (id2_subj); ddsrt_free (id1); ddsrt_free (id2); } #define N_RD 1 // N_RD > 1 not yet implemented #define N_WR 3 #define N_NODES (N_RD + N_WR) #define PERM_EXP_BASE 3 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 *perm_topic = get_permissions_topic (topic_name); // 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 < N_RD ? 3600 : PERM_EXP_BASE + 2 * i); /* readers should not expire */ dds_time_t t_exp = ddsrt_time_add_duration (t_perm, v); if (i >= N_RD) print_test_msg ("w[%d] grant expires at %d.%06d\n", i - N_RD, (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], NULL, t_perm, t_exp, perm_topic, perm_topic, 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); dds_qos_t * qos = dds_create_qos (); CU_ASSERT_FATAL (qos != NULL); dds_qset_history (qos, DDS_HISTORY_KEEP_ALL, -1); dds_qset_durability (qos, DDS_DURABILITY_TRANSIENT_LOCAL); dds_qset_reliability (qos, DDS_RELIABILITY_RELIABLE, DDS_INFINITY); dds_entity_t rd[N_RD]; for (int i = 0; i < N_RD; i++) { dds_entity_t sub = dds_create_subscriber (g_participant[i], NULL, NULL); CU_ASSERT_FATAL (sub > 0); dds_entity_t sub_tp = dds_create_topic (g_participant[i], &SecurityCoreTests_Type1_desc, topic_name, NULL, NULL); CU_ASSERT_FATAL (sub_tp > 0); rd[i] = dds_create_reader (sub, sub_tp, qos, NULL); CU_ASSERT_FATAL (rd[i] > 0); dds_set_status_mask (rd[i], DDS_DATA_AVAILABLE_STATUS); } dds_entity_t wr[N_WR]; for (int i = 0; i < N_WR; i++) { dds_entity_t pub = dds_create_publisher (g_participant[i + N_RD], NULL, NULL); CU_ASSERT_FATAL (pub > 0); dds_entity_t pub_tp = dds_create_topic (g_participant[i + N_RD], &SecurityCoreTests_Type1_desc, topic_name, NULL, NULL); CU_ASSERT_FATAL (pub_tp > 0); wr[i] = dds_create_writer (pub, pub_tp, qos, NULL); CU_ASSERT_FATAL (wr[i] > 0); dds_set_status_mask (wr[i], DDS_PUBLICATION_MATCHED_STATUS); sync_writer_to_readers (g_participant[i + N_RD], wr[i], N_RD); } dds_delete_qos (qos); SecurityCoreTests_Type1 sample = { 1, 1 }; SecurityCoreTests_Type1 rd_sample; void * samples[] = { &rd_sample }; dds_sample_info_t info[1]; dds_return_t ret; for (int run = 0; run < N_WR; run++) { // sleep until 1s after next writer pp permission expires dds_duration_t delay = DDS_SECS (PERM_EXP_BASE + 2 * run + 1) - (dds_time () - t_perm); if (delay > 0) dds_sleepfor (delay); print_test_msg ("run %d\n", run); for (int w = run; w < N_WR; w++) { sample.id = w; ret = dds_write (wr[w], &sample); CU_ASSERT_EQUAL_FATAL (ret, DDS_RETCODE_OK); print_test_msg ("write %d\n", w); } // Expect reader to receive data from writers with non-expired permissions int n_samples = 0, n_invalid = 0, n_wait = 0; while (n_samples + n_invalid < N_WR && n_wait < 5) { ret = dds_take (rd[0], samples, info, 1, 1); CU_ASSERT_FATAL (ret >= 0); if (ret == 0) { reader_wait_for_data (g_participant[0], rd[0], DDS_MSECS (200)); print_test_msg ("wait for data\n"); n_wait++; } else if (info[0].instance_state == DDS_IST_ALIVE) { print_test_msg ("recv sample %d\n", rd_sample.id); n_samples++; } else { print_test_msg ("recv inv sample\n"); n_invalid++; } } CU_ASSERT_EQUAL (n_samples, N_WR - run); CU_ASSERT (n_invalid <= run); } access_control_fini (N_NODES); for (int i = 0; i < N_NODES; i++) { ddsrt_free (grants[i]); ddsrt_free (id_subj[i]); ddsrt_free ((char *)id[i]); } ddsrt_free (ca); ddsrt_free (perm_topic); ddsrt_free (perm_config_str); } #undef N_RD #undef N_WR #undef N_NODES #undef PERM_EXP_BASE #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_topic_not_allowed", /* | | */"init_test_access_control_local_publishing_not_allowed", /* | | | */"init_test_access_control_local_subscribing_not_allowed", /* | | | | */"init_test_access_control_remote_permissions_invalidate", /* | | | | | */"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, false, false, false, false, false, false, false, false, false), // exp_pp_fail CU_DataPoints(bool, na, true, false, false, false, false, false, false, false, false), // exp_local_topic_fail CU_DataPoints(bool, na, false, false, false, false, false, false, false, false, false), // exp_remote_topic_fail CU_DataPoints(bool, na, na, true, false, false, false, false, false, false, false), // exp_wr_fail CU_DataPoints(bool, na, na, false, true, false, false, false, false, false, false), // exp_rd_fail CU_DataPoints(bool, na, na, na, na, true, true, true, false, true, true), // exp_wr_rd_sync_fail CU_DataPoints(bool, na, na, false, na, true, true, true, true, false, false), // exp_rd_wr_sync_fail }; #undef na 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=40) { print_test_msg ("running test access_control_hooks: %s\n", init_fn); const char * def_gov = PF_F COMMON_ETC_PATH("default_governance.p7s"); 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 *[]) { def_gov, def_gov }, (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) { 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); 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); // 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_rd_fail) sync_reader_to_writers (g_participant[0], lrd, exp_rd_wr_sync_fail ? 0 : 1); } access_control_fini (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 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); /* localtime will be converted to gmtime in get_permissions_grant */ dds_time_t now = dds_time (); char * perm_topic = get_permissions_topic (topic_name); char * grants[] = { get_permissions_grant ("id1", id1_subj, perm_inv_pp1 ? "99" : NULL, now, now + DDS_SECS(3600), perm_topic, perm_topic, NULL), get_permissions_grant ("id2", id2_subj, perm_inv_pp2 ? "99" : NULL, now, now + DDS_SECS(3600), perm_topic, perm_topic, NULL) }; char * perm_config = get_permissions_config (grants, 2, true); char * gov_topic_rule = get_governance_topic_rule ("*", false, false, true, true, "NONE", "NONE"); char * gov_config_pp1 = get_governance_config (false, join_ac_pp1, NULL, NULL, NULL, gov_topic_rule, true); char * gov_config_pp2 = get_governance_config (false, join_ac_pp2, NULL, NULL, NULL, 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); access_control_fini (2); ddsrt_free (gov_config_pp1); ddsrt_free (gov_config_pp2); ddsrt_free (gov_topic_rule); ddsrt_free (perm_topic); ddsrt_free (grants[0]); ddsrt_free (grants[1]); ddsrt_free (perm_config); ddsrt_free (ca); ddsrt_free (id1_subj); ddsrt_free (id2_subj); ddsrt_free (id1); ddsrt_free (id2); } #define na false #define E ENCRYPT CU_TheoryDataPoints(ddssec_access_control, discovery_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 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_protection, .timeout=30) { print_test_msg ("running test discovery_protection: %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 * perm_topic = get_permissions_topic (topic_name); char * grants[] = { get_permissions_grant ("id1", id1_subj, NULL, now, now + DDS_SECS(3600), perm_topic, perm_topic, NULL), get_permissions_grant ("id2", id2_subj, NULL, now, now + DDS_SECS(3600), perm_topic, perm_topic, NULL) }; char * perm_config = get_permissions_config (grants, 2, true); char * gov_topic_rule1 = get_governance_topic_rule (topic_name, enable_discovery_protection_pp1, false, true, true, "ENCRYPT", "NONE"); char * gov_topic_rule2 = get_governance_topic_rule (topic_name, enable_discovery_protection_pp2, false, true, true, "ENCRYPT", "NONE"); char * gov_config1 = get_governance_config (false, true, pk_to_str (discovery_protection_kind_pp1), NULL, "ENCRYPT", gov_topic_rule1, true); char * gov_config2 = get_governance_config (false, true, pk_to_str (discovery_protection_kind_pp2), NULL, "ENCRYPT", 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_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); if (!exp_rd_wr_match_fail) write_read_for (wr, g_participant[1], rd, DDS_MSECS (100), false, false); DDS_Security_DatawriterCryptoHandle secure_pub_wr_handle = get_builtin_writer_crypto_handle (g_participant[0], NN_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER); print_test_msg ("crypto handle for SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER: %ld\n", 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_crypto_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 SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER: %u\n", log->count); CU_ASSERT_FATAL (log->count > 0); ddsrt_free (log); } access_control_fini (2); ddsrt_free (gov_config1); ddsrt_free (gov_config2); ddsrt_free (gov_topic_rule1); ddsrt_free (gov_topic_rule2); ddsrt_free (perm_topic); ddsrt_free (grants[0]); ddsrt_free (grants[1]); ddsrt_free (perm_config); ddsrt_free (ca); ddsrt_free (id1_subj); ddsrt_free (id2_subj); ddsrt_free (id1); ddsrt_free (id2); }