Skip to content

Commit

Permalink
Add a common function for security files. (#51)
Browse files Browse the repository at this point in the history
* Add a common function for security files.

All three of the in-tree DDS RMW implementation hard-code a
list of files in the security enclave.  Instead of doing this
in three separate places, add in a common function that finds
the set of files in the security enclave that are needed for
DDS security.

get_security_files() currently returns them in an unordered_map.
The main reason to use a map over a structure is ABI concerns;
it should be easy to add another field to the unordered_map,
whereas adding one to a structure would be harder.  (I intend
to add something to the map in the near future).  It's also
not performance sensitive, so the extra string allocations
shouldn't hurt performance.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
  • Loading branch information
clalancette authored Jun 28, 2021
1 parent eeb04c3 commit c67739d
Show file tree
Hide file tree
Showing 4 changed files with 246 additions and 0 deletions.
6 changes: 6 additions & 0 deletions rmw_dds_common/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ add_library(${PROJECT_NAME}_library SHARED
src/gid_utils.cpp
src/graph_cache.cpp
src/qos.cpp
src/security.cpp
src/time_utils.cpp)

set_target_properties(${PROJECT_NAME}_library
Expand Down Expand Up @@ -104,6 +105,11 @@ if(BUILD_TESTING)
target_link_libraries(test_time_utils ${PROJECT_NAME}_library)
endif()

ament_add_gmock(test_security test/test_security.cpp)
if(TARGET test_security)
target_link_libraries(test_security ${PROJECT_NAME}_library)
endif()

add_performance_test(benchmark_graph_cache test/benchmark/benchmark_graph_cache.cpp)
if(TARGET benchmark_graph_cache)
target_link_libraries(benchmark_graph_cache ${PROJECT_NAME}_library)
Expand Down
57 changes: 57 additions & 0 deletions rmw_dds_common/include/rmw_dds_common/security.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// 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_DDS_COMMON__SECURITY_HPP_
#define RMW_DDS_COMMON__SECURITY_HPP_

#include <string>
#include <unordered_map>

#include "rmw_dds_common/visibility_control.h"

namespace rmw_dds_common
{

/// Get the set of security files in a security enclave.
/**
* This function will look through the passed in 'secure root'
* for a set of required filenames that must be in the enclave.
* If any of the required filenames are missing, the 'result'
* will be empty and the function will return false.
* If all of the required filenames are present, then this function
* will fill in the 'result' map with a key-value pair of
* friendy name -> filename. If the prefix is not empty, then
* the prefix will be applied to the filename.
*
* The friendly names that this function will currently fill in are:
* IDENTITY_CA
* CERTIFICATE
* PRIVATE_KEY
* PERMISSIONS_CA
* GOVERNANCE
* PERMISSIONS
*
* \param[in] prefix An optional prefix to apply to the filenames when storing them.
* \param[in] secure_root The path to the security enclave to look at.
* \param[out] result The map where the friendly name -> filename pairs are stored.
* \return `true` if all required files exist in the security enclave, `false` otherwise.
*/
RMW_DDS_COMMON_PUBLIC
bool get_security_files(
const std::string & prefix, const std::string & secure_root,
std::unordered_map<std::string, std::string> & result);

} // namespace rmw_dds_common

#endif // RMW_DDS_COMMON__SECURITY_HPP_
52 changes: 52 additions & 0 deletions rmw_dds_common/src/security.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// 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 <string>
#include <utility>
#include <unordered_map>

#include "rcpputils/filesystem_helper.hpp"
#include "rmw_dds_common/security.hpp"

namespace rmw_dds_common
{

bool get_security_files(
const std::string & prefix, const std::string & secure_root,
std::unordered_map<std::string, std::string> & result)
{
const std::unordered_map<std::string, std::string> required_files{
{"IDENTITY_CA", "identity_ca.cert.pem"},
{"CERTIFICATE", "cert.pem"},
{"PRIVATE_KEY", "key.pem"},
{"PERMISSIONS_CA", "permissions_ca.cert.pem"},
{"GOVERNANCE", "governance.p7s"},
{"PERMISSIONS", "permissions.p7s"},
};

for (const std::pair<std::string, std::string> & el : required_files) {
rcpputils::fs::path full_path(secure_root);
full_path /= el.second;
if (!full_path.is_regular_file()) {
result.clear();
return false;
}

result[el.first] = prefix + full_path.string();
}

return true;
}

} // namespace rmw_dds_common
131 changes: 131 additions & 0 deletions rmw_dds_common/test/test_security.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright 2021 Open Source Robotics Foundation, Inc.
//
// 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 <gtest/gtest.h>

#include <rcpputils/filesystem_helper.hpp>
#include <rmw_dds_common/security.hpp>

#include <array>
#include <fstream>
#include <string>
#include <unordered_map>

TEST(test_security, files_exist_no_prefix)
{
rcpputils::fs::path dir = rcpputils::fs::path("./test_folder");
rcpputils::fs::remove_all(dir);
EXPECT_TRUE(rcpputils::fs::create_directories(dir));
EXPECT_TRUE(rcpputils::fs::exists(dir));
EXPECT_TRUE(rcpputils::fs::is_directory(dir));

std::array<std::string, 6> required_files = {
"identity_ca.cert.pem", "cert.pem", "key.pem",
"permissions_ca.cert.pem", "governance.p7s", "permissions.p7s"
};
for (const std::string & filename : required_files) {
rcpputils::fs::path full_path = dir / filename;
std::ofstream output_buffer{full_path.string()};
output_buffer << "test";
ASSERT_TRUE(rcpputils::fs::exists(full_path));
}

std::unordered_map<std::string, std::string> security_files;
ASSERT_TRUE(rmw_dds_common::get_security_files("", dir.string(), security_files));

EXPECT_EQ(
security_files["IDENTITY_CA"],
rcpputils::fs::path("./test_folder/identity_ca.cert.pem").string());
EXPECT_EQ(
security_files["CERTIFICATE"],
rcpputils::fs::path("./test_folder/cert.pem").string());
EXPECT_EQ(
security_files["PRIVATE_KEY"],
rcpputils::fs::path("./test_folder/key.pem").string());
EXPECT_EQ(
security_files["PERMISSIONS_CA"],
rcpputils::fs::path("./test_folder/permissions_ca.cert.pem").string());
EXPECT_EQ(
security_files["GOVERNANCE"],
rcpputils::fs::path("./test_folder/governance.p7s").string());
EXPECT_EQ(
security_files["PERMISSIONS"],
rcpputils::fs::path("./test_folder/permissions.p7s").string());
}

TEST(test_security, files_exist_with_prefix)
{
rcpputils::fs::path dir = rcpputils::fs::path("./test_folder");
rcpputils::fs::remove_all(dir);
EXPECT_TRUE(rcpputils::fs::create_directories(dir));
EXPECT_TRUE(rcpputils::fs::exists(dir));
EXPECT_TRUE(rcpputils::fs::is_directory(dir));

std::array<std::string, 6> required_files = {
"identity_ca.cert.pem", "cert.pem", "key.pem",
"permissions_ca.cert.pem", "governance.p7s", "permissions.p7s"
};
for (const std::string & filename : required_files) {
rcpputils::fs::path full_path = dir / filename;
std::ofstream output_buffer{full_path.string()};
output_buffer << "test";
ASSERT_TRUE(rcpputils::fs::exists(full_path));
}

std::unordered_map<std::string, std::string> security_files;
ASSERT_TRUE(rmw_dds_common::get_security_files("file://", dir.string(), security_files));

EXPECT_EQ(
security_files["IDENTITY_CA"],
"file://" + rcpputils::fs::path("./test_folder/identity_ca.cert.pem").string());
EXPECT_EQ(
security_files["CERTIFICATE"],
"file://" + rcpputils::fs::path("./test_folder/cert.pem").string());
EXPECT_EQ(
security_files["PRIVATE_KEY"],
"file://" + rcpputils::fs::path("./test_folder/key.pem").string());
EXPECT_EQ(
security_files["PERMISSIONS_CA"],
"file://" + rcpputils::fs::path("./test_folder/permissions_ca.cert.pem").string());
EXPECT_EQ(
security_files["GOVERNANCE"],
"file://" + rcpputils::fs::path("./test_folder/governance.p7s").string());
EXPECT_EQ(
security_files["PERMISSIONS"],
"file://" + rcpputils::fs::path("./test_folder/permissions.p7s").string());
}

TEST(test_security, file_missing)
{
rcpputils::fs::path dir = rcpputils::fs::path("./test_folder");
rcpputils::fs::remove_all(dir);
EXPECT_TRUE(rcpputils::fs::create_directories(dir));
EXPECT_TRUE(rcpputils::fs::exists(dir));
EXPECT_TRUE(rcpputils::fs::is_directory(dir));

std::array<std::string, 5> required_files = {
"identity_ca.cert.pem", "cert.pem", "key.pem",
"permissions_ca.cert.pem", "governance.p7s"
};
for (const std::string & filename : required_files) {
rcpputils::fs::path full_path = dir / filename;
std::ofstream output_buffer{full_path.string()};
output_buffer << "test";
ASSERT_TRUE(rcpputils::fs::exists(full_path));
}

std::unordered_map<std::string, std::string> security_files;
ASSERT_FALSE(rmw_dds_common::get_security_files("", dir.string(), security_files));
ASSERT_EQ(security_files.size(), 0UL);
}

0 comments on commit c67739d

Please sign in to comment.