9. Building and Running Security Plugins-Based Applications

To use the Security Plugins with your DDS applications:

  1. Decide whether to load the Security Plugins statically or dynamically. (See Linking Applications with the Security Plugins.)

  2. Configure the DomainParticipants based on that decision. This includes specifying how to load the Security Plugins, by setting the security-related participant properties listed in Table 9.1. The properties you have to configure depend on whether you link your libraries statically or dynamically. You also need to configure the security artifacts that your DomainParticipants need to communicate securely (as specified in Using Connext DDS Secure).

  3. Link your DDS application against the Security Plugins libraries and the OpenSSL libraries. These libraries will be different for each target architecture.

    The Security Plugins libraries are in the rti_security_plugins-<connext version>-target-openssl-<1.1.1 or 3.0>-<target architecture>.rtipkg bundle;

    There are two types of OpenSSL bundles that you need to install, a host and a target:

    • openssl-<openssl version>-<connext version>-host-<host platform>.rtipkg

    • openssl-<openssl version>-<connext version>-target-<target architecture>.rtipkg

    See Download Instructions in the RTI Security Plugins Installation Guide. Refer to Libraries Required for Using the RTI Security Plugins to select the appropriate files for your platform and your chosen library format.

Note

You must set the security-related participant properties before you create a DomainParticipant (see Table 9.1). You cannot create a DomainParticipant without security and then call DomainParticipant::set_qos() with security properties, even if the DomainParticipant has not yet been enabled.

Note

You can have secure and unsecure DomainParticipants in the same DDS application. However, all your Secure DomainParticipants will load the same OpenSSL library with the same configuration (including the same OpenSSL Engines). If you manually load OpenSSL before creating any participant, your Secure DomainParticipants will reuse that OpenSSL library and its configuration.

9.1. Linking Applications with the Security Plugins

Your application’s start sequence will be different depending on whether you use static or dynamic linking. This choice will also have an impact on how you need to configure your application.

Table 9.1 defines the properties that you need to set in your DomainParticipant to load the Security Plugins. Depending on whether you link your libraries statically or dynamically, you need to set different properties to load the Security Plugins. The following sections will guide you through the linking process.

Differences in the Startup Sequence of Connext DDS Secure Applications Using Static Linking and Dynamic Linking

Figure 9.1 Differences in the Startup Sequence of Connext DDS Secure Applications Using Static Linking and Dynamic Linking

Important

Do not mix dynamic and static libraries. See Mixing Libraries Not Supported for further information.

9.1.1. Dynamic Linking

Note

For a step-by-step example on dynamic linking, please see the RTI Security Plugins Getting Started Guide.

When you use dynamic linking, your application loads the Connext Core libraries at load time and all the plugins (Transport, Security, Discovery, etc.) at run time. On Linux systems, for instance, your applications need to link against libnddsc.so and libnddscore.so. You can link your applications to these libraries with GCC using the following command:

$ gcc -o myApp myApp.o -L$NDDSHOME/lib/$ARCH -lnddsc -lnddscore

Note that in the above command, the Security Plugins are not included in the list of required libraries, because it is dynamically loaded at run time from the DomainParticipantQos com.rti.serv.secure.library (see Table 9.1). Using dynamic linking allows you to control the Security Plugins from the DomainParticipantQos. For example, if you are configuring the DomainParticipantQos using XML, the required snippet will look similar to:

<domain_participant_qos>
  <property>
    <value>
      <element>
        <name>com.rti.serv.secure.library</name>
        <value>nddssecurity</value>
      </element>
    </value>
  </property>
</domain_participant_qos>

This example QoS will make your DomainParticipant load the nddssecurity library (libnddssecurity.so on Linux systems), which in turn loads the OpenSSL libraries. Note that the Security Plugins are loaded on a per DomainParticipant basis. Therefore, you can have secure and unsecure DomainParticipants in the same application. Similarly, each of your DomainParticipants can load a different set of plugins.

