407 lines
18 KiB
Markdown
407 lines
18 KiB
Markdown
![]() |
# DDS Security effort
|
||
|
|
||
|
ADLink has decided to donate their Vortex OpenSplice DDS Security
|
||
|
implementation to the Cyclone DDS project. However, that will not be a simple
|
||
|
code drop.
|
||
|
|
||
|
This document catches all the work that is foreseen to port Vortex OpenSplice
|
||
|
DDS Security to Cyclone DDS.
|
||
|
|
||
|
This document can be removed when DDS Security has been implemented.
|
||
|
|
||
|
**Table of contents**
|
||
|
- [Definition of done](#done)
|
||
|
- [Footprint](#footprint)
|
||
|
- [Multi process testing (done)](#testing)
|
||
|
- [Runtime library loading (done)](#loading)
|
||
|
- [Hopscotch utility (done)](#hopscotch)
|
||
|
- [FSM utility (in progress)](#fsm)
|
||
|
- [Port DDS Security plugin API (done)](#port-api)
|
||
|
- [De-Serializing messages in DDSI (done)](#deserializing)
|
||
|
- [De-Serializing security message parameters in DDSI (done)](#deserializing_plist)
|
||
|
- [Port DDS Security builtin plugins (in progress)](#port-plugins)
|
||
|
- [Port DDSI DDS Security (in progress)](#port-ddsi)
|
||
|
- [Move configuration (in progress)](#Move-configuration)
|
||
|
- [Failure handling](#failures)
|
||
|
- [Multiple configurations](#multiple-configurations)
|
||
|
- [Example](#example)
|
||
|
- [QosProvider](#qosprovider)
|
||
|
- [Data Tags (optional)](#datatags)
|
||
|
|
||
|
|
||
|
## Definition of done<a name="done" />
|
||
|
|
||
|
When this document tells that a certain aspect is 'done', it means that it
|
||
|
has been accepted into the security branch of the cyclonedds repository
|
||
|
(https://github.com/eclipse-cyclonedds/cyclonedds/tree/security).
|
||
|
|
||
|
However, it is possible that various parts need some rework before the security
|
||
|
branch can be merged into the cyclonedds master branch.
|
||
|
|
||
|
|
||
|
## Footprint<a name="footprint" />
|
||
|
|
||
|
A non-functional requirement is that cyclonedds should be buildable without
|
||
|
the DDS Security support in it. That will reduce the footprint (and possibly
|
||
|
improve performance) for applications that don't need security.
|
||
|
|
||
|
For that, the ENABLE_SECURITY build option is introduced that translates into
|
||
|
the DDSI_INCLUDE_SECURITY compile switch. However, the usage of that switch
|
||
|
should not explode. That'll reduce the maintainability.
|
||
|
|
||
|
For instance, the usage of the switch can be minimized by using functions that
|
||
|
will reduce to an inline function that just returns a hardcode value when
|
||
|
security is not included (otherwise they'll do some certain task).
|
||
|
The compiler can use these inline functions to do clever stuff regarding
|
||
|
footprint and performance.
|
||
|
|
||
|
There can be other solutions to decrease security footprint without impeding
|
||
|
on the maintainability of the code by inserting the switch too much.
|
||
|
|
||
|
|
||
|
## Multi process testing (done)<a name="testing" />
|
||
|
|
||
|
To properly test DDS Security, multi process testing will be necessary.
|
||
|
This is not yet available in Cyclone DDS.
|
||
|
See the [Multi Process Testing](multi_process_testing.md) document for
|
||
|
more information.
|
||
|
|
||
|
|
||
|
## Runtime library loading (done)<a name="loading" />
|
||
|
|
||
|
The ddsi component needs to be able to load DDS Security plugins at runtime.
|
||
|
These plugins are provided as libraries.<br>
|
||
|
Loading libraries at runtime is currently not possible in Cyclone DDS.
|
||
|
|
||
|
|
||
|
## Hopscotch utility (done)<a name="hopscotch" />
|
||
|
|
||
|
This hash table is used by the Security plugins.<br>
|
||
|
Both versions on OpenSplice and Cyclone are equivalent.<br>
|
||
|
No additional effort is expected.
|
||
|
|
||
|
|
||
|
## FSM utility (in progress)<a name="fsm" />
|
||
|
|
||
|
The Finite State Machine utility has been introduced in OpenSplice to support
|
||
|
the handshake of DDS Security.<br>
|
||
|
This has to be ported to Cyclone.
|
||
|
|
||
|
However, it already has some technical dept, which should be removed before
|
||
|
adding it to Cyclone. This means that a refactor should happen as part of the
|
||
|
porting.
|
||
|
|
||
|
The related DBTs should also be ported to Cyclone unit tests.
|
||
|
|
||
|
It was decided to just port the FSM at the moment. The refactor will take place
|
||
|
when trying to get the security branch into master.
|
||
|
|
||
|
|
||
|
## Port DDS Security plugin API (done)<a name="port-api" />
|
||
|
|
||
|
The DDS Security plugin API are just a few header files. The ddsi component
|
||
|
uses that to link against. The implementation of the API is done in the
|
||
|
individual plugins. The plugins are [loaded at runtime](#loading) (when
|
||
|
configured).
|
||
|
|
||
|
This means that ddsi can be DDS Security prepared (after building against this
|
||
|
API) without there being actual DDS Security plugins.
|
||
|
|
||
|
It seems to be just a code drop of a number of header files.<br>
|
||
|
Maybe add some CMake module for both ddsi and the plugins to easily link
|
||
|
against?
|
||
|
|
||
|
|
||
|
## De-Serializing messages in DDSI (done)<a name="deserializing" />
|
||
|
|
||
|
DDSI needs to be able to (de)serialize a few Security messages. In OpenSplice,
|
||
|
some functionality of the database is used. This is unavailable in Cyclone.
|
||
|
|
||
|
What is available is a serializer that uses marshaling operations (see, for
|
||
|
for instance, m_ops in the dds_topic_descriptor struct).
|
||
|
|
||
|
The (de)serializing of the Security messages should be supported by supplying
|
||
|
the m_ops sequences, message structs (if not yet available) and some
|
||
|
convenience functions using both.
|
||
|
|
||
|
|
||
|
## De-Serializing security message parameters in DDSI (done)<a name="deserializing_plist" />
|
||
|
|
||
|
DDSI needs to be able to (de)serialize a few message parameters that have
|
||
|
been introduced by the DDS Security spec.
|
||
|
|
||
|
|
||
|
## Port DDS Security builtin plugins (in progress)<a name="port-plugins" />
|
||
|
|
||
|
No major changes between the DDS Security plugins in OpenSplice and Cyclone
|
||
|
are expected.
|
||
|
|
||
|
The DDS Security plugins require OpenSSL. Cyclone DDS already uses OpenSSL.
|
||
|
However, it expects (or at least it's preferred to have) version 1.1 or newer,
|
||
|
while the OpenSplice Security plugins are build against 1.0.2. There are some
|
||
|
API changes between the two versions. This will take some porting effort.
|
||
|
|
||
|
The build system should be ported from makefiles to cmake files.
|
||
|
|
||
|
There are security_plugin DBTs in OpenSplice. These tests are base on cunit,
|
||
|
which is also used in Cyclone. However, it is used slightly different. A small
|
||
|
porting effort is expected (i.e. let it work with cmake and runner generation).
|
||
|
|
||
|
This means some additional effort, compared to just a code drop. But it is not
|
||
|
expected to be major.
|
||
|
|
||
|
- Authentication plugin (done).
|
||
|
- Access Control plugin (in progress).
|
||
|
- Cryptography plugin (done).
|
||
|
|
||
|
There are a few sub-features that can be implemented separately.
|
||
|
- Check/handle expiry dates (in progress).
|
||
|
- Trusted directory support.
|
||
|
- etc?
|
||
|
|
||
|
|
||
|
## Port DDSI DDS Security (in progress)<a name="port-ddsi" />
|
||
|
|
||
|
There is already quite a bit of difference between the DDSI codebases in
|
||
|
OpenSplice and Cyclone. So, the copy/merge of the DDSI Security code from
|
||
|
OpenSplice to Cyclone will not be trivial.
|
||
|
|
||
|
Most parts of the merging will not be trivial, but should be quite
|
||
|
straightforward nonetheless. Things that are noticed to be somewhat different
|
||
|
between the DDSI code bases that could impact the merging work:
|
||
|
- Entity matching is slightly different.
|
||
|
- The q_entity.c has a lot of differences that can obfuscate the differences
|
||
|
related to DDS Security.
|
||
|
- Unacked messages logic has changed a bit. Does that impact gaps?
|
||
|
- (De)serializing, of course
|
||
|
(see also [De-Serializing in DDSI](#deserializing)).
|
||
|
- Writer history cache is different, which can impact the builtin volatile
|
||
|
Security endpoints.
|
||
|
- Unknown unknowns.
|
||
|
|
||
|
The buildsystem has to be upgraded.<br>
|
||
|
- A few files are added which are easy to add to cmake.<br>
|
||
|
- There's a new dependency on the [DDS Security API](#port-api), which is done.
|
||
|
|
||
|
Then, of course, there are the tests<br>
|
||
|
First of all, [Multi Process Testing](#testing) should be available, which now
|
||
|
it is.<br>
|
||
|
When that's the case, then the OpenSplice tests business
|
||
|
logic have to be ported from scripts and applications to that new framework.
|
||
|
That porting shouldn't be that hard. However, it will probably take a while.
|
||
|
|
||
|
The DDSI Port doesn't have to be a big bang. It can be split up into various
|
||
|
different pull requests. Examples are
|
||
|
- Extend configuration XML parsing with the security configuration (done).
|
||
|
- Extend nn_qos with security related policies. Fill them with values from the
|
||
|
configuration when applicable (done).
|
||
|
- Add DDS Security endpoints that are non-volatile (done).
|
||
|
- Add DDS Security endpoint that is volatile. This change has more impact than
|
||
|
all the non-volatile endpoints combined (done).
|
||
|
- Handshake (in progress).
|
||
|
- Payload (en)(de)coding (DDSI support: done. Wrapper: todo).
|
||
|
- Submsg (en)(de)coding (DDSI support: done. Wrapper: todo).
|
||
|
- RTPSmsg (en)(de)coding (DDSI support: done. Wrapper: todo).
|
||
|
- Etc
|
||
|
|
||
|
|
||
|
|
||
|
## Move configuration (in progress)<a name="Move-configuration" />
|
||
|
|
||
|
After the port, the DDS Security configuration is still (partly) done through
|
||
|
the overall configuration XML file (rest is coming from the permissions and
|
||
|
governance files).<br>
|
||
|
However, according to the specification, the configuration should happen by
|
||
|
means of the Participant QoS.
|
||
|
|
||
|
The ddsc dds_qos_t is mapped on the ddsi xqos. The ddsi xqos already has the
|
||
|
necessary policy (after the [port](#port-ddsi)), namely the property_policy.
|
||
|
This means that the ddsc qos itself is automatically prepared.<br>
|
||
|
However, getting and setting policies are done through getter and setter
|
||
|
functions in ddsc.<br>
|
||
|
This means we have to add these functions for the security configuration values.
|
||
|
|
||
|
The ddsc policy getter and setter functions use (arrays of) primitive types as
|
||
|
arguments. The configuration of Security is given by means of the property
|
||
|
policy, which isn't a primitive. To keep in line with the QoS API, we could add
|
||
|
something like:
|
||
|
```cpp
|
||
|
typedef struct dds_properties_t; /* opaque type in API, but mapped to
|
||
|
nn_property_qospolicy_t internally */
|
||
|
dds_properties_t *dds_properties_create();
|
||
|
void dds_properties_delete(dds_properties_t *);
|
||
|
void dds_properties_merge(dds_properties_t *, dds_properties_t *);
|
||
|
void dds_properties_add_property(dds_properties_t *, char *name, char *value);
|
||
|
void dds_properties_add_binaryproperty(dds_properties_t *, char *name,
|
||
|
uchar *value, int valuelength);
|
||
|
void dds_qset_properties(dds_qos_t*, dds_properties_t *);
|
||
|
void dds_qget_properties(dds_qos_t*, dds_properties_t **);
|
||
|
```
|
||
|
But this is very preliminary and is still up for debate.
|
||
|
|
||
|
After moving the Security configuration to the participant QoS, it's possible
|
||
|
to have different configurations within a single application if you have
|
||
|
multiple participants. However, ddsi only supports one Security configuration
|
||
|
for now. That doesn't change by changing where that configuration comes
|
||
|
from.<br>
|
||
|
To solve this, it is expected that creation of a participant with a different
|
||
|
configuration will force a failure for now.
|
||
|
Until [Multiple Configurations](#multiple-configurations) is implemented.
|
||
|
|
||
|
After the ddsc API has been extended, we can decide on what to do with the
|
||
|
configuration through XML.
|
||
|
- Keep it. It seems usable: no need to change applications when changing (or
|
||
|
adding) Security settings. However, conflicts between XML and QoS
|
||
|
configuration could cause problems. Simplest seems to be to only allow QoS
|
||
|
security configuration when it's not configured in XML already.
|
||
|
- Remove it. No conflict resolving needed.
|
||
|
|
||
|
All the Security tests depend on providing (different) configurations through
|
||
|
XML. Depending on if we keep or remove the XML configuration option, a lot of
|
||
|
tests have to be updated, or a few added (that test security configuration
|
||
|
through QoS).
|
||
|
|
||
|
For the loading of the plugin libraries, properties with specific names have to
|
||
|
be added to the property policy to know the location and names of the plugins.
|
||
|
As inspiration, fastrtps can be used:
|
||
|
https://github.com/ros2/rmw_fastrtps/blob/master/rmw_fastrtps_shared_cpp/src/rmw_node.cpp#L296
|
||
|
|
||
|
|
||
|
## Failure handling<a name="failures" />
|
||
|
|
||
|
Currently, when an local action is tried that isn't allowed by DDS Security
|
||
|
(like creating a participant when it's not permitted), DDSI is shut down.<br>
|
||
|
Mainly because in OpenSplice it's quite hard to get a failure state from DDSI
|
||
|
to the application.
|
||
|
|
||
|
In Cyclone, however, ddsc::dds_create_participant() results in a direct call to
|
||
|
ddsi::new_participant(). This means that if creation of an entity (or
|
||
|
participant in this example) fails due to security issues in ddsi, we can fail
|
||
|
the actual ddsc API call with a proper error result (there's already the
|
||
|
DDS_RETCODE_NOT_ALLOWED_BY_SECURITY in the ddsc API (not used)).
|
||
|
|
||
|
Maybe we have to do some additional cleanup when a failure is encountered.
|
||
|
|
||
|
Some tests probably have to be adjusted for the new behaviour.
|
||
|
|
||
|
|
||
|
## Multiple configurations<a name="multiple-configurations" />
|
||
|
|
||
|
Currently (because it's done through the overall XML configuration), only one
|
||
|
DDS Security configuration could be supported. Because of this fact, at various
|
||
|
locations, shortcuts could be made in both DDSI and plugins.<br>
|
||
|
However, because the configuration is coming from participants now (see
|
||
|
[Move Configuration](#Move-configuration), we should be able to support
|
||
|
multiple different DDS Security configurations.
|
||
|
|
||
|
Until now, the creation of a second participant with a different configuration
|
||
|
would force a failure (again, see [Move Configuration](#Move-configuration)).
|
||
|
|
||
|
It is expected that the plugin loading still happens through the configuration
|
||
|
XML (see [Move Configuration](#Move-configuration)). This means that DDSI doesn't have to support
|
||
|
multiple sets of plugins. Just the one set, provided at initialization. This
|
||
|
means that DDSI shouldn't have to be changed to support this.
|
||
|
|
||
|
So, it's the plugins need to be able to support multiple configurations.
|
||
|
|
||
|
The Cryptography plugin doesn't seem to care about global DDS Security
|
||
|
configurations. It has basically configurations per participant/topic/
|
||
|
endpoints, which already works. So, this plugin doesn't have to be upgraded.
|
||
|
|
||
|
The Authentication plugin does have global DDS Security configurations. Main
|
||
|
function related to that is validate_local_identity(). This function already
|
||
|
creates a new local identity every time it is called. So, this plugin doesn't
|
||
|
have to be upgraded either.
|
||
|
|
||
|
That leaves the Access Control plugin.<br>
|
||
|
The main function related to configuration is validate_local_permissions().
|
||
|
This function creates access rights depending on Permissions and Governance
|
||
|
files. Currently, there's only one local 'rights' structure that is linked
|
||
|
directly to the plugin (see also the ACCESS_CONTROL_USE_ONE_PERMISSION compile
|
||
|
switch).<br>
|
||
|
This has to change.<br>
|
||
|
The local rights structure needs to be coupled to a participant. This also
|
||
|
means that we have to search for it instead of having direct access when
|
||
|
entering the plugin.<br>
|
||
|
The remote rights can be used as example. That is basically a list of rights/
|
||
|
permissions with the remote identity handle as key.
|
||
|
|
||
|
Tests have to be added to make sure that a setup with different Security
|
||
|
configurations works.
|
||
|
|
||
|
|
||
|
## Example<a name="example" />
|
||
|
|
||
|
A Security example has to be added.
|
||
|
|
||
|
|
||
|
## QosProvider<a name="qosprovider" />
|
||
|
|
||
|
The Participant QoS now contains Security related information. This means that
|
||
|
the QosProvider has to be upgraded to support that.
|
||
|
|
||
|
|
||
|
## Data Tags (optional)<a name="datatags" />
|
||
|
|
||
|
The specification is somewhat fuzzy about the data tags.
|
||
|
|
||
|
The following is a summary (still elaborate) of how it seems to work:
|
||
|
|
||
|
The permissions document can contain the <data_tags> tag on publish and
|
||
|
subscribe level. It's related to the data samples, but don't have to be related
|
||
|
to the keys of those samples.<br>
|
||
|
The QoS of a writer/reader can also have data tags by means of the
|
||
|
DataTagQosPolicy. A writer/reader can only be created when the data_tags in the
|
||
|
QoS matches those in the permissions document. This check should happen on both
|
||
|
the local and remote level.
|
||
|
|
||
|
This creation check is the only thing that DDS Security actually does with the
|
||
|
data tags. They are only authenticated by DDS Security, but not interpreted
|
||
|
further.<br>
|
||
|
This is only a minor security addition, because the publisher can still publish
|
||
|
data that doesn't match the data tags because DDS doesn't interpret the data
|
||
|
nor compares it with the data tags.
|
||
|
|
||
|
What it can be used for is a kind of custom access control scheme on the
|
||
|
application level.<br>
|
||
|
An application that consumes data can see if a publisher is allowed to publish
|
||
|
that sample by comparing the data within the sample with the data tag(s)
|
||
|
associated with that publisher. As said, this comparison is not done on the DDS
|
||
|
level, but has to be done within the application itself.
|
||
|
|
||
|
That leaves the question, how does the application get the tags associated with
|
||
|
the related writer?<br>
|
||
|
In other words; the application gets a sample. It has to know from which writer
|
||
|
it originated and it has to have access to the data tag(s) of that writer.
|
||
|
The dds_sample_info_t contains the dds_instance_handle_t publication_handle,
|
||
|
which is unique to the writer (locally).<br>
|
||
|
That dds_instance_handle_t can be used to get the right
|
||
|
DDS_Security_PublicationBuiltinTopicDataSecure sample for the related secure
|
||
|
builtin reader [**note1**].<br>
|
||
|
DDS_Security_PublicationBuiltinTopicDataSecure contains QoS information
|
||
|
regarding that writer, including the DataTagQosPolicy. The remote
|
||
|
DDS_Security_PublicationBuiltinTopicDataSecure contents have been authenticated
|
||
|
by DDS Security and the data tags can be trusted.<br>
|
||
|
The application can check the sample data against the data_tags within that
|
||
|
QoS.
|
||
|
|
||
|
Things to do:
|
||
|
- Add DataTagQosPolicy to the ddsc API and the related QoSses.
|
||
|
- Add DDS_Security_PublicationBuiltinTopicDataSecure data type to the ddsc API
|
||
|
(better yet, all secure builtin types).
|
||
|
- Add the related builtin reader to the ddsc99 API (better yet, all secure
|
||
|
builtin readers).
|
||
|
- Add test regarding the secure builtin readers and data.
|
||
|
- Add data tag comparisons between QoS and permission documents during local
|
||
|
and remote entity creation.
|
||
|
- Add data tag remote/local (mis)match tests.
|
||
|
|
||
|
Especially because of the lack of access to builtin secure readers, supporting
|
||
|
data tags doesn't seem feasible in the near future. Also, it's optional in the
|
||
|
specification.
|
||
|
|
||
|
**note1**
|
||
|
That DDS_Security_PublicationBuiltinTopicDataSecure reader is not yet available
|
||
|
within the ddsc API, nor is the related data type. Don't know how much work it
|
||
|
would be to add them to that API.
|