Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions examples/connext_secure/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ if(NOT DEFINED CONNEXTDDS_CONNEXT_SECURE_EXAMPLES)
set(CONNEXTDDS_CONNEXT_SECURE_EXAMPLES
"cds"
"certificate_revocation_list"
"dynamic_permissions"
"lightweight"
"whitelist"
)
Expand Down
93 changes: 93 additions & 0 deletions examples/connext_secure/dynamic_permissions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Example Code: Dynamic Permissions

## Concept

This example showcases how the Security Plugins enforce Permissions Document
expiration, and how the Permissions Document can be renewed to resume
communication.

## Building the Example

Use the following commands to build the example and get the executables that
you can run:

```sh
cd c++11/
mkdir build && cd build
cmake ..
cmake --build .
```

You can optionally pass the
``-DCONNEXTDDS_DIR=<your_connext_installation_directory>``,
``-DOPENSSL_ROOT_DIR=<your_openssl_installation_directory>``,
``-DCONNEXTDDS_ARCH=<your_architecture>``,
``-DCMAKE_BUILD_TYPE=<Debug/Release>``, and
``-DBUILD_SHARED_LIBS=<ON/OFF>`` variables to the cmake configuration step.

After building the example, you will have a publisher Permissions Document that
expires in 1 minute. If you need to re-create it, please remove this file from
your build directory and re-run the ``createExpiringPermissions`` target.

```sh
rm security/ecdsa01/xml/Permissions2_expiring.xml && \
cmake --build . --target createExpiringPermissions
```

## Running the example

Demo is based on a standard rtiddsgen publisher and subscriber example code.

Run a publisher and a subscriber in separate terminal windows.

```sh
./dynamic_permissions_publisher
```

```sh
./dynamic_permissions_subscriber
```

Verify that they communicate and that the subscriber is receiving data.

```sh
# Publisher
Writing ::DynamicPermissions, count 0
Writing ::DynamicPermissions, count 1
# [...]

# Subscriber
::DynamicPermissions subscriber sleeping up to 1 sec...
[value: 0]
::DynamicPermissions subscriber sleeping up to 1 sec...
[value: 1]
::DynamicPermissions subscriber sleeping up to 1 sec...
# [...]
```

Once the Permissions Document of the publisher DomainParticipant expires, you
will see the following error messages:

```sh
# Publisher
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|CHECK STATUS|LC:Security] RTI_Security_PermissionsGrant_isValidTime:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517658","n":"108000"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85264","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"831AB06E.43876C36.FD825600.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_PermissionsGrant_isValidTime"}]}],"m":"now is after not_after of permissions file"}}
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|CHECK STATUS|LC:Security] RTI_Security_AccessControl_validate_status:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517658","n":"192000"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85264","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"831AB06E.43876C36.FD825600.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_AccessControl_validate_status"}]}],"m":"permissions' validity period is invalid."}}
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|CHECK STATUS|LC:Security] PRESParticipant_onSecurityLocalCredentialValidateEvent:FAILED TO VALIDATE | Local permissions credentials.
ERROR [0x831AB06E,0x43876C36,0xFD825600:0x000001C1|ADVANCE NOTIFY INVALID LOCAL PERMISSIONS|LC:Security] PRESParticipant_onSecurityLocalCredentialEventListener:FAILED TO VALIDATE | Local credentials.

# Subscriber
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] RTI_Security_PermissionsGrant_isValidTime:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517682","n":"984966998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_PermissionsGrant_isValidTime"}]}],"m":"now is after not_after of permissions file"}}
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] RTI_Security_AccessControl_validatePermissionsDocument:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517682","n":"985028998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_AccessControl_validatePermissionsDocument"}]}],"m":"grant has invalid time"}}
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] RTI_Security_AccessControl_validate_remote_permissions:{"DDS:Security:LogTopicV2":{"f":"10","s":"1","t":{"s":"1748517682","n":"985044998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"DDS:Access:Permissions"},{"plugin_method":"RTI_Security_AccessControl_validate_remote_permissions"}]}],"m":"failed to validate remote permissions"}}
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|GET SECURITY STATE|LC:Security] DDS_DomainParticipantTrustPlugins_forwardGetAuthenticatedRemoteParticipantSecurityState:FAILED TO VALIDATE | Remote permissions.
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|LC:Security] PRESParticipant_authorizeRemoteParticipant:{"DDS:Security:LogTopicV2":{"f":"10","s":"3","t":{"s":"1748517682","n":"985078998"},"h":"RTISP-10036","i":"0.0.0.0","a":"RTI Secure DDS Application","p":"85248","k":"50331706","x":[{"DDS":[{"domain_id":"0"},{"guid":"DED844B7.87B9550F.B66DD964.000001C1"},{"plugin_class":"RTI:Auth"},{"plugin_method":"PRESParticipant_authorizeRemoteParticipant"}]}],"m":"unauthorized remote participant 831ab06e.43876c36.fd825600 denied by local participant ded844b7.87b9550f.b66dd964"}}
ERROR [PARSE MESSAGE|0xDED844B7,0x87B9550F,0xB66DD964:0x000201C4{Entity=DR,MessageKind=DATA}|RECEIVE FROM 0x831AB06E,0x43876C36,0xFD825600:0x000201C3|:0x000001C1{Domain=0}|RECEIVE SAMPLE|PROCESS HANDSHAKE|LC:Security] PRESParticipant_processHandshake:FAILED TO VALIDATE | Failed to authorize remote DP (GUID: 0x831AB06E,0x43876C36,0xFD825600:0x000001C1).
```