Dynamic loading is particularly useful if, for example, you use your own security plugins library, because the library can be easily defined at run time through the QoS.

This is the initialization sequence of a DDS application using the Security Plugins with dynamic linking:

  1. When your application starts, the dynamic loader automatically loads the Connext libraries (libnddsc.so, libnddscore.so) from your dynamic library search path (LD_LIBRARY_PATH on Linux systems).

  2. When the DomainParticipant is created and the QoS is set, the Connext Core libraries dynamically load the security library (libnddssecurity.so) at run time. Because the Security Plugins library depends on libnddscore.so, the dynamic loader knows that the library has already been loaded, and it automatically resolves the undefined symbols to use the currently loaded library.

  3. Since the Security Plugins library depends on OpenSSL shared objects, these are automatically loaded by the dynamic loader. Therefore, OpenSSL libraries have to be accessible from your dynamic library search path (LD_LIBRARY_PATH on Linux systems).

  4. On Linux systems, if you encounter a segmentation fault in libcrypto.so, you may be dynamically loading the libcrypto.so from the operating system (possibly in addition to loading the libcrypto.so from RTI). In that case, set LD_PRELOAD to the RTI libcrypto.so. Example:

    export LD_PRELOAD=$NDDSHOME/resource/app/lib/x64Linux2.6gcc4.4.5/libcrypto.so.1.1 $NDDSHOME/bin/rtiadminconsole
    

Hint

When linking dynamically, the %PATH% or $LD_LIBRARY_PATH environment variable must include RTI and OpenSSL DLLs or libraries.

9.1.2. Static Linking

If you choose to statically link the RTI libraries, the mechanism for dynamically selecting and loading the Security Plugins is no longer available. Compared to dynamic linking, you need to pay attention to two things with static linking.

First, you need to include the Security Plugins library and the OpenSSL dependency libraries in the list of libraries needed during linking:

$ gcc -o myApp myApp.o -L$NDDSHOME/lib/$ARCH -lnddssecurityz $NDDSHOME/third_party/openssl-<version>/$ARCH/release/lib/libssl.a $NDDSHOME/third_party/openssl-<version>/$ARCH/release/lib/libcrypto.a

Note

For an example of the full linking command see <path to examples>/connext_dds/c/hello_security.

Second, you need to manually tell Connext the pointer to the function of the entry point of the Security Plugins before you create the DomainParticipant. Setting this pointer requires setting the com.rti.serv.secure.create_function_ptr property (see Table 9.1). Here is an example on how to set this pointer in code:

