diff --git a/src/core/ddsi/CMakeLists.txt b/src/core/ddsi/CMakeLists.txt index e0aaaa8..75c2342 100644 --- a/src/core/ddsi/CMakeLists.txt +++ b/src/core/ddsi/CMakeLists.txt @@ -20,6 +20,7 @@ PREPEND(srcs_ddsi "${CMAKE_CURRENT_LIST_DIR}/src" ddsi_mcgroup.c ddsi_security_omg.c ddsi_portmapping.c + ddsi_handshake.c ddsi_serdata.c ddsi_serdata_default.c ddsi_sertopic.c @@ -77,6 +78,7 @@ PREPEND(hdrs_private_ddsi "${CMAKE_CURRENT_LIST_DIR}/include/dds/ddsi" ddsi_plist_generic.h ddsi_security_omg.h ddsi_portmapping.h + ddsi_handshake.h ddsi_serdata.h ddsi_sertopic.h ddsi_serdata_default.h diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_entity_index.h b/src/core/ddsi/include/dds/ddsi/ddsi_entity_index.h index 78a353e..1566feb 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_entity_index.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_entity_index.h @@ -112,6 +112,7 @@ struct entidx_enum_proxy_reader { struct entidx_enum st; }; void entidx_enum_init (struct entidx_enum *st, const struct entity_index *ei, enum entity_kind kind) ddsrt_nonnull_all; void entidx_enum_init_topic (struct entidx_enum *st, const struct entity_index *gh, enum entity_kind kind, const char *topic, struct match_entities_range_key *max) ddsrt_nonnull_all; +void entidx_enum_init_topic_w_prefix (struct entidx_enum *st, const struct entity_index *ei, enum entity_kind kind, const char *topic, const ddsi_guid_prefix_t *prefix, struct match_entities_range_key *max) ddsrt_nonnull_all; void *entidx_enum_next_max (struct entidx_enum *st, const struct match_entities_range_key *max) ddsrt_nonnull_all; void *entidx_enum_next (struct entidx_enum *st) ddsrt_nonnull_all; void entidx_enum_fini (struct entidx_enum *st) ddsrt_nonnull_all; diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_handshake.h b/src/core/ddsi/include/dds/ddsi/ddsi_handshake.h new file mode 100644 index 0000000..1aef76d --- /dev/null +++ b/src/core/ddsi/include/dds/ddsi/ddsi_handshake.h @@ -0,0 +1,197 @@ +/* + * Copyright(c) 2006 to 2018 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 + */ +#ifndef DDSI_HANDSHAKE_H +#define DDSI_HANDSHAKE_H + +#include "q_unused.h" +#include "q_entity.h" +#include "ddsi_security_msg.h" + +#if defined (__cplusplus) +extern "C" { +#endif + +struct participant; +struct proxy_participant; +struct ddsi_handshake; +struct dssi_hsadmin; + +enum ddsi_handshake_state { + STATE_HANDSHAKE_IN_PROGRESS, + STATE_HANDSHAKE_TIMED_OUT, + STATE_HANDSHAKE_FAILED, + STATE_HANDSHAKE_PROCESSED, + STATE_HANDSHAKE_SEND_TOKENS, + STATE_HANDSHAKE_OK +}; + +/* The handshake will not use the related handshake object after this callback + * was executed. This means that it can be deleted in this callback. */ +typedef void (*ddsi_handshake_end_cb_t)( + struct q_globals const * const gv, + struct ddsi_handshake *handshake, + const ddsi_guid_t *lpguid, /* Local participant */ + const ddsi_guid_t *ppguid, /* Proxy participant */ + enum ddsi_handshake_state result); + +#ifdef DDSI_INCLUDE_SECURITY + +#include "dds/ddsi/ddsi_security_msg.h" + +/** + * @brief Release the handshake. + * + * This function will decrement the refcount associated with the handshake + * and delete the handshake when the refcount becomes 0. + * + * @param[in] handshake The handshake. + */ +void ddsi_handshake_release(struct ddsi_handshake *handshake); + +/** + * @brief Handle an authentication handshake message received from the remote participant. + * + * During the authentication phase handshake messages are being exchanged between the local and + * the remote participant. THis function will handle a handshake message received from a remote + * participant. + * + * @param[in] handshake The handshake. + * @param[in] pp The local participant. + * @param[in] proxypp The remote participant. + * @param[in] msg The handshake message received. + */ +void ddsi_handshake_handle_message(struct ddsi_handshake *handshake, const struct participant *pp, const struct proxy_participant *proxypp, const struct nn_participant_generic_message *msg); + +/** + * @brief Notify the handshake that crypto tokens have been received. + * + * The handshake could be finished at one end while the other side has not yet processed the + * final handshake messages. The arrival of crypto tokens signals that the other side has also finished + * processing the handshake. This function is used to signal the handshake that crypto tokens have been + * received. + * + * @param[in] handshake The handshake. + */ +void ddsi_handshake_crypto_tokens_received(struct ddsi_handshake *handshake); + +/** + * @brief Get the shared secret handle. + * + * During the handshake a shared secret is established which is used to encrypt + * and decrypt the crypto token exchange messages. This function will return a + * handle to the shared secret which will be passed to the crypto plugin to + * determine the session keys used for the echange of the the crypto tokens. + * + * @param[in] handshake The handshake. + * + * @returns handle to the shared sercet. + */ +int64_t ddsi_handshake_get_shared_secret(const struct ddsi_handshake *handshake); + +/** + * @brief Get the handshake handle + * + * This function returns the handshake handle that was returned by the authentication plugin + * when starting the handshake. + * + * @param[in] handshake The handshake. + * + * @returns The handshake handle. + */ +int64_t ddsi_handshake_get_handle(const struct ddsi_handshake *handshake); + +/** + * @brief Create and start the handshake for the participants + * + * This function will create a handshake for the specified local + * and remote participants when it does not yet exists. It will start the + * handshake procedure by calling the corresponding functions of the authentication plugin. + * The callback function is called by the handshake when to report events, + * for example to indicate that the handshake has finished or has failed. + * + * @param[in] pp The local participant. + * @param[in] proxypp The remote participant. + * @param[in] callback The callback function. + * + */ +void ddsi_handshake_register(const struct participant *pp, const struct proxy_participant *proxypp, ddsi_handshake_end_cb_t callback); + +/** + * @brief Remove the handshake associated with the specified participants. + * + * This function will remove the handshake from the handshake administation and release + * the handshake. When the handshake argument is not specified the handshake is searched + * in the handshake administation. + * + * @param[in] pp The local participant. + * @param[in] proxypp The remote participant. + * @param[in] handshake The handshake. + * + */ +void ddsi_handshake_remove(const struct participant *pp, const struct proxy_participant *proxypp, struct ddsi_handshake *handshake); + +/** + * @brief Searches for the handshake associated with the specified participants + * + * This function will search through the handshake administration to find the handshake + * corresponding the to specified local and remote participant. + * + * @param[in] pp The local participant. + * @param[in] proxypp The remote participant. + * + * @returns The handshake + */ +struct ddsi_handshake * ddsi_handshake_find(const struct participant *pp, const struct proxy_participant *proxypp); + +#else /* DDSI_INCLUDE_SECURITY */ + +#include "dds/ddsi/q_unused.h" + + +inline void ddsi_handshake_release(UNUSED_ARG(struct ddsi_handshake *handshake)) +{ +} + +inline void ddsi_handshake_crypto_tokens_received(UNUSED_ARG(struct ddsi_handshake *handshake)) +{ +} + +inline int64_t ddsi_handshake_get_shared_secret(UNUSED_ARG(const struct ddsi_handshake *handshake)) +{ + return 0; +} + +inline int64_t ddsi_handshake_get_handle(UNUSED_ARG(const struct ddsi_handshake *handshake)) +{ + return 0; +} + +inline void ddsi_handshake_register(UNUSED_ARG(const struct participant *pp), UNUSED_ARG(const struct proxy_participant *proxypp), UNUSED_ARG(ddsi_handshake_end_cb_t callback)) +{ +} + +inline void ddsi_handshake_remove(UNUSED_ARG(const struct participant *pp), UNUSED_ARG(const struct proxy_participant *proxypp), UNUSED_ARG(struct ddsi_handshake *handshake)) +{ +} + +inline struct ddsi_handshake * ddsi_handshake_find(UNUSED_ARG(const struct participant *pp), UNUSED_ARG(const struct proxy_participant *proxypp)) +{ + return NULL; +} + +#endif /* DDSI_INCLUDE_SECURITY */ + +#if defined (__cplusplus) +} +#endif + +#endif /* DDSI_HANDSHAKE_H */ diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_security_msg.h b/src/core/ddsi/include/dds/ddsi/ddsi_security_msg.h index 2db227c..e8ced85 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_security_msg.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_security_msg.h @@ -12,6 +12,8 @@ #ifndef DDSI_SECURITY_MSG_H #define DDSI_SECURITY_MSG_H +#ifdef DDSI_INCLUDE_SECURITY + #include "dds/ddsi/q_plist.h" #include "dds/ddsi/ddsi_guid.h" #include "dds/ddsrt/retcode.h" @@ -108,4 +110,6 @@ volatile_secure_data_filter( } #endif +#endif + #endif /* DDSI_SECURITY_MSG_H */ diff --git a/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h b/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h index 316954d..7ad3425 100644 --- a/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h +++ b/src/core/ddsi/include/dds/ddsi/ddsi_security_omg.h @@ -57,6 +57,17 @@ bool q_omg_security_enabled(void); */ bool q_omg_participant_is_secure(const struct participant *pp); +/** + * @brief Check if security is enabled for the proxy participant. + * + * @param[in] proxypp Proxy participant to check if it is secure. + * + * @returns bool + * @retval true Proxy participant is secure + * @retval false Proxy participant is not secure + */ +bool q_omg_proxy_participant_is_secure(const struct proxy_participant *proxypp); + /** * @brief Get the security handle of the given local participant. * @@ -490,6 +501,195 @@ secure_conn_write( nn_msg_sec_info_t *sec_info, ddsi_tran_write_fn_t conn_write_cb); +/** + * @brief Check if the participant and the proxy participant + * have compatible security info settings. + * + * Associated with a secure participant is the ParticipantSecurityInfo parameter. + * This parameter contains the setting of the security attributes and the associated + * plugin security attributes of the secure participant. + * This function will check if the received ParticipantSecurityInfo parameter is + * compatible with the local ParticipantSecurityInfo parameter. + * + * @param[in] pp The participant. + * @param[in] proxypp The proxy participant. + * + * @returns bool + * @retval true The participant and the proxy participant have compatible + * security info settings. + * @retval false Otherwise. + */ +bool q_omg_is_similar_participant_security_info(struct participant *pp, struct proxy_participant *proxypp); + +/** + * @brief Check if the participant allows communication with unauthenticated + * participants + * + * @param[in] pp The participant. + * + * @returns bool + * @retval true The participant allows unauthenticated communication + * @retval false Otherwise. + */ +bool q_omg_participant_allow_unauthenticated(struct participant *pp); + +/** + * @brief Register participant with security plugin and check if the + * participant is allowed by security. + * + * This function will register the participant with the authentication + * plugin which will check if the provided security QoS parameters are + * correct, e.g. is the provided certificate valid, etc. + * When that is successful it is checked with access control if the + * participant has the correct permissions and is allowed to be created. + * + * @param[in] pp The participant. + * @param[in] domain_id The domain id. + * + * @returns bool + * @retval true The security check on the participant succeeded. + * @retval false The security check on the participant failed. + */ +bool q_omg_security_check_create_participant(struct participant *pp, uint32_t domain_id); + +/** + * @brief Initialize the proxy participant security attributes + * + * @param[in] proxypp The proxy participant. + * + */ +void q_omg_security_init_remote_participant(struct proxy_participant *proxypp); + +/** + * @brief Check the if the proxy participant is allowed by checking the security permissions. + * + * The access control plugin is ask to verify if the proxy participant is allowed to + * communicate with the local participant. When the proxy participant is allowed the + * function will return a valid permission handle which is provided by the access control plugin. + * + * @param[in] domain_id The domain id + * @param[in] pp The participant + * @param[in] proxypp The proxy participant + * + * @returns permission handle + * @retval !0 The proxy participant is allowed + * @retval 0 The proxy participant is not allowed. + */ +int64_t q_omg_security_check_remote_participant_permissions(uint32_t domain_id, struct participant *pp, struct proxy_participant *proxypp); + +/** + * @brief Registers the matched proxy participant with the crypto plugin + * + * When the proxy participant is authenticated and allowed by access control then the match between the local and + * the remote participant must be registered with the cypto factory provided by the crypto plugin. The + * shared secret handle obtained from the authentication phase and the permission handle returned when validating + * the proxy participant with access control plugin have to be provided. + * + * + * @param[in] pp The participant. + * @param[in] proxypp The proxy participant. + * @param[in] shared_secret The shared_secret handle. + * @param[in] proxy_permissions The permission handle associated with the proxy participant. + */ +void q_omg_security_register_remote_participant(struct participant *pp, struct proxy_participant *proxypp, int64_t shared_secret, int64_t proxy_permissions); + +/** + * @brief Removes a registered proxy participant from administation of the authentication, + * access control and crypto plugins. + * + * @param[in] proxypp The proxy participant. + */ +void q_omg_security_deregister_remote_participant(struct proxy_participant *proxypp); + +/** + * @brief Generate and send the crypto tokens needed for encoding RTPS messages. + * + * When the security settings indicate that RTPS message encoding or signing is + * configured for the participant then this function will ask the cypto echange for + * the corresponding cypto tokens and send these to the proxy participant. + * + * @param[in] pp The participant. + * @param[in] proxypp The proxy participant. + */ +void q_omg_security_participant_send_tokens(struct participant *pp, struct proxy_participant *proxypp); + +/** + * @brief Check if the remote writer is allowed to communicate with endpoints of the + * local participant. + * + * This function will check with the access control plugin if the remote writer + * is allowed to communicate with this participant. + * + * @param[in] pwr The remote writer. + * @param[in] domain_id The domain id. + * @param[in] pp The local participant. + * + * @returns bool + * @retval true The remote writer is allowed to communicate. + * @retval false Otherwise. + */ +bool q_omg_security_check_remote_writer_permissions(const struct proxy_writer *pwr, uint32_t domain_id, struct participant *pp); + +/** + * @brief Check if the remote reader is allowed to communicate with endpoints of the + * local participant. + * + * This function will check with the access control plugin if the remote reader + * is allowed to communicate with this participant. + * + * @param[in] prd The remote reader. + * @param[in] domain_id The domain id. + * @param[in] pp The local participant. + * + * @returns bool + * @retval true The remote reader is allowed to communicate. + * @retval false Otherwise. + */ +bool q_omg_security_check_remote_reader_permissions(const struct proxy_reader *prd, uint32_t domain_id, struct participant *pp); + +/** + * @brief Check it the remote writer is allowed to communicate with the local reader. + * + * When a remote writer is allowed by access control it has to be checked if the remote + * writer is allowed to communicate with a particular local reader. This function will + * check if the provided security end-point attributes are compatible, When the security + * attributes are compatible then the function will register the reader and remote writer + * match with the crypto factory and will also ask the crypto exchange to generate the + * crypto tokens associate with the local reader which will be sent to the remote entity. + * Note that the reader crypto tokens are used to encrypt the reader specific submessages + * when submessage encoding or signing is configured. + * + * @param[in] rd The local reader. + * @param[in] pwr The remote writer. + * + * @returns bool + * @retval true The local reader and remote writer are allowed to communicate. + * @retval false Otherwise. + */ +bool q_omg_security_match_remote_writer_enabled(struct reader *rd, struct proxy_writer *pwr); + +/** + * @brief Check it the local writer is allowed to communicate with the remote reader. + * + * When a remote reader is allowed by access control it has to be checked if the local + * writer is allowed to communicate with a particular local writer. This function will + * check if the provided security end-point attributes are compatible, When the security + * attributes are compatible then the function will register the writer and remote reader + * match with the crypto factory and will also ask the crypto exchange to generate the + * crypto tokens associate with the local writer which will be sent to the remote entity. + * Note that the writer crypto tokens are used to encrypt the writer specific submessages + * when submessage encoding or signing is configured and also the crypto tokens used + * for encoding the payload of data or datafrag messages. + * + * @param[in] wr The local writer. + * @param[in] prd The remote reader. + * + * @returns bool + * @retval true The local writer and remote reader are allowed to communicate. + * @retval false Otherwise. + */ +bool q_omg_security_match_remote_reader_enabled(struct writer *wr, struct proxy_reader *prd); + #else /* DDSI_INCLUDE_SECURITY */ #include "dds/ddsi/q_unused.h" @@ -507,6 +707,13 @@ q_omg_participant_is_secure( return false; } +inline bool +q_omg_proxy_participant_is_secure( + UNUSED_ARG(const struct proxy_participant *proxypp)) +{ + return false; +} + inline unsigned determine_subscription_writer( UNUSED_ARG(const struct reader *rd)) @@ -530,6 +737,65 @@ is_proxy_participant_deletion_allowed( return true; } +inline bool q_omg_is_similar_participant_security_info(UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp)) +{ + return true; +} + +inline bool q_omg_participant_allow_unauthenticated(UNUSED_ARG(struct participant *pp)) +{ + return true; +} + +inline bool +q_omg_security_check_create_participant(UNUSED_ARG(struct participant *pp), UNUSED_ARG(uint32_t domain_id)) +{ + return true; +} + +inline void q_omg_security_init_remote_participant(UNUSED_ARG(struct proxy_participant *proxypp)) +{ +} + +inline int64_t q_omg_security_check_remote_participant_permissions(UNUSED_ARG(uint32_t domain_id), UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp)) +{ + return 0LL; +} + +inline void q_omg_security_register_remote_participant(UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp), UNUSED_ARG(int64_t shared_secret), UNUSED_ARG(int64_t proxy_permissions)) +{ +} + +inline void q_omg_security_deregister_remote_participant(UNUSED_ARG(struct proxy_participant *proxypp)) +{ +} + +inline void q_omg_security_participant_send_tokens(UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp)) +{ +} + +inline bool q_omg_security_match_remote_writer_enabled(UNUSED_ARG(struct reader *rd), UNUSED_ARG(struct proxy_writer *pwr)) +{ + return true; +} + +inline bool q_omg_security_match_remote_reader_enabled(UNUSED_ARG(struct writer *wr), UNUSED_ARG(struct proxy_reader *prd)) +{ + return true; +} + +inline bool +q_omg_security_check_remote_writer_permissions(UNUSED_ARG(const struct proxy_writer *pwr), UNUSED_ARG(uint32_t domain_id), UNUSED_ARG(struct participant *pp)) +{ + return true; +} + +inline bool +q_omg_security_check_remote_reader_permissions(UNUSED_ARG(const struct proxy_reader *prd), UNUSED_ARG(uint32_t domain_id), UNUSED_ARG(struct participant *pp)) +{ + return true; +} + inline void set_proxy_participant_security_info( UNUSED_ARG(struct proxy_participant *prd), @@ -551,7 +817,6 @@ set_proxy_writer_security_info( { } - inline bool decode_Data( UNUSED_ARG(const struct q_globals *gv), diff --git a/src/core/ddsi/include/dds/ddsi/q_entity.h b/src/core/ddsi/include/dds/ddsi/q_entity.h index 66c6ca1..2e3eba4 100644 --- a/src/core/ddsi/include/dds/ddsi/q_entity.h +++ b/src/core/ddsi/include/dds/ddsi/q_entity.h @@ -20,11 +20,13 @@ #include "dds/ddsi/q_rtps.h" #include "dds/ddsi/q_plist.h" #include "dds/ddsi/q_protocol.h" +#include "dds/ddsi/q_plist.h" #include "dds/ddsi/q_lat_estim.h" #include "dds/ddsi/q_hbcontrol.h" #include "dds/ddsi/q_feature_check.h" #include "dds/ddsi/q_inverse_uint32_set.h" #include "dds/ddsi/ddsi_serdata_default.h" +#include "dds/ddsi/ddsi_handshake.h" #include "dds/ddsi/ddsi_tran.h" @@ -214,6 +216,11 @@ struct participant int32_t builtin_refc; /* number of built-in endpoints in this participant [refc_lock] */ int builtins_deleted; /* whether deletion of built-in endpoints has been initiated [refc_lock] */ ddsrt_fibheap_t ldur_auto_wr; /* Heap that contains lease duration for writers with automatic liveliness in this participant */ +#ifdef DDSI_INCLUDE_SECURITY + int64_t local_identity_handle; /* OMG DDS Security related member */ + int64_t permissions_handle; /* OMG DDS Security related member */ + struct participant_sec_attributes *sec_attr; +#endif }; struct endpoint_common { @@ -356,7 +363,9 @@ struct proxy_participant unsigned proxypp_have_cm: 1; unsigned owns_lease: 1; #ifdef DDSI_INCLUDE_SECURITY + int64_t remote_identity_handle; /* OMG DDS Security related member */ nn_security_info_t security_info; + struct proxy_participant_sec_attributes *sec_attr; #endif }; @@ -712,6 +721,10 @@ void rebuild_or_clear_writer_addrsets(struct q_globals *gv, int rebuild); void local_reader_ary_setfastpath_ok (struct local_reader_ary *x, bool fastpath_ok); +void connect_writer_with_proxy_reader_secure(struct writer *wr, struct proxy_reader *prd, nn_mtime_t tnow); +void connect_reader_with_proxy_writer_secure(struct reader *rd, struct proxy_writer *pwr, nn_mtime_t tnow); + + struct ddsi_writer_info; DDS_EXPORT void ddsi_make_writer_info(struct ddsi_writer_info *wrinfo, const struct entity_common *e, const struct dds_qos *xqos, uint32_t statusinfo); diff --git a/src/core/ddsi/src/ddsi_entity_index.c b/src/core/ddsi/src/ddsi_entity_index.c index c7b09b9..b22488b 100644 --- a/src/core/ddsi/src/ddsi_entity_index.c +++ b/src/core/ddsi/src/ddsi_entity_index.c @@ -439,6 +439,18 @@ void entidx_enum_init_topic (struct entidx_enum *st, const struct entity_index * st->cur = NULL; } +void entidx_enum_init_topic_w_prefix (struct entidx_enum *st, const struct entity_index *ei, enum entity_kind kind, const char *topic, const ddsi_guid_prefix_t *prefix, struct match_entities_range_key *max) +{ + assert (kind == EK_READER || kind == EK_WRITER || kind == EK_PROXY_READER || kind == EK_PROXY_WRITER); + struct match_entities_range_key min; + match_endpoint_range (kind, topic, &min, max); + min.entity.e.guid.prefix = *prefix; + max->entity.e.guid.prefix = *prefix; + entidx_enum_init_minmax_int (st, ei, &min); + if (st->cur && all_entities_compare (st->cur, &max->entity) > 0) + st->cur = NULL; +} + void entidx_enum_init (struct entidx_enum *st, const struct entity_index *ei, enum entity_kind kind) { struct match_entities_range_key min; diff --git a/src/core/ddsi/src/ddsi_handshake.c b/src/core/ddsi/src/ddsi_handshake.c new file mode 100644 index 0000000..13d8390 --- /dev/null +++ b/src/core/ddsi/src/ddsi_handshake.c @@ -0,0 +1,109 @@ +/* + * Copyright(c) 2006 to 2018 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 "dds/ddsi/ddsi_handshake.h" + +#ifdef DDSI_INCLUDE_SECURITY + +#include "dds/security/dds_security_api.h" +#include "dds/ddsrt/hopscotch.h" + +struct ddsi_handshake +{ + enum ddsi_handshake_state state; + + ddsi_guid_t local_pguid; /* the guid of the local participant */ + ddsi_guid_t remote_pguid; /* the guid of the remote participant */ + ddsi_handshake_end_cb_t end_cb; + struct q_globals *gv; + ddsrt_mutex_t lock; + ddsrt_cond_t cv; + + ddsrt_atomic_uint32_t refc; + + DDS_Security_IdentityHandle local_identity_handle; + DDS_Security_IdentityHandle remote_identity_handle; + DDS_Security_HandshakeHandle handshake_handle; + + DDS_Security_HandshakeMessageToken handshake_message_in_token; + nn_message_identity_t handshake_message_in_id; + DDS_Security_HandshakeMessageToken *handshake_message_out; + DDS_Security_AuthRequestMessageToken local_auth_request_token; + DDS_Security_AuthRequestMessageToken *remote_auth_request_token; + DDS_Security_OctetSeq pdata; + DDS_Security_SharedSecretHandle shared_secret; + int handled_handshake_message; +}; + + +void ddsi_handshake_handle_message(struct ddsi_handshake *handshake, const struct participant *pp, const struct proxy_participant *proxypp, const struct nn_participant_generic_message *msg) +{ + DDSRT_UNUSED_ARG(handshake); + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); + DDSRT_UNUSED_ARG(msg); +} + +void ddsi_handshake_crypto_tokens_received(struct ddsi_handshake *handshake) +{ + DDSRT_UNUSED_ARG(handshake); +} + +int64_t ddsi_handshake_get_shared_secret(const struct ddsi_handshake *handshake) +{ + DDSRT_UNUSED_ARG(handshake); + + return 0; +} + +int64_t ddsi_handshake_get_handle(const struct ddsi_handshake *handshake) +{ + DDSRT_UNUSED_ARG(handshake); + + return 0; +} + +void ddsi_handshake_register(const struct participant *pp, const struct proxy_participant *proxypp, ddsi_handshake_end_cb_t callback) +{ + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); + DDSRT_UNUSED_ARG(callback); +} + +void ddsi_handshake_remove(const struct participant *pp, const struct proxy_participant *proxypp, struct ddsi_handshake *handshake) +{ + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); + DDSRT_UNUSED_ARG(handshake); +} + +struct ddsi_handshake * ddsi_handshake_find(const struct participant *pp, const struct proxy_participant *proxypp) +{ + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); + + return NULL; +} + + +#else + +extern inline void ddsi_handshake_release(UNUSED_ARG(struct ddsi_handshake *handshake)); +extern inline void ddsi_handshake_crypto_tokens_received(UNUSED_ARG(struct ddsi_handshake *handshake)); +extern inline int64_t ddsi_handshake_get_shared_secret(UNUSED_ARG(const struct ddsi_handshake *handshake)); +extern inline int64_t ddsi_handshake_get_handle(UNUSED_ARG(const struct ddsi_handshake *handshake)); +extern inline void ddsi_handshake_register(UNUSED_ARG(const struct participant *pp), UNUSED_ARG(const struct proxy_participant *proxypp), UNUSED_ARG(ddsi_handshake_end_cb_t callback)); +extern inline void ddsi_handshake_remove(UNUSED_ARG(const struct participant *pp), UNUSED_ARG(const struct proxy_participant *proxypp), UNUSED_ARG(struct ddsi_handshake *handshake)); +extern inline struct ddsi_handshake * ddsi_handshake_find(UNUSED_ARG(const struct participant *pp), UNUSED_ARG(const struct proxy_participant *proxypp)); + + +#endif /* DDSI_INCLUDE_DDS_SECURITY */ diff --git a/src/core/ddsi/src/ddsi_security_omg.c b/src/core/ddsi/src/ddsi_security_omg.c index 32f3b11..10fca50 100644 --- a/src/core/ddsi/src/ddsi_security_omg.c +++ b/src/core/ddsi/src/ddsi_security_omg.c @@ -92,6 +92,15 @@ q_omg_participant_is_secure( return false; } +bool +q_omg_proxy_participant_is_secure( + const struct proxy_participant *proxypp) +{ + /* TODO: Register remote participant */ + DDSRT_UNUSED_ARG(proxypp); + return false; +} + static bool q_omg_writer_is_discovery_protected( const struct writer *wr) @@ -147,6 +156,12 @@ q_omg_get_reader_security_info( return false; } +void +q_omg_security_init_remote_participant(struct proxy_participant *proxypp) +{ + DDSRT_UNUSED_ARG(proxypp); +} + static bool q_omg_proxyparticipant_is_authenticated( const struct proxy_participant *proxy_pp) @@ -174,6 +189,14 @@ q_omg_security_get_remote_participant_handle( return 0; } +bool +q_omg_participant_allow_unauthenticated(struct participant *pp) +{ + DDSRT_UNUSED_ARG(pp); + + return true; +} + unsigned determine_subscription_writer( const struct reader *rd) @@ -196,6 +219,21 @@ determine_publication_writer( return NN_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_WRITER; } +void +q_omg_security_register_remote_participant(struct participant *pp, struct proxy_participant *proxypp, int64_t shared_secret, int64_t proxy_permissions) +{ + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); + DDSRT_UNUSED_ARG(shared_secret); + DDSRT_UNUSED_ARG(proxy_permissions); +} + +void +q_omg_security_deregister_remote_participant(struct proxy_participant *proxypp) +{ + DDSRT_UNUSED_ARG(proxypp); +} + bool is_proxy_participant_deletion_allowed( struct q_globals * const gv, @@ -225,6 +263,93 @@ is_proxy_participant_deletion_allowed( return (!q_omg_proxyparticipant_is_authenticated(proxypp)); } +/* ask to access control security plugin for the remote participant permissions */ +int64_t +q_omg_security_check_remote_participant_permissions(uint32_t domain_id, struct participant *pp, struct proxy_participant *proxypp) +{ + DDSRT_UNUSED_ARG(domain_id); + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); + + return 0; +} + +bool +q_omg_is_similar_participant_security_info(struct participant *pp, struct proxy_participant *proxypp) +{ + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); + + return true; +} + +bool +q_omg_security_check_create_participant(struct participant *pp, uint32_t domain_id) +{ + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(domain_id); + return true; +} + +void +q_omg_security_participant_send_tokens(struct participant *pp, struct proxy_participant *proxypp) +{ + DDSRT_UNUSED_ARG(pp); + DDSRT_UNUSED_ARG(proxypp); +} + +bool +q_omg_security_match_remote_writer_enabled(struct reader *rd, struct proxy_writer *pwr) +{ + DDSRT_UNUSED_ARG(rd); + DDSRT_UNUSED_ARG(pwr); + + assert(rd); + assert(pwr); + + return true; +} + +bool +q_omg_security_match_remote_reader_enabled(struct writer *wr, struct proxy_reader *prd) +{ + DDSRT_UNUSED_ARG(wr); + DDSRT_UNUSED_ARG(prd); + + assert(wr); + assert(prd); + + return true; +} + +bool +q_omg_security_check_remote_writer_permissions(const struct proxy_writer *pwr, uint32_t domain_id, struct participant *pp) +{ + DDSRT_UNUSED_ARG(pwr); + DDSRT_UNUSED_ARG(domain_id); + DDSRT_UNUSED_ARG(pp); + + assert(pwr); + assert(pp); + assert(pwr->c.proxypp); + + return true; +} + +bool +q_omg_security_check_remote_reader_permissions(const struct proxy_reader *prd, uint32_t domain_id, struct participant *pp) +{ + DDSRT_UNUSED_ARG(prd); + DDSRT_UNUSED_ARG(domain_id); + DDSRT_UNUSED_ARG(pp); + + assert(prd); + assert(pp); + assert(prd->c.proxypp); + + return true; +} + bool q_omg_security_is_remote_rtps_protected( struct proxy_participant *proxy_pp, @@ -792,7 +917,7 @@ validate_msg_decoding( * that is expected. */ if (q_omg_security_is_remote_rtps_protected(proxypp, e->guid.entityid) && !rst->rtps_encoded) { - return 0; + return false; } return true; @@ -1182,6 +1307,9 @@ secure_conn_write( #include "dds/ddsi/ddsi_security_omg.h" + +extern inline bool q_omg_participant_is_secure(UNUSED_ARG(const struct participant *pp)); +extern inline bool q_omg_proxy_participant_is_secure(const struct proxy_participant *proxypp); extern inline bool q_omg_security_enabled(void); extern inline bool q_omg_participant_is_secure( @@ -1190,6 +1318,12 @@ extern inline bool q_omg_participant_is_secure( extern inline unsigned determine_subscription_writer( UNUSED_ARG(const struct reader *rd)); +extern inline bool q_omg_security_match_remote_writer_enabled(UNUSED_ARG(struct reader *rd), UNUSED_ARG(struct proxy_writer *pwr)); +extern inline bool q_omg_security_match_remote_reader_enabled(UNUSED_ARG(struct writer *wr), UNUSED_ARG(struct proxy_reader *prd)); + +extern inline bool q_omg_security_check_remote_writer_permissions(UNUSED_ARG(const struct proxy_writer *pwr), UNUSED_ARG(uint32_t domain_id), UNUSED_ARG(struct participant *pp)); +extern inline bool q_omg_security_check_remote_reader_permissions(UNUSED_ARG(const struct proxy_reader *prd), UNUSED_ARG(uint32_t domain_id), UNUSED_ARG(struct participant *par)); + extern inline unsigned determine_publication_writer( UNUSED_ARG(const struct writer *wr)); @@ -1198,6 +1332,24 @@ extern inline bool is_proxy_participant_deletion_allowed( UNUSED_ARG(const struct ddsi_guid *guid), UNUSED_ARG(const ddsi_entityid_t pwr_entityid)); +extern inline bool q_omg_is_similar_participant_security_info(UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp)); + +extern inline bool q_omg_participant_allow_unauthenticated(UNUSED_ARG(struct participant *pp)); + +extern inline bool q_omg_security_check_create_participant(UNUSED_ARG(struct participant *pp), UNUSED_ARG(uint32_t domain_id)); + +/* initialize the proxy participant security attributes */ +extern inline void q_omg_security_init_remote_participant(UNUSED_ARG(struct proxy_participant *proxypp)); + +/* ask to access control security plugin for the remote participant permissions */ +extern inline int64_t q_omg_security_check_remote_participant_permissions(UNUSED_ARG(uint32_t domain_id), UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp)); + +extern inline void q_omg_security_register_remote_participant(UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp), UNUSED_ARG(int64_t shared_secret), UNUSED_ARG(int64_t proxy_permissions)); + +extern inline void q_omg_security_deregister_remote_participant(UNUSED_ARG(struct proxy_participant *proxypp)); + +extern inline void q_omg_security_participant_send_tokens(UNUSED_ARG(struct participant *pp), UNUSED_ARG(struct proxy_participant *proxypp)); + extern inline void set_proxy_participant_security_info( UNUSED_ARG(struct proxy_participant *prd), UNUSED_ARG(const nn_plist_t *plist)); diff --git a/src/core/ddsi/src/q_entity.c b/src/core/ddsi/src/q_entity.c index d0b246f..6a126c7 100644 --- a/src/core/ddsi/src/q_entity.c +++ b/src/core/ddsi/src/q_entity.c @@ -105,10 +105,19 @@ static const unsigned prismtech_builtin_writers_besmask = NN_DISC_BUILTIN_ENDPOINT_CM_PUBLISHER_WRITER | NN_DISC_BUILTIN_ENDPOINT_CM_SUBSCRIBER_WRITER; + static dds_return_t new_writer_guid (struct writer **wr_out, const struct ddsi_guid *guid, const struct ddsi_guid *group_guid, struct participant *pp, const struct ddsi_sertopic *topic, const struct dds_qos *xqos, struct whc *whc, status_cb_t status_cb, void *status_cbarg); static dds_return_t new_reader_guid (struct reader **rd_out, const struct ddsi_guid *guid, const struct ddsi_guid *group_guid, struct participant *pp, const struct ddsi_sertopic *topic, const struct dds_qos *xqos, struct ddsi_rhc *rhc, status_cb_t status_cb, void *status_cbarg); static struct participant *ref_participant (struct participant *pp, const struct ddsi_guid *guid_of_refing_entity); static void unref_participant (struct participant *pp, const struct ddsi_guid *guid_of_refing_entity); +static struct entity_common *entity_common_from_proxy_endpoint_common (const struct proxy_endpoint_common *c); + +#ifdef DDSI_INCLUDE_SECURITY +static const unsigned BES_MASK_NON_SECURITY = 0xf000ffff; + +static void handshake_end_cb(struct q_globals const * const gv, struct ddsi_handshake *handshake, const struct ddsi_guid *lpguid, const struct ddsi_guid *ppguid, enum ddsi_handshake_state result); +static void downgrade_to_nonsecure(struct proxy_participant *proxypp); +#endif static int gcreq_participant (struct participant *pp); static int gcreq_writer (struct writer *wr); @@ -201,6 +210,22 @@ int is_builtin_endpoint (ddsi_entityid_t id, nn_vendorid_t vendorid) return is_builtin_entityid (id, vendorid) && id.u != NN_ENTITYID_PARTICIPANT; } +#ifdef DDSI_INCLUDE_SECURITY + +static int is_builtin_volatile_endpoint (ddsi_entityid_t id) +{ + switch (id.u) { + case NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER: + case NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_READER: + return 1; + default: + break; + } + return 0; +} + +#endif + bool is_local_orphan_endpoint (const struct entity_common *e) { return (e->guid.prefix.u[0] == 0 && e->guid.prefix.u[1] == 0 && e->guid.prefix.u[2] == 0 && @@ -570,6 +595,47 @@ static void add_security_builtin_endpoints(struct participant *pp, ddsi_guid_t * } #endif + +#ifdef DDSI_INCLUDE_SECURITY + +static void connect_participant_secure(struct q_globals *gv, struct participant *pp) +{ + struct proxy_participant *proxypp; + struct entidx_enum_proxy_participant it; + + if (q_omg_participant_is_secure(pp)) + { + entidx_enum_proxy_participant_init (&it, gv->entity_index); + while ((proxypp = entidx_enum_proxy_participant_next (&it)) != NULL) + { + /* Do not start handshaking when security info doesn't match. */ + if (q_omg_is_similar_participant_security_info(pp, proxypp)) + { + ddsi_handshake_register(pp, proxypp, handshake_end_cb); + } + } + entidx_enum_proxy_participant_fini (&it); + } +} + +static void disconnect_participant_secure(struct participant *pp) +{ + struct proxy_participant *proxypp; + struct entidx_enum_proxy_participant it; + struct q_globals * const gv = pp->e.gv; + + if (q_omg_participant_is_secure(pp)) + { + entidx_enum_proxy_participant_init (&it, gv->entity_index); + while ((proxypp = entidx_enum_proxy_participant_next (&it)) != NULL) + { + ddsi_handshake_remove(pp, proxypp, NULL); + } + entidx_enum_proxy_participant_fini (&it); + } +} +#endif + dds_return_t new_participant_guid (const ddsi_guid_t *ppguid, struct q_globals *gv, unsigned flags, const nn_plist_t *plist) { struct participant *pp; @@ -660,6 +726,16 @@ dds_return_t new_participant_guid (const ddsi_guid_t *ppguid, struct q_globals * goto new_pp_err_secprop; } } + + if (nn_xqos_has_prop (&pp->plist->qos, "dds.sec.", true)) + { + if (!q_omg_security_check_create_participant (pp, gv->config.domainId)) + { + ret = DDS_RETCODE_NOT_ALLOWED_BY_SECURITY; + goto not_allowed; + } + } + #endif if (gv->logconfig.c.mask & DDS_LC_DISCOVERY) @@ -856,9 +932,18 @@ dds_return_t new_participant_guid (const ddsi_guid_t *ppguid, struct q_globals * tsched.v = (pp->lease_duration == T_NEVER) ? T_NEVER : 0; pp->pmd_update_xevent = qxev_pmd_update (gv->xevents, tsched, &pp->e.guid); } + +#ifdef DDSI_INCLUDE_SECURITY + if (q_omg_participant_is_secure(pp)) + { + connect_participant_secure (gv, pp); + } +#endif + return ret; #ifdef DDSI_INCLUDE_SECURITY +not_allowed: new_pp_err_secprop: nn_plist_fini (pp->plist); ddsrt_free (pp->plist); @@ -1099,6 +1184,9 @@ dds_return_t delete_participant (struct q_globals *gv, const struct ddsi_guid *p return DDS_RETCODE_BAD_PARAMETER; builtintopic_write (gv->builtin_topic_interface, &pp->e, now(), false); remember_deleted_participant_guid (gv->deleted_participants, &pp->e.guid); +#ifdef DDSI_INCLUDE_SECURITY + disconnect_participant_secure (pp); +#endif entidx_remove_participant_guid (gv->entity_index, pp); gcreq_participant (pp); return 0; @@ -2449,6 +2537,31 @@ static bool topickind_qos_match_p_lock (struct entity_common *rd, const dds_qos_ return ret; } +void connect_writer_with_proxy_reader_secure(struct writer *wr, struct proxy_reader *prd, nn_mtime_t tnow) +{ + DDSRT_UNUSED_ARG(tnow); + proxy_reader_add_connection (prd, wr); + writer_add_connection (wr, prd); +} + +void connect_reader_with_proxy_writer_secure(struct reader *rd, struct proxy_writer *pwr, nn_mtime_t tnow) +{ + nn_count_t init_count; + struct proxy_writer_alive_state alive_state; + + /* Initialize the reader's tracking information for the writer liveliness state to something + sensible, but that may be outdated by the time the reader gets added to the writer's list + of matching readers. */ + proxy_writer_get_alive_state (pwr, &alive_state); + reader_add_connection (rd, pwr, &init_count, &alive_state); + proxy_writer_add_connection (pwr, rd, tnow, init_count); + + /* Once everything is set up: update with the latest state, any updates to the alive state + happening in parallel will cause this to become a no-op. */ + proxy_writer_get_alive_state (pwr, &alive_state); + reader_update_notify_pwr_alive_state (rd, pwr, &alive_state); +} + static void connect_writer_with_proxy_reader (struct writer *wr, struct proxy_reader *prd, nn_mtime_t tnow) { const int isb0 = (is_builtin_entityid (wr->e.guid.entityid, NN_VENDORID_ECLIPSE) != 0); @@ -2464,8 +2577,22 @@ static void connect_writer_with_proxy_reader (struct writer *wr, struct proxy_re writer_qos_mismatch (wr, reason); return; } - proxy_reader_add_connection (prd, wr); - writer_add_connection (wr, prd); + + if (!q_omg_security_check_remote_reader_permissions (prd, wr->e.gv->config.domainId, wr->c.pp)) + { + EELOGDISC (&wr->e, "connect_writer_with_proxy_reader (wr "PGUIDFMT") with (prd "PGUIDFMT") not allowed by security\n", + PGUID (wr->e.guid), PGUID (prd->e.guid)); + } + else if (!q_omg_security_match_remote_reader_enabled (wr, prd)) + { + EELOGDISC (&wr->e, "connect_writer_with_proxy_reader (wr "PGUIDFMT") with (prd "PGUIDFMT") waiting for approval by security\n", + PGUID (wr->e.guid), PGUID (prd->e.guid)); + } + else + { + proxy_reader_add_connection (prd, wr); + writer_add_connection (wr, prd); + } } static void connect_proxy_writer_with_reader (struct proxy_writer *pwr, struct reader *rd, nn_mtime_t tnow) @@ -2485,17 +2612,30 @@ static void connect_proxy_writer_with_reader (struct proxy_writer *pwr, struct r return; } - /* Initialze the reader's tracking information for the writer liveliness state to something - sensible, but that may be outdated by the time the reader gets added to the writer's list - of matching readers. */ - proxy_writer_get_alive_state (pwr, &alive_state); - reader_add_connection (rd, pwr, &init_count, &alive_state); - proxy_writer_add_connection (pwr, rd, tnow, init_count); + if (!q_omg_security_check_remote_writer_permissions(pwr, rd->e.gv->config.domainId, rd->c.pp)) + { + EELOGDISC (&rd->e, "connect_proxy_writer_with_reader (pwr "PGUIDFMT") with (rd "PGUIDFMT") not allowed by security\n", + PGUID (pwr->e.guid), PGUID (rd->e.guid)); + } + else if (!q_omg_security_match_remote_writer_enabled(rd, pwr)) + { + EELOGDISC (&rd->e, "connect_proxy_writer_with_reader (pwr "PGUIDFMT") with (rd "PGUIDFMT") waiting for approval by security\n", + PGUID (pwr->e.guid), PGUID (rd->e.guid)); + } + else + { + /* Initialize the reader's tracking information for the writer liveliness state to something + sensible, but that may be outdated by the time the reader gets added to the writer's list + of matching readers. */ + proxy_writer_get_alive_state (pwr, &alive_state); + reader_add_connection (rd, pwr, &init_count, &alive_state); + proxy_writer_add_connection (pwr, rd, tnow, init_count); - /* Once everything is set up: update with the latest state, any updates to the alive state - happening in parallel will cause this to become a no-op. */ - proxy_writer_get_alive_state (pwr, &alive_state); - reader_update_notify_pwr_alive_state (rd, pwr, &alive_state); + /* Once everything is set up: update with the latest state, any updates to the alive state + happening in parallel will cause this to become a no-op. */ + proxy_writer_get_alive_state (pwr, &alive_state); + reader_update_notify_pwr_alive_state (rd, pwr, &alive_state); + } } static bool ignore_local_p (const ddsi_guid_t *guid1, const ddsi_guid_t *guid2, const struct dds_qos *xqos1, const struct dds_qos *xqos2) @@ -2733,6 +2873,126 @@ static void match_proxy_reader_with_writers (struct proxy_reader *prd, nn_mtime_ generic_do_match(&prd->e, tnow, false); } +#ifdef DDSI_INCLUDE_SECURITY + +static void match_volatile_secure_endpoints (struct participant *pp, struct proxy_participant *proxypp) +{ + struct reader *rd; + struct writer *wr; + struct proxy_reader *prd; + struct proxy_writer *pwr; + ddsi_guid_t guid; + nn_mtime_t tnow = now_mt (); + + EELOGDISC (&pp->e, "match volatile endpoints (pp "PGUIDFMT") with (proxypp "PGUIDFMT")\n", + PGUID(pp->e.guid), PGUID(proxypp->e.guid)); + + guid = pp->e.guid; + guid.entityid.u = NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_READER; + rd = entidx_lookup_reader_guid (pp->e.gv->entity_index, &guid); + assert(rd); + guid.entityid.u = NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER; + wr = entidx_lookup_writer_guid (pp->e.gv->entity_index, &guid); + assert(wr); + + guid = proxypp->e.guid; + guid.entityid.u = NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_READER; + prd = entidx_lookup_proxy_reader_guid (pp->e.gv->entity_index, &guid); + assert(rd); + guid.entityid.u = NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER; + pwr = entidx_lookup_proxy_writer_guid (pp->e.gv->entity_index, &guid); + assert(wr); + + connect_proxy_writer_with_reader_wrapper(&pwr->e, &rd->e, tnow); + connect_writer_with_proxy_reader_wrapper(&wr->e, &prd->e, tnow); +} + +static struct entity_common * get_entity_parent(struct entity_common *e) +{ + switch (e->kind) + { + case EK_WRITER: + return &((struct writer *)e)->c.pp->e; + case EK_READER: + return &((struct reader *)e)->c.pp->e; + case EK_PROXY_WRITER: + return &((struct proxy_writer *)e)->c.proxypp->e; + case EK_PROXY_READER: + return &((struct proxy_reader *)e)->c.proxypp->e; + case EK_PARTICIPANT: + case EK_PROXY_PARTICIPANT: + return NULL; + } + return NULL; +} + +static void update_proxy_participant_endpoint_matching (struct proxy_participant *proxypp, struct participant *pp) +{ + struct entity_index * const entidx = pp->e.gv->entity_index; + struct proxy_endpoint_common *cep; + ddsi_guid_t guid; + ddsi_entityid_t *endpoint_ids; + uint32_t num = 0, i; + nn_mtime_t tnow = now_mt (); + + EELOGDISC (&proxypp->e, "update_proxy_participant_endpoint_matching (proxypp "PGUIDFMT" pp "PGUIDFMT")\n", + PGUID (proxypp->e.guid), PGUID (pp->e.guid)); + + ddsrt_mutex_lock(&proxypp->e.lock); + endpoint_ids = ddsrt_malloc(proxypp->refc * sizeof(ddsi_entityid_t)); + for (cep = proxypp->endpoints; cep != NULL; cep = cep->next_ep) + { + struct entity_common *e = entity_common_from_proxy_endpoint_common (cep); + endpoint_ids[num++] = e->guid.entityid; + } + ddsrt_mutex_unlock(&proxypp->e.lock); + + guid.prefix = proxypp->e.guid.prefix; + + for (i = 0; i < num; i++) + { + struct entity_common *e; + enum entity_kind mkind; + + guid.entityid = endpoint_ids[i]; + if ((e = entidx_lookup_guid_untyped(proxypp->e.gv->entity_index, &guid)) == NULL) + continue; + + mkind = generic_do_match_mkind (e->kind, false); + if (!is_builtin_entityid (e->guid.entityid, NN_VENDORID_ECLIPSE)) + { + struct entidx_enum it; + struct entity_common *em; + struct match_entities_range_key max; + const char *tp = entity_topic_name (e); + + entidx_enum_init_topic_w_prefix (&it, entidx, mkind, tp, &pp->e.guid.prefix, &max); + while ((em = entidx_enum_next_max (&it, &max)) != NULL) + { + if (&pp->e == get_entity_parent(e)) + generic_do_match_connect (e, em, tnow, false); + } + entidx_enum_fini (&it); + } + else + { + const ddsi_entityid_t tgt_ent = builtin_entityid_match (e->guid.entityid); + const ddsi_guid_t tgt_guid = { pp->e.guid.prefix, tgt_ent }; + + if (!is_builtin_volatile_endpoint (tgt_ent)) + { + struct entity_common *ep; + if ((ep = entidx_lookup_guid (entidx, &tgt_guid, mkind)) != NULL) + generic_do_match_connect (e, ep, tnow, false); + } + } + } + + ddsrt_free(endpoint_ids); +} + +#endif + /* ENDPOINT --------------------------------------------------------- */ static void new_reader_writer_common (const struct ddsrt_log_cfg *logcfg, const struct ddsi_guid *guid, const struct ddsi_sertopic *topic, const struct dds_qos *xqos) @@ -4048,7 +4308,7 @@ static void add_proxy_builtin_endpoints( /* Register lease for auto liveliness, but be careful not to accidentally re-register DDSI2's lease, as we may have become dependent on DDSI2 any time after - ephash_insert_proxy_participant_guid even if privileged_pp_guid was NULL originally */ + entidx_insert_proxy_participant_guid even if privileged_pp_guid was NULL originally */ ddsrt_mutex_lock (&proxypp->e.lock); if (proxypp->owns_lease) @@ -4124,6 +4384,142 @@ static void proxy_participant_remove_pwr_lease_locked (struct proxy_participant } } +#ifdef DDSI_INCLUDE_SECURITY + +void handshake_end_cb +( + struct q_globals const * const gv, + struct ddsi_handshake *handshake, + const struct ddsi_guid *lpguid, + const struct ddsi_guid *ppguid, + enum ddsi_handshake_state result) +{ + struct proxy_participant *proxypp; + struct participant *pp; + int64_t shared_secret; + int64_t permissions_hdl; + + assert(handshake); + assert(lpguid); + assert(ppguid); + + assert(gv); + + proxypp = entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid); + if (!proxypp) + return; + + pp = entidx_lookup_participant_guid (gv->entity_index, lpguid); + if (!pp) + return; + + switch(result) + { + case STATE_HANDSHAKE_PROCESSED: + shared_secret = ddsi_handshake_get_shared_secret(handshake); + DDS_CLOG (DDS_LC_DISCOVERY, &gv->logconfig, "handshake (lguid="PGUIDFMT" rguid="PGUIDFMT") processed\n", PGUID (*lpguid), PGUID (*ppguid)); + permissions_hdl = q_omg_security_check_remote_participant_permissions(gv->config.domainId, pp, proxypp); + if (permissions_hdl != 0) { + q_omg_security_register_remote_participant(pp, proxypp, shared_secret, permissions_hdl); + match_volatile_secure_endpoints(pp, proxypp); + } + break; + + case STATE_HANDSHAKE_SEND_TOKENS: + DDS_CLOG (DDS_LC_DISCOVERY, &gv->logconfig, "handshake (lguid="PGUIDFMT" rguid="PGUIDFMT") send tokens\n", PGUID (*lpguid), PGUID (*ppguid)); + q_omg_security_participant_send_tokens(pp, proxypp); + break; + + case STATE_HANDSHAKE_OK: + DDS_CLOG (DDS_LC_DISCOVERY, &gv->logconfig, "handshake (lguid="PGUIDFMT" rguid="PGUIDFMT") succeeded\n", PGUID (*lpguid), PGUID (*ppguid)); + update_proxy_participant_endpoint_matching(proxypp, pp); + ddsi_handshake_remove(pp, proxypp, handshake); + break; + + case STATE_HANDSHAKE_TIMED_OUT: + DDS_CERROR (&gv->logconfig, "handshake (lguid="PGUIDFMT" rguid="PGUIDFMT") failed: (%d) Timed out\n", PGUID (*lpguid), PGUID (*ppguid), (int)result); + if (q_omg_participant_allow_unauthenticated(pp)) { + downgrade_to_nonsecure(proxypp); + update_proxy_participant_endpoint_matching(proxypp, pp); + } + ddsi_handshake_remove(pp, proxypp, handshake); + break; + case STATE_HANDSHAKE_FAILED: + DDS_CERROR (&gv->logconfig, "handshake (lguid="PGUIDFMT" rguid="PGUIDFMT") failed: (%d) Failed\n", PGUID (*lpguid), PGUID (*ppguid), (int)result); + if (q_omg_participant_allow_unauthenticated(pp)) { + downgrade_to_nonsecure(proxypp); + update_proxy_participant_endpoint_matching(proxypp, pp); + } + ddsi_handshake_remove(pp, proxypp, handshake); + break; + default: + DDS_CERROR (&gv->logconfig, "handshake (lguid="PGUIDFMT" rguid="PGUIDFMT") failed: (%d) Unknown failure\n", PGUID (*lpguid), PGUID (*ppguid), (int)result); + ddsi_handshake_remove(pp, proxypp, handshake); + break; + } +} + +static int proxy_participant_check_security_info(struct q_globals *gv, struct proxy_participant *proxypp) +{ + int r = 0; + struct participant *pp; + struct entidx_enum_participant est; + + entidx_enum_participant_init (&est, gv->entity_index); + while (((pp = entidx_enum_participant_next (&est)) != NULL) && (r == 0)) { + if (q_omg_is_similar_participant_security_info(pp, proxypp)) { + r = 1; + break; + } + } + entidx_enum_participant_fini(&est); + return r; +} + + +static void proxy_participant_create_handshakes(struct q_globals *gv, struct proxy_participant *proxypp) +{ + struct participant *pp; + struct entidx_enum_participant est; + + entidx_enum_participant_init (&est, gv->entity_index); + while (((pp = entidx_enum_participant_next (&est)) != NULL)) { + if (q_omg_participant_is_secure(pp)) + { + ddsi_handshake_register(pp, proxypp, handshake_end_cb); + } + } + entidx_enum_participant_fini(&est); +} + +#endif + +#ifdef DDSI_INCLUDE_SECURITY + +static void free_proxy_participant(struct proxy_participant *proxypp) +{ + q_omg_security_deregister_remote_participant(proxypp); + unref_addrset (proxypp->as_default); + unref_addrset (proxypp->as_meta); + nn_plist_fini (proxypp->plist); + ddsrt_free (proxypp->plist); + if (proxypp->owns_lease) + { + struct lease * minl_auto = ddsrt_atomic_ldvoidp (&proxypp->minl_auto); + ddsrt_fibheap_delete (&lease_fhdef_proxypp, &proxypp->leaseheap_auto, proxypp->lease); + assert (ddsrt_fibheap_min (&lease_fhdef_proxypp, &proxypp->leaseheap_auto) == NULL); + assert (ddsrt_fibheap_min (&lease_fhdef_proxypp, &proxypp->leaseheap_man) == NULL); + assert (ddsrt_atomic_ldvoidp (&proxypp->minl_man) == NULL); + assert (!compare_guid (&minl_auto->entity->guid, &proxypp->e.guid)); + lease_unregister (minl_auto); + lease_free (minl_auto); + lease_free (proxypp->lease); + } + entity_common_fini (&proxypp->e); + ddsrt_free (proxypp); +} +#endif + void new_proxy_participant ( struct q_globals *gv, @@ -4145,6 +4541,9 @@ void new_proxy_participant runs on a single thread, it can't go wrong. FIXME, maybe? The same holds for the other functions for creating entities. */ struct proxy_participant *proxypp; +#ifdef DDSI_INCLUDE_SECURITY + bool secure = false; +#endif assert (ppguid->entityid.u == NN_ENTITYID_PARTICIPANT); assert (entidx_lookup_proxy_participant_guid (gv->entity_index, ppguid) == NULL); @@ -4229,7 +4628,17 @@ void new_proxy_participant nn_xqos_mergein_missing (&proxypp->plist->qos, &gv->default_plist_pp.qos, ~(uint64_t)0); ddsrt_avl_init (&proxypp_groups_treedef, &proxypp->groups); +#ifdef DDSI_INCLUDE_SECURITY + proxypp->remote_identity_handle = 0; + proxypp->sec_attr = NULL; + secure = ((bes & NN_DISC_BUILTIN_ENDPOINT_PARTICIPANT_SECURE_ANNOUNCER) != 0); + if (!secure) + { + /* Make sure we don't create any security builtin endpoint when it's considered unsecure. */ + proxypp->bes &= BES_MASK_NON_SECURITY; + } set_proxy_participant_security_info(proxypp, plist); +#endif if (custom_flags & CF_INC_KERNEL_SEQUENCE_NUMBERS) proxypp->kernel_sequence_numbers = 1; @@ -4253,12 +4662,52 @@ void new_proxy_participant else proxypp->proxypp_have_cm = 0; - /* Proxy participant must be in the hash tables for - new_proxy_{writer,reader} to work */ - entidx_insert_proxy_participant_guid (gv->entity_index, proxypp); +#ifdef DDSI_INCLUDE_SECURITY + if (secure) + { + /* Secure participant detected: start handshake. */ + if ((plist->present & PP_IDENTITY_TOKEN)) + { + /* initialize the security attributes associated with the proxy participant */ + q_omg_security_init_remote_participant(proxypp); - /* TODO: Do security checks on the proxy participant. Either add the endpoints or delete the proxy. */ + /* check if the proxy participant has a match with a local participant */ + if (proxy_participant_check_security_info(gv, proxypp)) + { + /* Proxy participant must be in the hash tables for new_proxy_{writer,reader} to work */ + entidx_insert_proxy_participant_guid (gv->entity_index, proxypp); + /* Create builtin endpoints, of which a few are used in the handshake. */ + add_proxy_builtin_endpoints(gv, ppguid, proxypp, timestamp); + /* create authentication handshakes for each local secure participant */ + proxy_participant_create_handshakes(gv, proxypp); + } + else + { + DDS_CWARNING(&gv->logconfig, "Remote secure participant "PGUIDFMT" not allowed\n", PGUID (*ppguid)); + free_proxy_participant(proxypp); + } + } + else + { + /* Do not communicate with un-secure participants. */ + DDS_CWARNING(&gv->logconfig, "Don't communicate with secure participant "PGUIDFMT" which does not provide an identity token\n", PGUID (*ppguid)); + free_proxy_participant(proxypp); + } + } + else + { + /* Remote is un-secure. Try the discovery anyway. Maybe there's a local secure + * participant that allowed communication with remote non-secure ones + */ + entidx_insert_proxy_participant_guid (gv->entity_index, proxypp); + add_proxy_builtin_endpoints(gv, ppguid, proxypp, timestamp); + DDS_CLOG (DDS_LC_INFO, &gv->logconfig, "Un-secure participant "PGUIDFMT" tries to connect.\n", PGUID (*ppguid)); + } +#else + /* Proxy participant must be in the hash tables for new_proxy_{writer,reader} to work */ + entidx_insert_proxy_participant_guid (gv->entity_index, proxypp); add_proxy_builtin_endpoints(gv, ppguid, proxypp, timestamp); +#endif } int update_proxy_participant_plist_locked (struct proxy_participant *proxypp, seqno_t seq, const struct nn_plist *datap, enum update_proxy_participant_source source, nn_wctime_t timestamp) @@ -4356,6 +4805,10 @@ static void unref_proxy_participant (struct proxy_participant *proxypp, struct p } ddsrt_mutex_unlock (&proxypp->e.lock); ELOGDISC (proxypp, "unref_proxy_participant("PGUIDFMT"): refc=0, freeing\n", PGUID (proxypp->e.guid)); + +#ifdef DDSI_INCLUDE_SECURITY + q_omg_security_deregister_remote_participant(proxypp); +#endif unref_addrset (proxypp->as_default); unref_addrset (proxypp->as_meta); nn_plist_fini (proxypp->plist); @@ -4478,6 +4931,61 @@ static void delete_ppt (struct proxy_participant *proxypp, nn_wctime_t timestamp gcreq_proxy_participant (proxypp); } +#ifdef DDSI_INCLUDE_SECURITY + +struct setab { + enum entity_kind kind; + uint32_t id; +}; + + +static void downgrade_to_nonsecure(struct proxy_participant *proxypp) +{ + const nn_wctime_t tnow = now(); + struct ddsi_guid guid; + static const struct setab setab[] = { + {EK_PROXY_WRITER, NN_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_SECURE_WRITER}, + {EK_PROXY_READER, NN_ENTITYID_SEDP_BUILTIN_PUBLICATIONS_SECURE_READER}, + {EK_PROXY_WRITER, NN_ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_WRITER}, + {EK_PROXY_READER, NN_ENTITYID_SEDP_BUILTIN_SUBSCRIPTIONS_SECURE_READER}, + {EK_PROXY_WRITER, NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_STATELESS_MESSAGE_WRITER}, + {EK_PROXY_READER, NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_STATELESS_MESSAGE_READER}, + {EK_PROXY_WRITER, NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_SECURE_WRITER}, + {EK_PROXY_READER, NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_MESSAGE_SECURE_READER}, + {EK_PROXY_WRITER, NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_WRITER}, + {EK_PROXY_READER, NN_ENTITYID_P2P_BUILTIN_PARTICIPANT_VOLATILE_SECURE_READER}, + {EK_PROXY_WRITER, NN_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_WRITER}, + {EK_PROXY_READER, NN_ENTITYID_SPDP_RELIABLE_BUILTIN_PARTICIPANT_SECURE_READER} + }; + int i; + + DDS_CWARNING (&proxypp->e.gv->logconfig, "downgrade participant "PGUIDFMT" to non-secure\n", PGUID (proxypp->e.guid)); + + guid.prefix = proxypp->e.guid.prefix; + /* Remove security related endpoints. */ + for (i = 0; i < (int)(sizeof(setab)/sizeof(*setab)); i++) + { + guid.entityid.u = setab[i].id; + switch (setab[i].kind) + { + case EK_PROXY_READER: + (void)delete_proxy_reader (proxypp->e.gv, &guid, tnow, 0); + break; + case EK_PROXY_WRITER: + (void)delete_proxy_writer (proxypp->e.gv, &guid, tnow, 0); + break; + default: + assert(0); + } + } + + /* Cleanup all kinds of related security information. */ + q_omg_security_deregister_remote_participant(proxypp); + proxypp->bes &= BES_MASK_NON_SECURITY; +} +#endif + + typedef struct proxy_purge_data { struct proxy_participant *proxypp; const nn_locator_t *loc;