Communication will stop.

## Renewing the Permissions Document

This example updates the publisher DomainParticipant's Permissions Document
after 70 samples. At that point, communication with the subscriber will
resume.
67 changes: 67 additions & 0 deletions examples/connext_secure/dynamic_permissions/c++11/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#
# (c) 2025 Copyright, Real-Time Innovations, Inc. All rights reserved.
#
# RTI grants Licensee a license to use, modify, compile, and create derivative
# works of the Software. Licensee has the right to distribute object form
# only for use with RTI products. The Software is provided "as is", with no
# warranty of any type, including any warranty for fitness for any purpose.
# RTI is under no obligation to maintain or support the Software. RTI shall
# not be liable for any incidental or consequential damages arising out of the
# use or inability to use the software.
#
cmake_minimum_required(VERSION 3.11)
project(rtiexamples-dynamic-permissions)
list(APPEND CMAKE_MODULE_PATH
"${CMAKE_CURRENT_SOURCE_DIR}/../../../../resources/cmake/Modules"
)
include(ConnextDdsConfigureCmakeUtils)
connextdds_configure_cmake_utils()

find_package(RTIConnextDDS
"7.0.0"
REQUIRED
COMPONENTS
security_plugins
)

if(NOT TARGET RTIConnextDDS::security_plugins)
message(WARNING "RTIConnextDDS::security_plugins component is missing. Skipping example")
return()
endif()

# Include ConnextDdsAddExample.cmake from resources/cmake
include(ConnextDdsAddExample)

connextdds_add_example(
IDL "dynamic_permissions"
LANG "C++11"
)

include (ConnextDdsGenerateSecurityArtifacts)
connextdds_generate_security_artifacts()

# Do a copy of the original subscriber's Permissions Document, but with the
# validity modified so that it expires in 1 minute.
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2_expiring.xml"
COMMAND ${CMAKE_COMMAND}
-DINPUT_FILE="${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2.xml"
-DOUTPUT_FILE="${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2_expiring.xml"
-P ${CMAKE_SOURCE_DIR}/modify_permissions.cmake
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2.xml"
)

# Sign the modified Permissions Document
connextdds_openssl_smime_sign(
INPUT "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/Permissions2_expiring.xml"
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/signed/signed_Permissions2_expiring.p7s"
SIGNER_CERTIFICATE "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/certs/ca_cert.pem"
PRIVATE_KEY_FILE "${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/certs/ca_key.pem"
)

# Create a Permissions Document that is about to expire
add_custom_target(createExpiringPermissions
ALL
DEPENDS
dynamic_permissions_securityArtifacts
"${CMAKE_CURRENT_BINARY_DIR}/security/ecdsa01/xml/signed/signed_Permissions2_expiring.p7s")
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?xml version="1.0"?>
<!--
(c) 2025 Copyright, Real-Time Innovations, Inc. All rights reserved.
RTI grants Licensee a license to use, modify, compile, and create derivative
works of the Software. Licensee has the right to distribute object form only
for use with RTI products. The Software is provided "as is", with no warranty
of any type, including any warranty for fitness for any purpose. RTI is under
no obligation to maintain or support the Software. RTI shall not be liable for
any incidental or consequential damages arising out of the use or inability to
use the software.
-->
<dds xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://community.rti.com/schema/7.0.0/rti_dds_qos_profiles.xsd">
<qos_library name="dynamic_permissions_Library">
<qos_profile name="subscriber" base_name="BuiltinQosLib::Generic.Security" is_default_qos="true">
<domain_participant_qos>
<property>
<value>
<element>
<name>dds.sec.auth.identity_ca</name>
<value>file:security/ecdsa01/certs/ca_cert.pem</value>
</element>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:security/ecdsa01/certs/peer1_cert.pem</value>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:security/ecdsa01/certs/peer1_key.pem</value>
</element>
<element>
<name>dds.sec.access.permissions_ca</name>
<value>file:security/ecdsa01/certs/ca_cert.pem</value>
</element>
<element>
<name>dds.sec.access.governance</name>
<value>file:security/ecdsa01/xml/signed/signed_Governance.p7s</value>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:security/ecdsa01/xml/signed/signed_Permissions1.p7s</value>
</element>
</value>
</property>
</domain_participant_qos>
</qos_profile>
<qos_profile name="publisher" base_name="subscriber">
<domain_participant_qos>
<property>
<value>
<element>
<name>dds.sec.auth.identity_certificate</name>
<value>file:security/ecdsa01/certs/peer2_cert.pem</value>
</element>
<element>
<name>dds.sec.auth.private_key</name>
<value>file:security/ecdsa01/certs/peer2_key.pem</value>
</element>
<element>
<name>dds.sec.access.permissions</name>
<value>file:security/ecdsa01/xml/signed/signed_Permissions2_expiring.p7s</value>
</element>
</value>
</property>
</domain_participant_qos>
</qos_profile>
</qos_library>
</dds>
132 changes: 132 additions & 0 deletions examples/connext_secure/dynamic_permissions/c++11/application.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* (c) Copyright, Real-Time Innovations, 2025. All rights reserved.
* RTI grants Licensee a license to use, modify, compile, and create derivative
* works of the software solely for use with RTI Connext DDS. Licensee may
* redistribute copies of the software provided that all such copies are subject
* to this license. The software is provided "as is", with no warranty of any
* type, including any warranty for fitness for any purpose. RTI is under no
* obligation to maintain or support the software. RTI shall not be liable for
* any incidental or consequential damages arising out of the use or inability
* to use the software.
*/

