Skip to content

Commit

Permalink
Add basic support for security logging plugin
Browse files Browse the repository at this point in the history
Support setting the security logging properties in Fast RTPS by exposing
three environment variables:

- `ROS_SECURITY_LOG_FILE`: Log security events to the provided file path
- `ROS_SECURITY_LOG_PUBLISH`: Publish security events to DDS topic
- `ROS_SECURITY_LOG_VERBOSITY`: Control verbosity of logged events

Signed-off-by: Kyle Fazzari <kyle@canonical.com>
  • Loading branch information
kyrofa committed Apr 21, 2020
1 parent 45709b7 commit 7aa9721
Show file tree
Hide file tree
Showing 6 changed files with 530 additions and 0 deletions.
1 change: 1 addition & 0 deletions rmw_fastrtps_shared_cpp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ add_library(rmw_fastrtps_shared_cpp
src/rmw_publisher.cpp
src/rmw_request.cpp
src/rmw_response.cpp
src/rmw_security_logging.cpp
src/rmw_service.cpp
src/rmw_service_names_and_types.cpp
src/rmw_service_server_is_available.cpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Copyright 2020 Canonical Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef RMW_FASTRTPS_SHARED_CPP__RMW_SECURITY_LOGGING_HPP_
#define RMW_FASTRTPS_SHARED_CPP__RMW_SECURITY_LOGGING_HPP_

#include "fastrtps/rtps/attributes/PropertyPolicy.h"

/// Apply any requested security logging configuration to the policy.
/**
* \param policy policy to which security logging properties may be added.
* \returns false if the requested configuration could not be applied (rmw error will be set).
* \returns true if the requested configuration was applied (or no configuration was requested).
*/
bool apply_security_logging_configuration(eprosima::fastrtps::rtps::PropertyPolicy & policy);

#endif // RMW_FASTRTPS_SHARED_CPP__RMW_SECURITY_LOGGING_HPP_
6 changes: 6 additions & 0 deletions rmw_fastrtps_shared_cpp/src/participant.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "rmw_fastrtps_shared_cpp/custom_participant_info.hpp"
#include "rmw_fastrtps_shared_cpp/participant.hpp"
#include "rmw_fastrtps_shared_cpp/rmw_common.hpp"
#include "rmw_fastrtps_shared_cpp/rmw_security_logging.hpp"

using Domain = eprosima::fastrtps::Domain;
using IPLocator = eprosima::fastrtps::rtps::IPLocator;
Expand Down Expand Up @@ -232,6 +233,11 @@ rmw_fastrtps_shared_cpp::create_participant(
Property(
"dds.sec.access.builtin.Access-Permissions.permissions", security_files_paths[5]));

// Configure security logging
if (!apply_security_logging_configuration(property_policy)) {
return nullptr;
}

participantAttrs.rtps.properties = property_policy;
} else if (security_options->enforce_security) {
RMW_SET_ERROR_MSG("couldn't find all security files!");
Expand Down
217 changes: 217 additions & 0 deletions rmw_fastrtps_shared_cpp/src/rmw_security_logging.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// Copyright 2020 Canonical Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <map>
#include <string>
#include <sstream>
#include <utility>

#include "fastrtps/config.h"
#include "rcutils/filesystem.h"
#include "rcutils/get_env.h"
#include "rmw/error_handling.h"
#include "rmw/qos_profiles.h"
#include "rmw/types.h"

#include "rmw_fastrtps_shared_cpp/rmw_security_logging.hpp"

#if HAVE_SECURITY

namespace
{
// Environment variable names
// TODO(security-wg): These are intended to be temporary, and need to be refactored into a proper
// abstraction.
const char log_file_variable_name[] = "ROS_SECURITY_LOG_FILE";
const char log_publish_variable_name[] = "ROS_SECURITY_LOG_PUBLISH";
const char log_verbosity_variable_name[] = "ROS_SECURITY_LOG_VERBOSITY";

// Logging properties
const char logging_plugin_property_name[] = "dds.sec.log.plugin";
const char log_file_property_name[] = "dds.sec.log.builtin.DDS_LogTopic.log_file";
const char verbosity_property_name[] = "dds.sec.log.builtin.DDS_LogTopic.logging_level";
const char distribute_enable_property_name[] =
"dds.sec.log.builtin.DDS_LogTopic.distribute";

// Fast RTPS supports the following verbosities:
// - EMERGENCY_LEVEL
// - ALERT_LEVEL
// - CRITICAL_LEVEL
// - ERROR_LEVEL
// - WARNING_LEVEL
// - NOTICE_LEVEL
// - INFORMATIONAL_LEVEL
// - DEBUG_LEVEL
//
// ROS has less logging levels, but it makes sense to use them here for consistency, so we have
// the following mapping.
const std::map<RCUTILS_LOG_SEVERITY, std::string> verbosity_mapping {
{RCUTILS_LOG_SEVERITY_FATAL, "EMERGENCY_LEVEL"},
{RCUTILS_LOG_SEVERITY_ERROR, "ERROR_LEVEL"},
{RCUTILS_LOG_SEVERITY_WARN, "WARNING_LEVEL"},
{RCUTILS_LOG_SEVERITY_INFO, "INFORMATIONAL_LEVEL"},
{RCUTILS_LOG_SEVERITY_DEBUG, "DEBUG_LEVEL"},
};

void severity_names_str(std::string & str)
{
std::stringstream stream;
auto penultimate = --verbosity_mapping.crend();
for (auto it = verbosity_mapping.crbegin(); it != penultimate; ++it) {
stream << g_rcutils_log_severity_names[it->first] << ", ";
}

stream << "or " << g_rcutils_log_severity_names[penultimate->first];
str = stream.str();
}

bool string_to_verbosity(const std::string & str, std::string & verbosity)
{
int ros_severity;
if (rcutils_logging_severity_level_from_string(
str.c_str(),
rcutils_get_default_allocator(), &ros_severity) == RCUTILS_RET_OK)
{
try {
verbosity = verbosity_mapping.at(static_cast<RCUTILS_LOG_SEVERITY>(ros_severity));
return true;
} catch (std::out_of_range &) {
// Fall to the return below
}
}

return false;
}

bool validate_boolean(const std::string & str)
{
return str == "true" || str == "false";
}

void add_property(
eprosima::fastrtps::rtps::PropertySeq & properties,
eprosima::fastrtps::rtps::Property && property)
{
// Add property to vector. If property already exists, overwrite it.
std::string property_name = property.name();
for (auto & existing_property : properties) {
if (existing_property.name() == property_name) {
existing_property = property;
return;
}
}

properties.push_back(property);
}

bool get_env(const std::string & variable_name, std::string & variable_value)
{
const char * value;
const char * error_message = rcutils_get_env(variable_name.c_str(), &value);
if (error_message != NULL) {
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"unable to get %s environment variable: %s",
variable_name.c_str(),
error_message);
return false;
}

variable_value = std::string(value);

return true;
}
} // namespace

