diff --git a/zenoh_security_configuration_tools/CMakeLists.txt b/zenoh_security_configuration_tools/CMakeLists.txt new file mode 100644 index 000000000..31288d61f --- /dev/null +++ b/zenoh_security_configuration_tools/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.8) +project(zenoh_security_configuration_tools) + +# Default to C++17 +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# find dependencies +find_package(ament_cmake REQUIRED) +find_package(nlohmann_json REQUIRED) +find_package(tinyxml2_vendor REQUIRED) +find_package(TinyXML2 REQUIRED) +find_package(CLI11 REQUIRED) +find_package(zenoh_cpp_vendor REQUIRED) + +add_executable(zenoh_security_configuration_tools + src/zenoh_security_configuration_tools.cpp + src/policy_parser.cpp +) +target_link_libraries(zenoh_security_configuration_tools PRIVATE + CLI11::CLI11 + nlohmann_json::nlohmann_json + tinyxml2::tinyxml2 + zenohcxx::zenohc +) +target_include_directories(${PROJECT_NAME} PUBLIC + "$" + "$") + if(WIN32) +target_compile_definitions(${PROJECT_NAME} + PRIVATE "ZENOH_SECURITY_CONFIGURATION_TOOLS_BUILDING_LIBRARY") +endif() + +if(BUILD_TESTING) + find_package(ament_lint_auto REQUIRED) + ament_lint_auto_find_test_dependencies() +endif() + + +install( + TARGETS zenoh_security_configuration_tools + DESTINATION lib/${PROJECT_NAME} +) + +ament_package() diff --git a/zenoh_security_configuration_tools/include/zenoh_security_configuration_tools/policy_parser.hpp b/zenoh_security_configuration_tools/include/zenoh_security_configuration_tools/policy_parser.hpp new file mode 100644 index 000000000..a4dfb2bcc --- /dev/null +++ b/zenoh_security_configuration_tools/include/zenoh_security_configuration_tools/policy_parser.hpp @@ -0,0 +1,95 @@ +// Copyright (c) 2025, Open Source Robotics Foundation, Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef ZENOH_SECURITY_CONFIGURATION_TOOLS__POLICY_PARSER_HPP_ +#define ZENOH_SECURITY_CONFIGURATION_TOOLS__POLICY_PARSER_HPP_ + +#include + +#include +#include +#include + +#include + +#include "zenoh_security_configuration_tools/visibility_control.hpp" + +namespace zenoh +{ +/** + * This class parses the ROS 2 secutiry policy files into json5 Zenoh Config files + **/ +class PolicyParser +{ +public: + /// The library is loaded in the constructor. + /** + * \param[in] filename The policy string path. + * \throws std::runtime_error if there are some invalid arguments or the library + * was not load properly + */ + ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC + PolicyParser( + const std::string & filename, + const std::string & configfile, + uint16_t domain_id); + + void parse(); + +private: + void parse_enclaves(const tinyxml2::XMLElement * root); + void parse_profiles(const tinyxml2::XMLElement * root); + void parse_services(const tinyxml2::XMLElement * root, const std::string & node_name); + void parse_topics(const tinyxml2::XMLElement * root, const std::string & node_name); + void clear(); + void fill_data( + zenoh::Config & config, + const std::string & node_name); + + std::string check_name( + const std::string & name, + const std::string & node_name); + + tinyxml2::XMLDocument doc_; + std::string configfile_path_; + + std::set services_reply_allow_; + std::set services_reply_deny_; + std::set services_request_allow_; + std::set services_request_deny_; + + std::set topics_sub_allow_; + std::set topics_pub_allow_; + std::set topics_sub_deny_; + std::set topics_pub_deny_; + + uint16_t domain_id_; +}; +} // namespace zenoh + +#endif // ZENOH_SECURITY_CONFIGURATION_TOOLS__POLICY_PARSER_HPP_ diff --git a/zenoh_security_configuration_tools/include/zenoh_security_configuration_tools/visibility_control.hpp b/zenoh_security_configuration_tools/include/zenoh_security_configuration_tools/visibility_control.hpp new file mode 100644 index 000000000..11ebeb874 --- /dev/null +++ b/zenoh_security_configuration_tools/include/zenoh_security_configuration_tools/visibility_control.hpp @@ -0,0 +1,87 @@ +// Copyright (c) 2025, Open Source Robotics Foundation, Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#ifndef ZENOH_SECURITY_CONFIGURATION_TOOLS__VISIBILITY_CONTROL_HPP_ +#define ZENOH_SECURITY_CONFIGURATION_TOOLS__VISIBILITY_CONTROL_HPP_ + +/*! \file visibility_control.hpp + * \brief Macros for controlling visibilty of exported iterfaces. + * + * This logic was borrowed (then namespaced) from the examples on the gcc wiki: + * https://gcc.gnu.org/wiki/Visibility + */ +/** + * \def ZENOH_SECURITY_CONFIGURATION_TOOLS_EXPORT + * \brief Exposes the function with its decorated name in the compiled library object. + */ +/** + * \def ZENOH_SECURITY_CONFIGURATION_TOOLS_IMPORT + * \brief On Windows declares a function will be imported from a dll, otherwise it is empty + */ +/** + * \def ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC + * \brief Declares symbols and functions will be visible for export. + */ +/** + * \def ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC_TYPE + * \brief On Windows, this is a replica of ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC, otherwise it is empty. + */ +/** + * \def ZENOH_SECURITY_CONFIGURATION_TOOLS_LOCAL + * \brief Declares symbols cannot be exported from the dll. + */ + +#if defined _WIN32 || defined __CYGWIN__ + #ifdef __GNUC__ + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_EXPORT __attribute__ ((dllexport)) + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_IMPORT __attribute__ ((dllimport)) + #else + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_EXPORT __declspec(dllexport) + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_IMPORT __declspec(dllimport) + #endif + #ifdef ZENOH_SECURITY_CONFIGURATION_TOOLS_BUILDING_LIBRARY + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC ZENOH_SECURITY_CONFIGURATION_TOOLS_EXPORT + #else + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC ZENOH_SECURITY_CONFIGURATION_TOOLS_IMPORT + #endif + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC_TYPE ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_LOCAL +#else + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_EXPORT __attribute__ ((visibility("default"))) + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_IMPORT + #if __GNUC__ >= 4 + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC __attribute__ ((visibility("default"))) + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_LOCAL __attribute__ ((visibility("hidden"))) + #else + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_LOCAL + #endif + #define ZENOH_SECURITY_CONFIGURATION_TOOLS_PUBLIC_TYPE +#endif + +#endif // ZENOH_SECURITY_CONFIGURATION_TOOLS__VISIBILITY_CONTROL_HPP_ diff --git a/zenoh_security_configuration_tools/package.xml b/zenoh_security_configuration_tools/package.xml new file mode 100644 index 000000000..658ee17b4 --- /dev/null +++ b/zenoh_security_configuration_tools/package.xml @@ -0,0 +1,23 @@ + + + + zenoh_security_configuration_tools + 0.3.0 + This package generates zenoh secutiry configurations + Alejandro Hernanadez + Apache License 2.0 + + + nlohmann-json-dev + + cli11 + tinyxml2_vendor + zenoh_cpp_vendor + + ament_lint_auto + ament_lint_common + + + ament_cmake + + diff --git a/zenoh_security_configuration_tools/src/policy_parser.cpp b/zenoh_security_configuration_tools/src/policy_parser.cpp new file mode 100644 index 000000000..b1a12c18f --- /dev/null +++ b/zenoh_security_configuration_tools/src/policy_parser.cpp @@ -0,0 +1,458 @@ +// Copyright (c) 2025, Open Source Robotics Foundation, Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include "zenoh_security_configuration_tools/policy_parser.hpp" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +static const char * root_str = "policy"; +static const char * enclaves_str = "enclaves"; +static const char * enclave_str = "enclave"; +static const char * profiles_str = "profiles"; +static const char * profile_str = "profile"; +static const char * services_str = "services"; +static const char * service_str = "service"; +static const char * topics_str = "topics"; +static const char * topic_str = "topic"; + +using json = nlohmann::json; + +namespace zenoh +{ +PolicyParser::PolicyParser( + const std::string & filename, + const std::string & configfile, + uint16_t domain_id) +:configfile_path_(configfile), domain_id_(domain_id) +{ + const tinyxml2::XMLError error = doc_.LoadFile(filename.c_str()); + if (error != tinyxml2::XML_SUCCESS) { + throw std::runtime_error("Invalid argument: wrong policy file."); + } +} + +bool replace( + std::string & str, + const std::string & from, + const std::string & to) +{ + size_t start_pos = str.find(from); + if(start_pos == std::string::npos) { + return false; + } + str.replace(start_pos, from.length(), to); + return true; +} + +std::string PolicyParser::check_name( + const std::string & name, + const std::string & node_name) +{ + std::string result = name; + replace(result, "~", node_name); + if (result[0] == '/') { + result = result.substr(1); + } + return result; +} + +void PolicyParser::parse_services( + const tinyxml2::XMLElement * root, + const std::string & node_name) +{ + const tinyxml2::XMLElement * services_node = root->FirstChildElement(); + do{ + if (services_node != nullptr) { + if (strcmp(services_node->Name(), services_str) == 0) { + std::string service_type; + const char * permission_s = services_node->Attribute("reply"); + if (permission_s != nullptr) { + service_type = "reply"; + } else { + permission_s = services_node->Attribute("request"); + if (permission_s != nullptr) { + service_type = "request"; + } + } + + if (permission_s == nullptr) { + throw std::runtime_error("Not able to get permission from service " + + std::to_string(services_node->GetLineNum())); + } + std::string permission = permission_s; + + const tinyxml2::XMLElement * service_node = services_node->FirstChildElement(); + do { + if (service_node != nullptr) { + if (strcmp(service_node->Name(), service_str) == 0) { + if (service_type == "reply") { + if (permission == "ALLOW") { + services_reply_allow_.insert(check_name(service_node->GetText(), node_name)); + } else if (permission == "DENY") { + services_reply_deny_.insert(check_name(service_node->GetText(), node_name)); + } + } else if (service_type == "request") { + if (permission == "ALLOW") { + services_request_allow_.insert(check_name(service_node->GetText(), node_name)); + } else if (permission == "DENY") { + services_request_deny_.insert(check_name(service_node->GetText(), node_name)); + } + } + } else { + throw std::runtime_error("Invalid file"); + } + } + } while ((service_node = service_node->NextSiblingElement()) != nullptr); + } + } else { + throw std::runtime_error("Invalid file"); + } + } while ((services_node = services_node->NextSiblingElement()) != nullptr); +} + +void PolicyParser::clear() +{ + services_reply_allow_.clear(); + services_reply_deny_.clear(); + services_request_allow_.clear(); + services_request_deny_.clear(); + topics_sub_allow_.clear(); + topics_pub_allow_.clear(); + topics_sub_deny_.clear(); + topics_pub_deny_.clear(); +} + +json to_key_exprs( + const std::set & key_exprs, + uint16_t domain_id) +{ + json key_exprs_ret = json::array(); + + for (const auto & name : key_exprs) { + key_exprs_ret.push_back(std::to_string(domain_id) + "/" + name + "/**"); + } + + return key_exprs_ret; +} + +void PolicyParser::fill_data( + zenoh::Config & config, + const std::string & node_name) +{ + json rules = json::array(); + json policies_rules = json::array(); + + if (!services_reply_allow_.empty()) { + json rule_allow_reply = json::object({ + {"id", "incoming_queries"}, + {"messages", json::array({"query"})}, + {"flows", json::array({"ingress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(services_reply_allow_, domain_id_)}, + }); + rules.push_back(rule_allow_reply); + policies_rules.push_back("incoming_queries"); + + json rule_outgoing_reply = json::object({ + {"id", "outgoing_queryables_replies"}, + {"messages", json::array({"declare_queryable", "reply"})}, + {"flows", json::array({"egress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(services_reply_allow_, domain_id_)}, + }); + rules.push_back(rule_outgoing_reply); + policies_rules.push_back("outgoing_queryables_replies"); + } + + if (!services_request_allow_.empty()) { + json rule_allow_request_out = json::object({ + {"id", "outgoing_queries"}, + {"messages", json::array({"query"})}, + {"flows", json::array({"egress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(services_request_allow_, domain_id_)}, + }); + rules.push_back(rule_allow_request_out); + policies_rules.push_back("outgoing_queries"); + + json rule_allow_request_in = json::object({ + {"id", "incoming_queryables_replies"}, + {"messages", json::array({"declare_queryable", "reply"})}, + {"flows", json::array({"ingress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(services_request_allow_, domain_id_)}, + }); + rules.push_back(rule_allow_request_in); + policies_rules.push_back("incoming_queryables_replies"); + } + + if (!topics_pub_allow_.empty()) { + json rule_allow_pub_out = json::object({ + {"id", "outgoing_publications"}, + {"messages", json::array({"put"})}, + {"flows", json::array({"egress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(topics_pub_allow_, domain_id_)}, + }); + rules.push_back(rule_allow_pub_out); + policies_rules.push_back("outgoing_publications"); + + json rule_allow_pub_in = json::object({ + {"id", "incoming_subscriptions"}, + {"messages", json::array({"declare_subscriber"})}, + {"flows", json::array({"ingress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(topics_pub_allow_, domain_id_)}, + }); + rules.push_back(rule_allow_pub_in); + policies_rules.push_back("incoming_subscriptions"); + } + + if (!topics_sub_allow_.empty()) { + json rule_allow_sub_out = json::object({ + {"id", "outgoing_subscriptions"}, + {"messages", json::array({"declare_subscriber"})}, + {"flows", json::array({"egress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(topics_sub_allow_, domain_id_)}, + }); + rules.push_back(rule_allow_sub_out); + policies_rules.push_back("outgoing_subscriptions"); + + json rule_allow_sub_in = json::object({ + {"id", "incoming_publications"}, + {"messages", json::array({"put"})}, + {"flows", json::array({"ingress"})}, + {"permission", "allow"}, + {"key_exprs", to_key_exprs(topics_sub_allow_, domain_id_)}, + }); + rules.push_back(rule_allow_sub_in); + policies_rules.push_back("incoming_publications"); + } + + json liveliness_messages = json::array({ + "liveliness_token", "liveliness_query", "declare_liveliness_subscriber"}); + if (!services_reply_allow_.empty() || !services_request_allow_.empty()) { + liveliness_messages.push_back("reply"); + } + + json rule_liveliness = json::object({ + {"id", "liveliness_tokens"}, + {"messages", liveliness_messages}, + {"flows", json::array({"ingress", "egress"})}, + {"permission", "allow"}, + {"key_exprs", + json::array({"@ros2_lv/" + std::to_string(domain_id_) + "/**"})}, + }); + rules.push_back(rule_liveliness); + policies_rules.push_back("liveliness_tokens"); + + json policies = json::array(); + policies.push_back(json::object({ + {"rules", json::array({"liveliness_tokens"})}, + {"subjects", json::array({"router"})}, + })); + policies.push_back(json::object({ + {"rules", policies_rules}, + {"subjects", json::array({node_name})}, + })); + + json subjects = json::array({ + json::object({{"id", "router"}}), + json::object({{"id", node_name}}), + }); + + config.insert_json5("access_control/rules", rules.dump()); + config.insert_json5("access_control/policies", policies.dump()); + config.insert_json5("access_control/subjects", subjects.dump()); +} + +void PolicyParser::parse_topics( + const tinyxml2::XMLElement * root, + const std::string & node_name) +{ + const tinyxml2::XMLElement * topics_node = root->FirstChildElement(); + do{ + if (topics_node != nullptr) { + if (strcmp(topics_node->Name(), topics_str) == 0) { + std::string topic_type; + const char * permission_s = topics_node->Attribute("subscribe"); + if (permission_s != nullptr) { + topic_type = "subscribe"; + } else { + permission_s = topics_node->Attribute("publish"); + if (permission_s != nullptr) { + topic_type = "publish"; + } + } + + if (permission_s == nullptr) { + throw std::runtime_error("Not able to get permission from service " + + std::to_string(topics_node->GetLineNum())); + } + std::string permission = permission_s; + + const tinyxml2::XMLElement * topic_node = topics_node->FirstChildElement(); + do { + if (topic_node != nullptr) { + if (strcmp(topic_node->Name(), topic_str) == 0) { + if (topic_type == "publish") { + if (permission == "ALLOW") { + topics_pub_allow_.insert(check_name(topic_node->GetText(), node_name)); + } else if (permission == "DENY") { + topics_pub_allow_.insert(check_name(topic_node->GetText(), node_name)); + } + } else if (topic_type == "subscribe") { + if (permission == "ALLOW") { + topics_sub_allow_.insert(check_name(topic_node->GetText(), node_name)); + } else if (permission == "DENY") { + topics_sub_deny_.insert(check_name(topic_node->GetText(), node_name)); + } + } + + } else { + throw std::runtime_error("Invalid file"); + } + } + } while ((topic_node = topic_node->NextSiblingElement()) != nullptr); + } + } else { + throw std::runtime_error("Invalid file"); + } + } while ((topics_node = topics_node->NextSiblingElement()) != nullptr); +} + +void PolicyParser::parse_profiles(const tinyxml2::XMLElement * root) +{ + const tinyxml2::XMLElement * profiles_node = root->FirstChildElement(); + do{ + if (profiles_node != nullptr) { + if (strcmp(profiles_node->Name(), profiles_str) == 0) { + const tinyxml2::XMLElement * profile_node = profiles_node->FirstChildElement(); + do { + if (profile_node != nullptr) { + if (strcmp(profile_node->Name(), profile_str) == 0) { + const char * node_name = profile_node->Attribute("node"); + if (node_name == nullptr) { + std::string error_msg = "Attribute name is required in " + + std::string(profile_str) + " tag. Line " + + std::to_string(profiles_node->GetLineNum()); + throw std::runtime_error(error_msg); + } + + zenoh::Config config = zenoh::Config::create_default(); + if (!configfile_path_.empty()) { + // Initialize the zenoh configuration. + zenoh::ZResult result; + config = zenoh::Config::from_file(configfile_path_, &result); + if (result != Z_OK) { + std::string error_msg = "Invalid configuration file " + configfile_path_; + throw std::runtime_error("Error getting Zenoh config file."); + } + } + config.insert_json5("access_control/enabled", "true"); + config.insert_json5("access_control/default_permission", "'deny'"); + + parse_services(profile_node, node_name); + parse_topics(profile_node, node_name); + + this->fill_data(config, node_name); + + std::string filename = std::string(node_name) + ".json5"; + std::ofstream new_config_file(filename); + json j_config = json::parse(config.to_string()); + new_config_file << j_config.dump(4); + std::cout << "New file create called " << filename << std::endl; + new_config_file.close(); + + this->clear(); + } + } else { + throw std::runtime_error("Invalid file"); + } + } while ((profile_node = profile_node->NextSiblingElement()) != nullptr); + } else { + std::string error_msg = "Invalid file: Malformed Zenoh policy root. Line: " + + std::to_string(profiles_node->GetLineNum()); + throw std::runtime_error(error_msg); + } + } else { + throw std::runtime_error("Invalid file"); + } + } while ((profiles_node = profiles_node->NextSiblingElement()) != nullptr); +} + +void PolicyParser::parse_enclaves(const tinyxml2::XMLElement * root) +{ + const tinyxml2::XMLElement * enclaves_node = root->FirstChildElement(); + if (enclaves_node != nullptr) { + if (strcmp(enclaves_node->Name(), enclaves_str) == 0) { + const tinyxml2::XMLElement * enclave_node = enclaves_node->FirstChildElement(); + if (enclave_node != nullptr) { + if (strcmp(enclave_node->Name(), enclave_str) == 0) { + parse_profiles(enclave_node); + } + } else { + throw std::runtime_error("Invalid file"); + } + } else { + std::string error_msg = "Invalid file: Malformed Zenoh policy root. Line: " + + std::to_string(enclaves_node->GetLineNum()); + throw std::runtime_error(error_msg); + } + } else { + throw std::runtime_error("Invalid file"); + } +} + +void PolicyParser::parse() +{ + const tinyxml2::XMLElement * root = doc_.RootElement(); + if (root != nullptr) { + if (strcmp(root->Name(), root_str) == 0) { + parse_enclaves(root); + } else { + std::string error_msg = "Invalid file: Malformed Zenoh policy root. Line: " + + std::to_string(root->GetLineNum()); + throw std::runtime_error(error_msg); + } + } else { + throw std::runtime_error("Invalid file"); + } +} + +} // namespace zenoh diff --git a/zenoh_security_configuration_tools/src/zenoh_security_configuration_tools.cpp b/zenoh_security_configuration_tools/src/zenoh_security_configuration_tools.cpp new file mode 100644 index 000000000..964c99753 --- /dev/null +++ b/zenoh_security_configuration_tools/src/zenoh_security_configuration_tools.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2019, Open Source Robotics Foundation, Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include + +#include "zenoh_security_configuration_tools/policy_parser.hpp" + +int main(int argc, char * argv[]) +{ + CLI::App app{"Zenoh Security Configuration Tools.\n"}; + + std::string policy_filename; + std::string zenoh_config_filename; + uint16_t domain_id = 0; + app.add_option("-f,--policy", policy_filename, "Policy file name")->required(); + app.add_option("-c,--config", zenoh_config_filename, "Zenoh config file name"); + app.add_option("-d,--domainid", domain_id, "Domain ID"); + + try { + app.parse(argc, argv); + } catch (const CLI::ParseError & e) { + return app.exit(e); + } + + auto policy_parser = zenoh::PolicyParser( + policy_filename, + zenoh_config_filename, + domain_id); + policy_parser.parse(); + return 0; +}