Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add pkcs11 support to get_security_files #66

Merged
merged 6 commits into from Apr 8, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
32 changes: 32 additions & 0 deletions rmw_dds_common/include/rmw_dds_common/security.hpp
Expand Up @@ -52,6 +52,38 @@ bool get_security_files(
const std::string & prefix, const std::string & secure_root,
std::unordered_map<std::string, std::string> & result);

/// 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] supports_pkcs11 Whether the RMW has support for PKCS#11 URIs.
* \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(
bool supports_pkcs11,
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_
112 changes: 99 additions & 13 deletions rmw_dds_common/src/security.cpp
Expand Up @@ -13,41 +13,127 @@
// limitations under the License.

#include <filesystem>
#include <fstream>
#include <functional>
#include <string>
#include <utility>
#include <unordered_map>
#include <vector>

#include "rmw_dds_common/security.hpp"

namespace rmw_dds_common
{

// Processor for security attributes with FILE URI
static bool process_file_uri_security_file(
bool /*supports_pkcs11*/,
const std::string & prefix,
const std::filesystem::path & full_path,
std::string & result)
{
if (!std::filesystem::is_regular_file(full_path)) {
return false;
}
result = prefix + full_path.generic_string();
return true;
}

// Processor for security attributes with PKCS#11 URI
static bool process_pkcs_uri_security_file(
bool supports_pkcs11,
const std::string & /*prefix*/,
const std::filesystem::path & full_path,
std::string & result)
{
if (!supports_pkcs11) {
return false;
}

const std::string p11_prefix("pkcs11:");

std::ifstream ifs(full_path);
if (!ifs.is_open()) {
return false;
}

if (!(ifs >> result)) {
return false;
}
if (result.find(p11_prefix) != 0) {
return false;
}

return true;
}

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"},
return get_security_files(false, prefix, secure_root, result);
}

bool get_security_files(
bool supports_pkcs11,
const std::string & prefix,
const std::string & secure_root,
std::unordered_map<std::string, std::string> & result)
{
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
using std::placeholders::_4;
using security_file_processor =
std::function<bool (bool, const std::string &, const std::filesystem::path &, std::string &)>;
using processor_vector =
std::vector<std::pair<std::string, security_file_processor>>;

// Key: the security attribute
// Value: ordered sequence of pairs. Each pair contains one possible file name
// for the attribute and the corresponding processor method
// Pairs are ordered by priority: the first one matching is used.
const std::unordered_map<std::string, processor_vector> required_files{
{"IDENTITY_CA", {
{"identity_ca.cert.p11", std::bind(process_pkcs_uri_security_file, _1, _2, _3, _4)},
{"identity_ca.cert.pem", std::bind(process_file_uri_security_file, _1, _2, _3, _4)}}},
{"CERTIFICATE", {
{"cert.p11", std::bind(process_pkcs_uri_security_file, _1, _2, _3, _4)},
{"cert.pem", std::bind(process_file_uri_security_file, _1, _2, _3, _4)}}},
{"PRIVATE_KEY", {
{"key.p11", std::bind(process_pkcs_uri_security_file, _1, _2, _3, _4)},
{"key.pem", std::bind(process_file_uri_security_file, _1, _2, _3, _4)}}},
{"PERMISSIONS_CA", {
{"permissions_ca.cert.p11", std::bind(process_pkcs_uri_security_file, _1, _2, _3, _4)},
{"permissions_ca.cert.pem", std::bind(process_file_uri_security_file, _1, _2, _3, _4)}}},
{"GOVERNANCE", {
{"governance.p7s", std::bind(process_file_uri_security_file, _1, _2, _3, _4)}}},
{"PERMISSIONS", {
{"permissions.p7s", std::bind(process_file_uri_security_file, _1, _2, _3, _4)}}},
};

const std::unordered_map<std::string, std::string> optional_files{
{"CRL", "crl.pem"},
};

for (const std::pair<const std::string, std::string> & el : required_files) {
std::filesystem::path full_path(secure_root);
full_path /= el.second;
if (!std::filesystem::is_regular_file(full_path)) {
for (const std::pair<const std::string,
clalancette marked this conversation as resolved.
Show resolved Hide resolved
std::vector<std::pair<std::string, security_file_processor>>> & el : required_files)
{
std::string attribute_value;
bool processed = false;
for (auto & proc : el.second) {
std::filesystem::path full_path(secure_root);
full_path /= proc.first;
if (proc.second(supports_pkcs11, prefix, full_path, attribute_value)) {
processed = true;
break;
}
}
if (!processed) {
result.clear();
return false;
}

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

for (const std::pair<const std::string, std::string> & el : optional_files) {
Expand Down