#endif

bool apply_security_logging_configuration(eprosima::fastrtps::rtps::PropertyPolicy & policy)
{
#if HAVE_SECURITY
eprosima::fastrtps::rtps::PropertySeq properties;
std::string env_value;

// Handle logging to file
if (!get_env(log_file_variable_name, env_value)) {
return false;
}
if (!env_value.empty()) {
add_property(
properties,
eprosima::fastrtps::rtps::Property(
log_file_property_name, env_value.c_str()));
}

// Handle log distribution over DDS
if (!get_env(log_publish_variable_name, env_value)) {
return false;
}
if (!env_value.empty()) {
if (!validate_boolean(env_value)) {
RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"%s is not valid: '%s' is not a supported value (use 'true' or 'false')",
log_publish_variable_name,
env_value.c_str());
return false;
}

add_property(
properties,
eprosima::fastrtps::rtps::Property(
distribute_enable_property_name, env_value.c_str()));
}

// Handle log verbosity
if (!get_env(log_verbosity_variable_name, env_value)) {
return false;
}
if (!env_value.empty()) {
std::string verbosity;
if (!string_to_verbosity(env_value, verbosity)) {
std::string humanized_severity_list;
severity_names_str(humanized_severity_list);

RMW_SET_ERROR_MSG_WITH_FORMAT_STRING(
"%s is not valid: %s is not a supported verbosity (use %s)",
log_verbosity_variable_name,
env_value.c_str(),
humanized_severity_list.c_str());
return false;
}

add_property(
properties,
eprosima::fastrtps::rtps::Property(verbosity_property_name, verbosity.c_str()));
}

if (!properties.empty()) {
add_property(
properties,
eprosima::fastrtps::rtps::Property(
logging_plugin_property_name,
"builtin.DDS_LogTopic"));
}

// Now that we're done parsing, actually update the properties
for (auto & item : properties) {
add_property(policy.properties(), std::move(item));
}

return true;
#else
RMW_SET_ERROR_MSG(
"This Fast-RTPS version doesn't have the security libraries\n"
"Please compile Fast-RTPS using the -DSECURITY=ON CMake option");
return false;
#endif
}
6 changes: 6 additions & 0 deletions rmw_fastrtps_shared_cpp/test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,9 @@ if(TARGET test_dds_attributes_to_rmw_qos)
ament_target_dependencies(test_dds_attributes_to_rmw_qos)
target_link_libraries(test_dds_attributes_to_rmw_qos ${PROJECT_NAME})
endif()

ament_add_gmock(test_security_logging test_security_logging.cpp)
if(TARGET test_security_logging)
ament_target_dependencies(test_security_logging)
target_link_libraries(test_security_logging ${PROJECT_NAME})
endif()
Loading

0 comments on commit 7aa9721

Please sign in to comment.