#ifndef APPLICATION_H
#define APPLICATION_H

#include <iostream>
#include <csignal>
#include <climits>

namespace application {

// Catch control-C and tell application to shut down
bool shutdown_requested = false;

inline void stop_handler(int)
{
shutdown_requested = true;
std::cout << "preparing to shut down..." << std::endl;
}

inline void setup_signal_handlers()
{
signal(SIGINT, stop_handler);
signal(SIGTERM, stop_handler);
}

enum ParseReturn { PARSE_RETURN_OK, PARSE_RETURN_FAILURE, PARSE_RETURN_EXIT };

struct ApplicationArguments {
ParseReturn parse_result;
unsigned int domain_id;
unsigned int sample_count;
NDDS_Config_LogVerbosity verbosity;
};

inline void set_verbosity(ApplicationArguments &arguments, int verbosity)
{
switch (verbosity) {
case 0:
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_SILENT;
break;
case 1:
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_ERROR;
break;
case 2:
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_WARNING;
break;
case 3:
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_STATUS_ALL;
break;
default:
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_ERROR;
break;
}
}

// Parses application arguments for example. Returns whether to exit.
inline void parse_arguments(
ApplicationArguments &arguments,
int argc,
char *argv[])
{
int arg_processing = 1;
bool show_usage = false;
arguments.domain_id = 0;
arguments.sample_count = INT_MAX;
arguments.verbosity = NDDS_CONFIG_LOG_VERBOSITY_ERROR;
arguments.parse_result = PARSE_RETURN_OK;

while (arg_processing < argc) {
if ((argc > arg_processing + 1)
&& (strcmp(argv[arg_processing], "-d") == 0
|| strcmp(argv[arg_processing], "--domain") == 0)) {
arguments.domain_id = atoi(argv[arg_processing + 1]);
arg_processing += 2;
} else if (
(argc > arg_processing + 1)
&& (strcmp(argv[arg_processing], "-s") == 0
|| strcmp(argv[arg_processing], "--sample-count") == 0)) {
arguments.sample_count = atoi(argv[arg_processing + 1]);
arg_processing += 2;
} else if (
(argc > arg_processing + 1)
&& (strcmp(argv[arg_processing], "-v") == 0
|| strcmp(argv[arg_processing], "--verbosity") == 0)) {
set_verbosity(arguments, atoi(argv[arg_processing + 1]));
arg_processing += 2;
} else if (
strcmp(argv[arg_processing], "-h") == 0
|| strcmp(argv[arg_processing], "--help") == 0) {
std::cout << "Example application." << std::endl;
show_usage = true;
arguments.parse_result = PARSE_RETURN_EXIT;
break;
} else {
std::cout << "Bad parameter." << std::endl;
show_usage = true;
arguments.parse_result = PARSE_RETURN_FAILURE;
break;
}
}
if (show_usage) {
std::cout << "Usage:\n"
" -d, --domain <int> Domain ID this "
"application will\n"
" subscribe in. \n"
" Default: 0\n"
" -s, --sample_count <int> Number of samples to "
"receive before\n"
" cleanly shutting down. \n"
" Default: infinite\n"
" -v, --verbosity <int> How much debugging output "
"to show.\n"
" Range: 0-3 \n"
" Default: 1"
<< std::endl;
}
}

} // namespace application

#endif // APPLICATION_H
Loading
Loading