#include "security/security_default.h"
...
extern "C" int publisher_main(int domainId, int sample_count)
{
  DDS_DomainParticipantFactory *factory = NULL;
  struct DDS_DomainParticipantQos participantQos =
    DDS_DomainParticipantQos_INITIALIZER;

  DDS_DomainParticipant *participant = NULL;
  factory = DDS_DomainParticipantFactory_get_instance();
  if (factory == NULL) {
    /* error */
  }
  if (DDS_DomainParticipantFactory_get_default_participant_qos(
        factory, &participantQos) != DDS_RETCODE_OK) {
    /* error */
  }
  /* This property indicates that the DomainParticipant has security turned
   * on. The property name MUST be "com.rti.serv.load_plugin". The value can
   * be anything.*/
  if (DDS_PropertyQosPolicyHelper_add_property(
        &participantQos.property,
        "com.rti.serv.load_plugin",
        "com.rti.serv.secure",
        DDS_BOOLEAN_FALSE)
      != DDS_RETCODE_OK) {
    /* error */
  }
  /* The property name "com.rti.serv.secure.create_function_ptr" indicates the
   * entry point for the Security Plugins library.  The value MUST be the
   * value of the function pointer of RTI_Security_PluginSuite_create */
  if (DDS_PropertyQosPolicyHelper_add_pointer_property(
        &participantQos.property,
        "com.rti.serv.secure.create_function_ptr",
        RTI_Security_PluginSuite_create)
      != DDS_RETCODE_OK) {
    /* error */
  }
  /* create DomainParticipant with participantQos */
  participant=
    DDS_DomainParticipantFactory_create_participant(
        factory, domainId, &participantQos,
        NULL /* listener */,
        DDS_STATUS_MASK_NONE);
  if (participant == NULL) {
    /* error */
  }
  ...

Enabling the Security Plugins in code requires recompiling your application each time you want to enable and disable them. As an alternative, you can set the function pointer value for com.rti.serv.secure.create_function_ptr property in an environment variable that is set programmatically. Hence you can specify the function pointer in an XML file, which allows you to enable and disable the Security Plugins without recompiling.

If you choose to use this method, you have to specify the environment variable from which the pointer will be loaded, as follows:

<domain_participant_qos>
  <property>
    <value>
      <element>
        <name>com.rti.serv.load_plugin</name>
        <value>com.rti.serv.secure</value>
      </element>
      <element>
        <name>com.rti.serv.secure.create_function_ptr</name>
        <value>$(RTISECURITYFUNCPTR)</value>
      </element>
    </value>
  </property>
</domain_participant_qos>

Then, in the DDS application that links in the Security Plugins, get the function pointer of RTI_Security_PluginSuite_create and write it to the same environment variable you named in the XML file (RTISECURITYFUNCPTR in this example). Be aware that your application must set the environment variable before it creates the DomainParticipant that loads the QoS Profile using that variable, as shown in the following snippet:

#include "security/security_default.h"
  ...
  char putenvBuffer[35]; /* 35 = strlen(“RTISECURITYFUNCPTR”) + 2*8 + 1 */
  int putenvReturn;
  putenvBuffer[0] = '\0';
  sprintf(putenvBuffer, "RTISECURITYFUNCPTR=%p",
      RTI_Security_PluginSuite_create);
  putenvReturn = putenv(putenvBuffer);
  if (putenvReturn) {
    printf(
        "Error: couldn't set env variable for RTISECURITYFUNCPTR. "
        "error code: %d\n", putenvReturn);
  }
  ...
  /* create DomainParticipant */
  ...

Finally, you have to create a DomainParticipant by using the XML profile where you load the function pointer.

Caution

putenv() will not export variables from your process to the calling process (e.g., to the shell). Instead, the environment variables set in your process will only apply to your process and child processes of it. Therefore, you need to use this function in the same process that links in the Security Plugins (or a child process).

This is the initialization sequence of a DDS application using the Security Plugins with static linking:

  1. Since your application includes the binary code of the Connext libraries, when it starts, these libraries are copied in the memory space of your application.

  2. When the DomainParticipant is created and the QoS is set, the Connext Core libraries initialize the Security Plugins, which are also in the application’s memory space along with the OpenSSL library.

Important

If you statically link the Security Plugins, the QoS property com.rti.serv.secure.library will be silently ignored, if defined. With static linking, the Security Plugins are only set at compile time; there is no runtime selection.

9.2. Mixing Libraries Not Supported

You must choose either static or dynamic linking. Mixing static and dynamic RTI libraries —for example, using static Connext Core libraries and dynamic Security Plugins libraries— is not supported.

The examples in this section are for Linux systems, but except for small differences in names, the same concepts apply to Windows and macOS systems as well.

Suppose you have a Connext-based application, myApp, and you want to use the Security Plugins to protect the communication. The library dependency looks something like Figure 9.2.

Library Dependency

Figure 9.2 Library Dependency

Figure 9.2 is a simple and common situation, but make sure that the Connext Core libraries that your application uses are the same kind of libraries that the Security Plugins uses. For example, if myApp links statically with nddsc, but you load nddssecurity dynamically, there will be a mismatch between the libraries, potentially creating a dangerous situation. In particular, problems may become visible when you enable the Security Plugins logging distribution (see Configuring the Logging Distribution).

You can easily end up in a mixed linking scenario without realizing it. For example, suppose you design your statically linked application without security in mind, then add security from the QoS. This scenario is not valid because when the runtime dynamic loader loads the Security Plugins library, it also loads a second copy of the Connext Core libraries in memory as shown by Figure 9.3.

Mixed Library Linking (Not Supported)

Figure 9.3 Mixed Library Linking (Not Supported)

If you mix static and dynamic libraries, your application might still work, but this configuration is not supported and you might end up with unexpected behavior at run time. To avoid undesired situations, you must use static or dynamic linking, but not both.

Important

Even if a combination of static and dynamic libraries seems to work, RTI cannot guarantee there won’t be issues when running the Connext application.

9.3. Properties for Enabling Security

Table 9.1 lists the properties that you can use for enabling security. These properties are configured through the DomainParticipant’s PROPERTY QosPolicy (see QoS Properties).

Table 9.1 Properties for Enabling Security

Property Name (prefix with com.rti.serv.secure.) 1

Property Value Description

com.rti.serv.load_plugin

Note:

This property does not take a prefix.

Required

The prefix name of the security plugin suite that will be loaded by Connext. For example: com.rti.serv.secure. You will use this string as the prefix to some of the property names. Setting this value to non-NULL will also configure the DomainParticipant to attempt authentication with newly discovered remote participants.

Note:

You can load only one security plugin suite.

String.

Default: NULL unless using the Generic.Security builtin profile

library

Only required if linking dynamically

Must be set to the dynamic library that implements the security plugin suite. If using one of Connext’s provided security plugin suites, you must set this value to either nddssecurity (Security Plugins library) or nddslightweightsecurity (Lightweight Security Plugins library).

This library and the dependent OpenSSL libraries must be in your library search path (pointed to by the environment variable LD_LIBRARY_PATH on Linux systems, Path on Windows systems, or DYLD_LIBRARY_PATH on macOS systems).

String.

Default: NULL unless using the Generic.Security builtin profile

create_function

Only required if linking dynamically

Must be set to the security plugin suite creation function that is implemented by the library. If using Connext’s provided security plugin suite, you must set this value to RTI_Security_PluginSuite_create.

String.

Default: NULL unless using the Generic.Security builtin profile

create_function_ptr

Only required if linking statically

Must be set to the security plugin suite creation function implemented by the library. If using Connext’s provided security plugin suite, you must set this value to the stringified pointer value of RTI_Security_PluginSuite_create, as demonstrated in the hello_security examples.

Notes:
  • You cannot set this value in an XML profile. You must set it in code.

  • If this property is set to a value other than NULL, it will always take effect, even if create_function is also set to a value other than NULL.

String.

Default: NULL

property_validation_action

Optional

By default, property names given in the PROPERTY QoSPolicy are validated to avoid using incorrect or unknown names (for example, due to a typo). This behavior is controlled by the dds.participant.property_validation_action property (see Property Validation in the RTI Connext DDS Core Libraries User’s Manual for more information).

In general, we recommend that you use dds.participant.property_validation_action to control the validation of the properties for both the Connext Core libraries and the Security Plugins libraries. However, there are cases where you might want to configure different behaviors for the Core and the Security Plugins. For example, suppose you are running a customized version of the Security Plugins that supports a new, experimental property. In that case, you will need to disable the Connext Core property validation but still may want to keep the Security Plugins property validation.

The Security Plugins’s property_validation_action property configures the validation of the property names associated with the Security Plugins:

  • VALIDATION_ACTION_EXCEPTION: validate the properties. Upon failure, log exceptions and fail.

  • VALIDATION_ACTION_SKIP: skip validation.

  • VALIDATION_ACTION_WARNING: validate the properties. Upon failure, log warnings and do not fail.

If this property is not set, the property validation behavior will be the same as that of the DomainParticipant (controlled by the dds.participant.property_validation_action property), which by default is VALIDATION_ACTION_EXCEPTION. See Property Validation in the Connext DDS Core Libraries User’s Manual for more information. See also the Properties Reference Guide.

Enum: VALIDATION_ACTION_EXCEPTION, VALIDATION_ACTION_SKIP, VALIDATION_ACTION_WARNING.

Default: not set

1

Assuming you used com.rti.serv.secure as the alias to load the plugin. If not, change the prefix to match the string used with com.rti.serv.load_plugins, followed by the . character.

9.4. Advanced Concepts

9.4.1. Creating/Deleting a DomainParticipant as a C++ Static Object

In some scenarios, you may want a DomainParticipant that can be shared by multiple functions. You could implement this by declaring a DomainParticipant as a C++ static object, as in the following example.

struct Test {
  DDSDomainParticipantFactory *factory;
  DDSDomainParticipant *participant;
  Test(string n) : name(n)
  {
    DDS_DomainParticipantQos participantQos;

    factory = DDSDomainParticipantFactory::get_instance();
    factory->get_default_participant_qos(participantQos);

    /* set up participantQos to use security */

    participant = factory->create_participant(
        0,
        participantQos,
        NULL,
        DDS_STATUS_MASK_NONE);
  }
  ~Test()
  {
    factory->delete_participant(participant);
    DDSDomainParticipantFactory::finalize_instance();
  }
  std::string name;
};
Test my_static("my_static");

int main(int argc, char **argv)
{
  return 0;
}

This DomainParticipant is created and deleted outside of the main() function. This code may crash because OpenSSL®’s cleanup function, OPENSSL_cleanup() (see OPENSSL_cleanup in OpenSSL Documentation), may get executed before the ~Test() destructor gets executed, in which case some of the DomainParticipant’s internal OpenSSL state will be inconsistent. To work around this problem, you must invoke OPENSSL_init_crypto() and OPENSSL_cleanup() as follows:

/* Add -I$(NDDSHOME)/third_party/openssl-<version>/$(target)/release/include to your compilation flags. */
#include <openssl/crypto.h>

struct Test {
  DDSDomainParticipantFactory *factory;
  DDSDomainParticipant *participant;
  Test(string n) : name(n)
  {
    DDS_DomainParticipantQos participantQos;

    factory = DDSDomainParticipantFactory::get_instance();
    factory->get_default_participant_qos(participantQos);

    /* Set up participantQos to use security */
    ...

    /*
     * OPENSSL_INIT_NO_ATEXIT prevents OpenSSL from registering
     * OPENSSL_cleanup() as a function to be invoked upon exit.
     * The other two flags are required by Security Plugins. Only call
     * OPENSSL_init_crypto before creating the first participant in the process.
     */
    OPENSSL_init_crypto(
        OPENSSL_INIT_NO_ATEXIT
        | OPENSSL_INIT_ADD_ALL_CIPHERS
        | OPENSSL_INIT_ADD_ALL_DIGESTS,
        NULL);

    participant = factory->create_participant(
        0,
        participantQos,
        NULL,
        DDS_STATUS_MASK_NONE);
  }

  ~Test()
  {
    factory->delete_participant(participant);

    /*
     * Make sure that OPENSSL_cleanup is invoked only after deleting the last
     * participant in the process.
     */
    OPENSSL_cleanup();
    DDSDomainParticipantFactory::finalize_instance();
  }

  std::string name;
};

9.5. Platform-Specific Notes

9.5.1. Building Security Plugins-Based Applications for VxWorks 7

You can use the Security Plugins on supported VxWorks 7 platforms (see Supported Platforms in the Security Plugins Release Notes).

Building VxWorks applications requires cross-compiling your source code for your target platform. When building Connext DDS Secure applications for VxWorks 7, you will use the OpenSSL libraries from Wind River that are installed on your VxWorks system. Therefore, there’s no need to install any OpenSSL bundle provided by RTI. Instead, applications should be linked against the OpenSSL libraries provided with the OS (installed by default).

The OpenSSL binaries (such as openssl) will be under <vxworks_toolchain_path>/vxworks-7/host/<host_arch>/bin.

The OpenSSL libraries (such as libcrypto) will be under <your_vsb_directory>/usr/lib/common.

9.5.1.1. Considerations for Building and Running Connext DDS Secure in Kernel Modules

Note

Load the libnddssecurity module before running your application.

To be able to run the Security Plugins as a kernel module, the OpenSSL symbols must be available in the kernel. To do that, you must include the component INCLUDE_IPSSL in your kernel. Once you have done this, OpenSSL is already included in the kernel itself, and you do not have to include it in the application kernel module. If INCLUDE_IPSSL is not enabled, you will need to rebuild your kernel, including this component.

9.5.1.2. Considerations for Building and Running Connext DDS Secure in RTP Executables

When running an RTP (Real-Time Process) executable, the process runs at user level, which means that the program does not have access to the kernel memory directly. Thus, the process described in the previous section, Considerations for Building and Running Connext DDS Secure in Kernel Modules, is not valid in this case. In other words, even if you already included OpenSSL symbols in your kernel, the RTP executable cannot use them directly, and thus, you must link it against libcrypto.

As you will be using the OpenSSL shipped by Wind River, when you link libcrypto against the application, you need to link the one placed into <your_vsb_directory>/usr/lib/common. To do that, replace $NDDSHOME/third_party/openssl-<version> with <your_vsb_directory>/usr/lib/common, where your system’s libraries live. Other than that, follow the steps in Dynamic Linking.

9.6. Libraries Required for Using the RTI Security Plugins

To use the RTI Security Plugins, link against the additional libraries in one of the following tables, depending on your platform. Select the files appropriate for your chosen library format.

Table 9.2 Additional Libraries for Using RTI Security Plugins on Linux Systems

Library Format

RTI Security Plugins Libraries 6

OpenSSL 1.1.1 Libraries 7

OpenSSL 3.0 Libraries 8

Dynamic Release

libnddssecurity.so

  • libssl.so

  • libcrypto.so

  • libssl.so

  • libcrypto.so

Dynamic Debug

libnddssecurityd.so

  • libssl.so

  • libcrypto.so

  • libssl.so

  • libcrypto.so

Static Release

libnddssecurityz.a

  • libssl.a

  • libcrypto.a

  • libssl.a

  • libcrypto.a

Static Debug

libnddssecurityzd.a

  • libssl.a

  • libcrypto.a

  • libssl.a

  • libcrypto.a

6(1,2,3,4,5)

These libraries are in <NDDSHOME>/lib/<architecture>.

7(1,2,3)

These libraries are in <NDDSHOME>/third_party/openssl-1.1.1<letter>/<architecture>/<debug or release dir>/lib.

8(1,2,3)

These libraries are in <NDDSHOME>/third_party/openssl-3.0.<number>/<architecture>/<debug or release dir>/lib.

9.7. Libraries Required for Using the RTI Lightweight Security Plugins

To use the RTI Lightweight Security Plugins, link against the additional libraries in one of the following tables, depending on your platform. Select the files appropriate for your chosen library format.

Table 9.8 Additional Libraries for Using RTI Lightweight Security Plugins on Linux Systems

Library Format

RTI Lightweight Security Plugins Libraries 13

OpenSSL 1.1.1 Libraries 14

OpenSSL 3.0 Libraries 15

Dynamic Release

libnddslightweightsecurity.so

libcrypto.so

libcrypto.so

Dynamic Debug

libnddslightweightsecurityd.so

libcrypto.so

libcrypto.so

Static Release

libnddslightweightsecurityz.a

libcrypto.a

libcrypto.a

Static Debug

libnddslightweightsecurityzd.a

libcrypto.a

libcrypto.a

13(1,2,3,4,5)

These libraries are in <NDDSHOME>/lib/<architecture>.

14(1,2,3)

These libraries are in <NDDSHOME>/third_party/openssl-1.1.1<letter>/<architecture>/<debug or release dir>/lib.

15(1,2,3)

These libraries are in <NDDSHOME>/third_party/openssl-3.0.<number>/<architecture>/<debug or release dir>/lib.