diff options
Diffstat (limited to 'implementation/security/src/policy_manager_impl.cpp')
-rw-r--r-- | implementation/security/src/policy_manager_impl.cpp | 1446 |
1 files changed, 1429 insertions, 17 deletions
diff --git a/implementation/security/src/policy_manager_impl.cpp b/implementation/security/src/policy_manager_impl.cpp index a816714..849a36b 100644 --- a/implementation/security/src/policy_manager_impl.cpp +++ b/implementation/security/src/policy_manager_impl.cpp @@ -1,15 +1,1281 @@ -// Copyright (C) 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +// Copyright (C) 2019-2021 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. -#include "../include/policy.hpp" +#ifdef _WIN32 + #define WIN32_LEAN_AND_MEAN + #define NOMINMAX + #include <windows.h> + #include <stdlib.h> +#endif + +#include <algorithm> +#include <sstream> + #include "../include/policy_manager_impl.hpp" -#include "../include/security_impl.hpp" +#include "../../configuration/include/configuration_element.hpp" +#ifdef ANDROID +#include "../../configuration/include/internal_android.hpp" +#else +#include "../../configuration/include/internal.hpp" +#endif // ANDROID +#include "../../utility/include/utility.hpp" namespace vsomeip_v3 { -policy_manager_impl::~policy_manager_impl() { +template<typename T_> +void read_data(const std::string &_in, T_ &_out) { + std::stringstream its_converter; + + if (_in.size() > 2 + && _in[0] == '0' + && (_in[1] == 'x' || _in[1] == 'X')) + its_converter << std::hex << _in; + else + its_converter << std::dec << _in; + + its_converter >> _out; +} + +policy_manager_impl::policy_manager_impl() + : +#ifndef VSOMEIP_DISABLE_SECURITY + policy_enabled_(false), + check_credentials_(false), + allow_remote_clients_(true), + check_whitelist_(false), + policy_base_path_(""), +#endif // !VSOMEIP_DISABLE_SECURITY + is_configured_(false), + check_routing_credentials_(false) +{ +} + +bool +policy_manager_impl::is_enabled() const { +#ifdef VSOMEIP_DISABLE_SECURITY + return (false); +#else + return (policy_enabled_); +#endif +} + +bool +policy_manager_impl::is_audit() const { +#ifdef VSOMEIP_DISABLE_SECURITY + return (false); +#else + return (!check_credentials_); +#endif +} + +bool +policy_manager_impl::check_credentials(client_t _client, + const vsomeip_sec_client_t *_sec_client) { + +#ifdef VSOMEIP_DISABLE_SECURITY + (void)_client; + (void)_sec_client; + + return (true); +#else + if (!policy_enabled_) + return (true); + + if (!_sec_client) + return (true); + + if (_sec_client->client_type != VSOMEIP_CLIENT_UDS) + return (true); + + uid_t its_uid(_sec_client->client.uds_client.user); + gid_t its_gid(_sec_client->client.uds_client.group); + + bool has_id(false); + + boost::shared_lock<boost::shared_mutex> its_lock(any_client_policies_mutex_); + for (const auto &p : any_client_policies_) { + + std::lock_guard<std::mutex> its_policy_lock(p->mutex_); + + bool has_uid, has_gid(false); + + const auto found_uid = p->credentials_.find(its_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(its_gid); + has_gid = (found_gid != found_uid->second.end()); + } + + has_id = (has_uid && has_gid); + + if ((has_id && p->allow_who_) || (!has_id && !p->allow_who_)) { + if (!store_client_to_sec_client_mapping(_client, _sec_client)) { + std::string security_mode_text = "!"; + if (!check_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " with UID/GID=" << std::dec << its_uid << "/" << its_gid + << " : Check credentials failed as existing credentials would be overwritten" + << security_mode_text; + return !check_credentials_; + } + store_sec_client_to_client_mapping(_sec_client, _client); + return true; + } + } + + std::string security_mode_text = " ~> Skip!"; + if (!check_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + VSOMEIP_INFO << "vSomeIP Security: Client 0x" << std::hex << _client + << " with UID/GID=" << std::dec << its_uid << "/" << its_gid + << " : Check credentials failed" << security_mode_text; + + return !check_credentials_; +#endif // VSOMEIP_DISABLE_SECURITY +} + +bool +policy_manager_impl::check_routing_credentials( + const vsomeip_sec_client_t *_sec_client) const { + +#ifdef VSOMEIP_DISABLE_SECURITY + (void)_sec_client; + + return (true); +#else + uid_t its_uid(0); + gid_t its_gid(0); + bool is_known_uid_gid(false); + + std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); + if (_sec_client && _sec_client->client_type == VSOMEIP_CLIENT_UDS) { + its_uid = _sec_client->client.uds_client.user; + its_gid = _sec_client->client.uds_client.group; + + if (routing_credentials_.first == its_uid + && routing_credentials_.second == its_gid) { + + return (true); + } + + is_known_uid_gid = true; + } + + std::string security_mode_text = "!"; + if (!check_routing_credentials_) { + + security_mode_text = " but will be allowed due to audit mode is active!"; + } + + VSOMEIP_INFO << "vSomeIP Security: UID/GID=" + << (is_known_uid_gid ? std::to_string(its_uid) : "n/a") + << "." + << (is_known_uid_gid ? std::to_string(its_gid) : "n/a") + << " : Check routing credentials failed as " + << "configured routing manager credentials " + << "do not match with routing manager credentials" + << security_mode_text; + + return (!check_routing_credentials_); +#endif // VSOMEIP_DISABLE_SECURITY +} + +void +policy_manager_impl::set_routing_credentials(uint32_t _uid, uint32_t _gid, + const std::string &_name) { + + if (is_configured_) { + VSOMEIP_WARNING << "vSomeIP Security: Multiple definitions of routing-credentials." + << " Ignoring definition from " << _name; + } else { + routing_credentials_ = std::make_pair(_uid, _gid); + is_configured_ = true; + } +} + +bool +policy_manager_impl::is_client_allowed(const vsomeip_sec_client_t *_sec_client, + service_t _service, instance_t _instance, method_t _method, + bool _is_request_service) const { + +#ifdef VSOMEIP_DISABLE_SECURITY + (void)_sec_client; + (void)_service; + (void)_instance; + (void)_method; + (void)_is_request_service; + + return (true); +#else + if (!policy_enabled_) { + return true; + } + + uid_t its_uid(ANY_UID); + gid_t its_gid(ANY_GID); + if (_sec_client) { + if (_sec_client->client_type == VSOMEIP_CLIENT_UDS) { + its_uid = _sec_client->client.uds_client.user; + its_gid = _sec_client->client.uds_client.group; + } else { + return true; + } + } else { + std::string security_mode_text = " ~> Skip!"; + if (!check_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + VSOMEIP_INFO << "vSomeIP Security: uid/gid " + << std::dec << its_uid << "/" << its_gid << " is not valid." + << "Therefore it isn't allowed to communicate to service/instance " + << _service << "/" << _instance + << security_mode_text; + + return (!check_credentials_); + } + + // Check cache + auto its_credentials = std::make_pair(its_uid, its_gid); + auto its_key = std::make_tuple(_service, _instance, _method); + { + boost::shared_lock<boost::shared_mutex> its_cache_lock(is_client_allowed_cache_mutex_); + const auto its_iter = is_client_allowed_cache_.find(its_credentials); + if (its_iter != is_client_allowed_cache_.end()) { + if (its_iter->second.find(its_key) != its_iter->second.end()) { + return (true); + } + } + } + + + // Check policies + boost::shared_lock<boost::shared_mutex> its_lock(any_client_policies_mutex_); + for (const auto &p : any_client_policies_) { + std::lock_guard<std::mutex> its_policy_lock(p->mutex_); + bool has_uid, has_gid(false); + bool is_matching(false); + + const auto found_uid = p->credentials_.find(its_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(its_gid); + has_gid = (found_gid != found_uid->second.end()); + } + + const auto found_service = p->requests_.find(_service); + if (found_service != p->requests_.end()) { + const auto found_instance = found_service->second.find(_instance); + if (found_instance != found_service->second.end()) { + if (!_is_request_service) { + const auto found_method = found_instance->second.find(_method); + is_matching = (found_method != found_instance->second.end()); + } else { + // handle VSOMEIP_REQUEST_SERVICE + is_matching = true; + } + } + } + + if ((has_uid && has_gid && p->allow_who_) || ((!has_uid || !has_gid) && !p->allow_who_)) { + if (p->allow_what_) { + // allow policy + if (is_matching) { + boost::unique_lock<boost::shared_mutex> its_cache_lock(is_client_allowed_cache_mutex_); + is_client_allowed_cache_[its_credentials].insert(its_key); + return (true); + } + } else { + // deny policy + // allow client if the service / instance / !ANY_METHOD was not found + if ((!is_matching && (_method != ANY_METHOD)) + // allow client if the service / instance / ANY_METHOD was not found + // and it is a "deny nothing" policy + || (!is_matching && (_method == ANY_METHOD) && p->requests_.empty())) { + boost::unique_lock<boost::shared_mutex> its_cache_lock(is_client_allowed_cache_mutex_); + is_client_allowed_cache_[its_credentials].insert(its_key); + return (true); + } + } + } + } + + std::string security_mode_text = " ~> Skip!"; + if (!check_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + + VSOMEIP_INFO << "vSomeIP Security: UID/GID=" + << std::dec << its_uid << "/" << its_gid + << " : Isn't allowed to communicate with service/instance/(method / event) " + << std::hex << _service << "/" << _instance << "/" << _method + << security_mode_text; + + return (!check_credentials_); +#endif // VSOMEIP_DISABLE_SECURITY +} + +bool +policy_manager_impl::is_offer_allowed(const vsomeip_sec_client_t *_sec_client, + service_t _service, instance_t _instance) const { + +#ifdef VSOMEIP_DISABLE_SECURITY + (void)_sec_client; + (void)_service; + (void)_instance; + + return (true); +#else + if (!policy_enabled_) + return true; + + uint32_t its_uid(ANY_UID), its_gid(ANY_GID); + if (_sec_client) { + if (_sec_client->client_type == VSOMEIP_CLIENT_UDS) { + its_uid = _sec_client->client.uds_client.user; + its_gid = _sec_client->client.uds_client.group; + } else { + return true; + } + } else { + std::string security_mode_text = " ~> Skip offer!"; + if (!check_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + VSOMEIP_INFO << "vSomeIP Security: uid/gid " + << std::dec << its_uid << "/" << its_gid << " is not valid." + << "Therefore it isn't allowed to offer service/instance " + << _service << "/" << _instance + << security_mode_text; + + return (!check_credentials_); + } + + boost::shared_lock<boost::shared_mutex> its_lock(any_client_policies_mutex_); + for (const auto &p : any_client_policies_) { + std::lock_guard<std::mutex> its_policy_lock(p->mutex_); + bool has_uid, has_gid(false), has_offer(false); + + const auto found_uid = p->credentials_.find(its_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(its_gid); + has_gid = (found_gid != found_uid->second.end()); + } + + const auto found_service = p->offers_.find(_service); + if (found_service != p->offers_.end()) { + const auto found_instance = found_service->second.find(_instance); + has_offer = (found_instance != found_service->second.end()); + } + + if ((has_uid && has_gid && p->allow_who_) + || ((!has_uid || !has_gid) && !p->allow_who_)) { + if (p->allow_what_ == has_offer) { + return (true); + } + } + } + + std::string security_mode_text = " ~> Skip offer!"; + if (!check_credentials_) { + security_mode_text = " but will be allowed due to audit mode is active!"; + } + + VSOMEIP_INFO << "vSomeIP Security: UID/GID=" + << std::dec << its_uid << "/" << its_gid + << " isn't allowed to offer service/instance " + << std::hex << _service << "/" << _instance + << security_mode_text; + + return (!check_credentials_); +#endif // VSOMEIP_DISABLE_SECURITY +} + +#ifndef VSOMEIP_DISABLE_SECURITY +void +policy_manager_impl::load(const configuration_element &_element, const bool _lazy_load) { + + load_policies(_element); + if (!_lazy_load) { + + load_security_update_whitelist(_element); + load_security_policy_extensions(_element); + load_routing_credentials(_element); + + if (policy_enabled_ && check_credentials_) + VSOMEIP_INFO << "Security configuration is active."; + + if (policy_enabled_ && !check_credentials_) + VSOMEIP_INFO << "Security configuration is active but in audit mode (allow all)"; + } +} + +bool +policy_manager_impl::remove_security_policy(uint32_t _uid, uint32_t _gid) { + boost::unique_lock<boost::shared_mutex> its_lock(any_client_policies_mutex_); + bool was_removed(false); + if (!any_client_policies_.empty()) { + std::vector<std::shared_ptr<policy>>::iterator p_it = any_client_policies_.begin(); + while (p_it != any_client_policies_.end()) { + bool is_matching(false); + { + std::lock_guard<std::mutex> its_policy_lock((*p_it)->mutex_); + bool has_uid(false), has_gid(false); + const auto found_uid = (*p_it)->credentials_.find(_uid); + has_uid = (found_uid != (*p_it)->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(_gid); + has_gid = (found_gid != found_uid->second.end()); + } + + // only remove "credentials allow" policies to prevent removal of + // blacklist configured in file + if (has_uid && has_gid && (*p_it)->allow_who_) { + is_matching = true; + } + } + if (is_matching) { + was_removed = true; + p_it = any_client_policies_.erase(p_it); + } else { + ++p_it; + } + + boost::unique_lock<boost::shared_mutex> its_cache_lock(is_client_allowed_cache_mutex_); + is_client_allowed_cache_.erase(std::make_pair(_uid, _gid)); + } + } + return (was_removed); +} + +void +policy_manager_impl::update_security_policy(uint32_t _uid, uint32_t _gid, + const std::shared_ptr<policy> &_policy) { + + boost::unique_lock<boost::shared_mutex> its_lock(any_client_policies_mutex_); + std::shared_ptr<policy> its_matching_policy; + for (auto p : any_client_policies_) { + std::lock_guard<std::mutex> its_guard(p->mutex_); + if (p->credentials_.size() == 1) { + const auto its_uids = *(p->credentials_.begin()); + if (its_uids.first.lower() == _uid + && its_uids.first.upper() == _uid) { + if (its_uids.second.size() == 1) { + const auto its_gids = *(its_uids.second.begin()); + if (its_gids.lower() == _gid + && its_gids.upper() == _gid) { + if (p->allow_who_ == _policy->allow_who_) { + its_matching_policy = p; + break; + } + } + } + } + } + } + + if (its_matching_policy) { + std::lock_guard<std::mutex> its_guard{its_matching_policy->mutex_}; + for (const auto &r : _policy->requests_) { + service_t its_lower, its_upper; + get_bounds(r.first, its_lower, its_upper); + for (auto s = its_lower; s <= its_upper; s++) { + boost::icl::discrete_interval<service_t> its_service(s, s, + boost::icl::interval_bounds::closed()); + its_matching_policy->requests_ += std::make_pair(its_service, r.second); + } + } + for (const auto &o : _policy->offers_) { + service_t its_lower, its_upper; + get_bounds(o.first, its_lower, its_upper); + for (auto s = its_lower; s <= its_upper; s++) { + boost::icl::discrete_interval<service_t> its_service(s, s, + boost::icl::interval_bounds::closed()); + its_matching_policy->offers_ += std::make_pair(its_service, o.second); + } + } + } else { + any_client_policies_.push_back(_policy); + } + + boost::unique_lock<boost::shared_mutex> its_cache_lock(is_client_allowed_cache_mutex_); + is_client_allowed_cache_.erase(std::make_pair(_uid, _gid)); +} + +void +policy_manager_impl::add_security_credentials(uint32_t _uid, uint32_t _gid, + const std::shared_ptr<policy> &_policy, client_t _client) { + + bool was_found(false); + boost::unique_lock<boost::shared_mutex> its_lock(any_client_policies_mutex_); + for (const auto &p : any_client_policies_) { + bool has_uid(false), has_gid(false); + + std::lock_guard<std::mutex> its_policy_lock(p->mutex_); + const auto found_uid = p->credentials_.find(_uid); + has_uid = (found_uid != p->credentials_.end()); + if (has_uid) { + const auto found_gid = found_uid->second.find(_gid); + has_gid = (found_gid != found_uid->second.end()); + } + + if (has_uid && has_gid && p->allow_who_) { + was_found = true; + break; + } + } + + // Do not add the new (credentials-only-policy) if a allow + // credentials policy with same credentials was found + if (!was_found) { + any_client_policies_.push_back(_policy); + VSOMEIP_INFO << __func__ << " Added security credentials at client: 0x" + << std::hex << _client << std::dec << " with UID: " << _uid << " GID: " << _gid; + } +} + +bool +policy_manager_impl::is_policy_update_allowed(uint32_t _uid, std::shared_ptr<policy> &_policy) const { + + bool is_uid_allowed(false); + { + std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); + const auto found_uid = uid_whitelist_.find(_uid); + is_uid_allowed = (found_uid != uid_whitelist_.end()); + } + + if (is_uid_allowed && _policy) { + std::lock_guard<std::mutex> its_lock(service_interface_whitelist_mutex_); + std::lock_guard<std::mutex> its_policy_lock(_policy->mutex_); + for (const auto &its_request : _policy->requests_) { + bool has_service(false); + + service_t its_service(0); + for (its_service = its_request.first.lower(); + its_service <= its_request.first.upper(); + its_service++) { + + const auto found_service = service_interface_whitelist_.find(its_service); + has_service = (found_service != service_interface_whitelist_.end()); + if (!has_service) + break; + } + + if (!has_service) { + if (!check_whitelist_) { + VSOMEIP_INFO << "vSomeIP Security: Policy update requesting service ID: " + << std::hex << its_service + << " is not allowed, but will be allowed due to whitelist audit mode is active!"; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Policy update requesting service ID: " + << std::hex << its_service + << " is not allowed! -> ignore update"; + } + return (!check_whitelist_); + } + } + return (true); + } else { + if (!check_whitelist_) { + VSOMEIP_INFO << "vSomeIP Security: Policy update for UID: " << std::dec << _uid + << " is not allowed, but will be allowed due to whitelist audit mode is active!"; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Policy update for UID: " << std::dec << _uid + << " is not allowed! -> ignore update"; + } + return (!check_whitelist_); + } +} + +bool +policy_manager_impl::is_policy_removal_allowed(uint32_t _uid) const { + std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); + for (auto its_uid_range : uid_whitelist_) { + if (its_uid_range.lower() <= _uid && _uid <= its_uid_range.upper()) { + return (true); + } + } + + if (!check_whitelist_) { + VSOMEIP_INFO << "vSomeIP Security: Policy removal for UID: " + << std::dec << _uid + << " is not allowed, but will be allowed due to whitelist audit mode is active!"; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Policy removal for UID: " + << std::dec << _uid + << " is not allowed! -> ignore removal"; + } + return (!check_whitelist_); +} + +bool +policy_manager_impl::parse_policy(const byte_t* &_buffer, uint32_t &_buffer_size, + uint32_t &_uid, uint32_t &_gid, const std::shared_ptr<policy> &_policy) const { + + bool is_valid = _policy->deserialize(_buffer, _buffer_size); + if (is_valid) + is_valid = _policy->get_uid_gid(_uid, _gid); + return is_valid; +} + +/////////////////////////////////////////////////////////////////////////////// +// Configuration +/////////////////////////////////////////////////////////////////////////////// +void +policy_manager_impl::load_policies(const configuration_element &_element) { +#ifdef _WIN32 + return; +#endif + try { + auto optional = _element.tree_.get_child_optional("security"); + if (!optional) { + return; + } + policy_enabled_ = true; + auto found_policy = _element.tree_.get_child("security"); + for (auto its_security = found_policy.begin(); + its_security != found_policy.end(); ++its_security) { + if (its_security->first == "check_credentials") { + if (its_security->second.data() == "true") { + check_credentials_ = true; + } else { + check_credentials_ = false; + } + } else if (its_security->first == "allow_remote_clients") { + if (its_security->second.data() == "true") { + allow_remote_clients_ = true; + } else { + allow_remote_clients_ = false; + } + } else if (its_security->first == "policies") { + for (auto its_policy = its_security->second.begin(); + its_policy != its_security->second.end(); ++its_policy) { + load_policy(its_policy->second); + } + } + } + } catch (...) { + } +} + +void +policy_manager_impl::load_policy(const boost::property_tree::ptree &_tree) { + + std::shared_ptr<policy> policy(std::make_shared<policy>()); + bool allow_deny_set(false); + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + if (i->first == "credentials") { + boost::icl::interval_set<uid_t> its_uid_interval_set; + boost::icl::interval_set<gid_t> its_gid_interval_set; + boost::icl::discrete_interval<uid_t> its_uid_interval; + boost::icl::discrete_interval<gid_t> its_gid_interval; + + bool has_uid(false), has_gid(false); + bool has_uid_range(false), has_gid_range(false); + for (auto n = i->second.begin(); + n != i->second.end(); ++n) { + std::string its_key(n->first); + std::string its_value(n->second.data()); + if (its_key == "uid") { + if(n->second.data().empty()) { + load_interval_set(n->second, its_uid_interval_set); + has_uid_range = true; + } else { + if (its_value != "any") { + uint32_t its_uid; + read_data(its_value, its_uid); + its_uid_interval = boost::icl::construct< + boost::icl::discrete_interval<uid_t> >( + its_uid, its_uid, + boost::icl::interval_bounds::closed()); + } else { + its_uid_interval = boost::icl::construct< + boost::icl::discrete_interval<uid_t> >( + std::numeric_limits<uid_t>::min(), + std::numeric_limits<uid_t>::max(), + boost::icl::interval_bounds::closed()); + } + has_uid = true; + } + } else if (its_key == "gid") { + if(n->second.data().empty()) { + load_interval_set(n->second, its_gid_interval_set); + has_gid_range = true; + } else { + if (its_value != "any") { + uint32_t its_gid; + read_data(its_value, its_gid); + its_gid_interval = boost::icl::construct< + boost::icl::discrete_interval<gid_t> >( + its_gid, its_gid, + boost::icl::interval_bounds::closed()); + } else { + its_gid_interval = boost::icl::construct< + boost::icl::discrete_interval<gid_t> >( + std::numeric_limits<gid_t>::min(), + std::numeric_limits<gid_t>::max(), + boost::icl::interval_bounds::closed()); + } + has_gid = true; + } + } else if (its_key == "allow" || its_key == "deny") { + policy->allow_who_ = (its_key == "allow"); + load_credential(n->second, policy->credentials_); + } + } + + if (has_uid && has_gid) { + its_gid_interval_set.insert(its_gid_interval); + + policy->credentials_ += std::make_pair(its_uid_interval, its_gid_interval_set); + policy->allow_who_ = true; + } + if (has_uid_range && has_gid_range) { + for (const auto u : its_uid_interval_set) + policy->credentials_ += std::make_pair(u, its_gid_interval_set); + policy->allow_who_ = true; + } + } else if (i->first == "allow") { + if (allow_deny_set) { + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: \"allow\" tag overrides " + << "already set \"deny\" tag. " + << "Either \"deny\" or \"allow\" is allowed."; + } + allow_deny_set = true; + policy->allow_what_ = true; + load_policy_body(policy, i); + } else if (i->first == "deny") { + if (allow_deny_set) { + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: \"deny\" tag overrides " + << "already set \"allow\" tag. " + << "Either \"deny\" or \"allow\" is allowed."; + } + allow_deny_set = true; + policy->allow_what_ = false; + load_policy_body(policy, i); + } + } + boost::unique_lock<boost::shared_mutex> its_lock(any_client_policies_mutex_); + any_client_policies_.push_back(policy); +} + +void +policy_manager_impl::load_policy_body(std::shared_ptr<policy> &_policy, + const boost::property_tree::ptree::const_iterator &_tree) { + + for (auto l = _tree->second.begin(); l != _tree->second.end(); ++l) { + if (l->first == "requests") { + for (auto n = l->second.begin(); n != l->second.end(); ++n) { + service_t its_service = 0x0; + instance_t its_instance = 0x0; + boost::icl::interval_map<instance_t, + boost::icl::interval_set<method_t> > its_instance_method_intervals; + for (auto k = n->second.begin(); k != n->second.end(); ++k) { + if (k->first == "service") { + read_data(k->second.data(), its_service); + } else if (k->first == "instance") { // legacy definition for instances + boost::icl::interval_set<instance_t> its_instance_interval_set; + boost::icl::interval_set<method_t> its_method_interval_set; + boost::icl::discrete_interval<instance_t> all_instances(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + boost::icl::discrete_interval<method_t> all_methods(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + + std::string its_value(k->second.data()); + if (its_value != "any") { + read_data(its_value, its_instance); + if (its_instance != 0x0) { + its_instance_interval_set.insert(its_instance); + its_method_interval_set.insert(all_methods); + } + } else { + its_instance_interval_set.insert(all_instances); + its_method_interval_set.insert(all_methods); + } + for (const auto i : its_instance_interval_set) { + its_instance_method_intervals + += std::make_pair(i, its_method_interval_set); + } + } else if (k->first == "instances") { // new instances definition + for (auto p = k->second.begin(); p != k->second.end(); ++p) { + boost::icl::interval_set<instance_t> its_instance_interval_set; + boost::icl::interval_set<method_t> its_method_interval_set; + boost::icl::discrete_interval<method_t> all_methods(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + for (auto m = p->second.begin(); m != p->second.end(); ++m) { + if (m->first == "ids") { + load_interval_set(m->second, its_instance_interval_set); + } else if (m->first == "methods") { + load_interval_set(m->second, its_method_interval_set); + } + } + if (its_method_interval_set.empty()) + its_method_interval_set.insert(all_methods); + for (const auto i : its_instance_interval_set) { + its_instance_method_intervals + += std::make_pair(i, its_method_interval_set); + } + } + + if (its_instance_method_intervals.empty()) { + boost::icl::interval_set<instance_t> its_legacy_instance_interval_set; + boost::icl::interval_set<method_t> its_legacy_method_interval_set; + boost::icl::discrete_interval<method_t> all_methods(0x01, 0xFFFF, + boost::icl::interval_bounds::closed()); + its_legacy_method_interval_set.insert(all_methods); + + // try to only load instance ranges with any method to be allowed + load_interval_set(k->second, its_legacy_instance_interval_set); + for (const auto i : its_legacy_instance_interval_set) { + its_instance_method_intervals + += std::make_pair(i, its_legacy_method_interval_set); + } + } + } + } + if (its_service != 0x0 && !its_instance_method_intervals.empty()) { + _policy->requests_ += std::make_pair( + boost::icl::discrete_interval<service_t>( + its_service, its_service, + boost::icl::interval_bounds::closed()), + its_instance_method_intervals); + } + } + } else if (l->first == "offers") { + for (auto n = l->second.begin(); n != l->second.end(); ++n) { + service_t its_service(0x0); + instance_t its_instance(0x0); + boost::icl::interval_set<instance_t> its_instance_interval_set; + for (auto k = n->second.begin(); k != n->second.end(); ++k) { + if (k->first == "service") { + read_data(k->second.data(), its_service); + } else if (k->first == "instance") { // legacy definition for instances + std::string its_value(k->second.data()); + if (its_value != "any") { + read_data(its_value, its_instance); + if (its_instance != 0x0) { + its_instance_interval_set.insert(its_instance); + } + } else { + its_instance_interval_set.insert( + boost::icl::discrete_interval<instance_t>( + 0x0001, 0xFFFF)); + } + } else if (k->first == "instances") { // new instances definition + load_interval_set(k->second, its_instance_interval_set); + } + } + if (its_service != 0x0 && !its_instance_interval_set.empty()) { + _policy->offers_ + += std::make_pair( + boost::icl::discrete_interval<service_t>( + its_service, its_service, + boost::icl::interval_bounds::closed()), + its_instance_interval_set); + } + } + } + } +} + + +void +policy_manager_impl::load_credential( + const boost::property_tree::ptree &_tree, + boost::icl::interval_map<uid_t, + boost::icl::interval_set<gid_t> > &_credentials) { + + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + boost::icl::interval_set<uid_t> its_uid_interval_set; + boost::icl::interval_set<gid_t> its_gid_interval_set; + + for (auto j = i->second.begin(); j != i->second.end(); ++j) { + std::string its_key(j->first); + if (its_key == "uid") { + load_interval_set(j->second, its_uid_interval_set); + } else if (its_key == "gid") { + load_interval_set(j->second, its_gid_interval_set); + } else { + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " + << "Malformed credential (contains illegal key \"" + << its_key << "\")"; + } + } + + for (const auto its_uid_interval : its_uid_interval_set) { + _credentials + += std::make_pair(its_uid_interval, its_gid_interval_set); + } + } +} + +bool +policy_manager_impl::load_routing_credentials(const configuration_element &_element) { + try { + auto its_routing_cred = _element.tree_.get_child("routing-credentials"); + if (is_configured_) { + VSOMEIP_WARNING << "vSomeIP Security: Multiple definitions of routing-credentials." + << " Ignoring definition from " << _element.name_; + } else { + for (auto i = its_routing_cred.begin(); + i != its_routing_cred.end(); + ++i) { + std::string its_key(i->first); + std::string its_value(i->second.data()); + if (its_key == "uid") { + uint32_t its_uid(0); + read_data(its_value, its_uid); + std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); + std::get<0>(routing_credentials_) = its_uid; + } else if (its_key == "gid") { + uint32_t its_gid(0); + read_data(its_value, its_gid); + std::lock_guard<std::mutex> its_lock(routing_credentials_mutex_); + std::get<1>(routing_credentials_) = its_gid; + } + } + check_routing_credentials_ = true; + is_configured_ = true; + } + } catch (...) { + return false; + } + return true; +} + + +void +policy_manager_impl::load_security_update_whitelist(const configuration_element &_element) { +#ifdef _WIN32 + return; +#endif + try { + auto optional = _element.tree_.get_child_optional("security-update-whitelist"); + if (!optional) { + return; + } + auto found_whitelist = _element.tree_.get_child("security-update-whitelist"); + for (auto its_whitelist = found_whitelist.begin(); + its_whitelist != found_whitelist.end(); ++its_whitelist) { + + if (its_whitelist->first == "uids") { + { + std::lock_guard<std::mutex> its_lock(uid_whitelist_mutex_); + load_interval_set(its_whitelist->second, uid_whitelist_); + } + } else if (its_whitelist->first == "services") { + { + std::lock_guard<std::mutex> its_lock(service_interface_whitelist_mutex_); + load_interval_set(its_whitelist->second, service_interface_whitelist_); + } + } else if (its_whitelist->first == "check-whitelist") { + if (its_whitelist->second.data() == "true") { + check_whitelist_ = true; + } else { + check_whitelist_ = false; + } + } + } + } catch (...) { + } +} + +void +policy_manager_impl::load_security_policy_extensions(const configuration_element &_element) { +#ifdef _WIN32 + return; +#endif + try { + auto optional = _element.tree_.get_child_optional("container_policy_extensions"); + if (!optional) { + return; + } + auto found_policy_extensions = _element.tree_.get_child("container_policy_extensions"); + boost::filesystem::path its_base_path; + { + boost::unique_lock<boost::shared_mutex> its_lock(policy_extension_paths_mutex_); + its_base_path = boost::filesystem::path(policy_base_path_); + } + + for (auto i = found_policy_extensions.begin(); + i != found_policy_extensions.end(); ++i) { + boost::filesystem::path its_canonical_path; + std::string its_client_host(""); + std::string its_path(""); + auto its_data = i->second; + for (auto j = its_data.begin(); j != its_data.end(); ++j) { + std::string its_key(j->first); + std::string its_value(j->second.data()); + if (its_key == "container") { + if(its_value != "") { + its_client_host = its_value; + } + } else if (its_key == "path") { + if(its_value != "") { + its_path = its_value; + } + } + } + + std::string str = VSOMEIP_DEFAULT_CONFIGURATION_FOLDER; + std::string its_filesystem_path = str.substr(0, str.find_last_of("\\/")) + + its_path.erase(0, its_path.find_first_of("\\/")); + + if (!utility::is_folder(its_filesystem_path)) { + VSOMEIP_DEBUG << __func__ << ": The path " + << its_filesystem_path + << " is not valid"; + } + std::map<std::string, bool> empty_map; + policy_extension_paths_[its_client_host] = std::make_pair(its_filesystem_path, empty_map); + + VSOMEIP_INFO << __func__ << ": Insert policy extension path: [" << its_filesystem_path + << "] for hostname: [" << its_client_host << "]"; + } + } catch (...) { + } +} + +template<typename T_> +void policy_manager_impl::load_interval_set( + const boost::property_tree::ptree &_tree, + boost::icl::interval_set<T_> &_intervals, bool _exclude_margins) { + + boost::icl::interval_set<T_> its_intervals; + T_ its_min = std::numeric_limits<T_>::min(); + T_ its_max = std::numeric_limits<T_>::max(); + + if (_exclude_margins) { + its_min++; + its_max--; + } + + const std::string its_key(_tree.data()); + if (its_key == "any") { + its_intervals.insert(boost::icl::discrete_interval<T_>::closed( + its_min, its_max)); + } else { + for (auto i = _tree.begin(); i != _tree.end(); ++i) { + auto its_data = i->second; + if (!its_data.data().empty()) { + T_ its_id; + read_data(its_data.data(), its_id); + if (its_id >= its_min && its_id <= its_max) + its_intervals.insert(its_id); + } else { + T_ its_first, its_last; + bool has_first(false), has_last(false); + for (auto j = its_data.begin(); j != its_data.end(); ++j) { + std::string its_key(j->first); + std::string its_value(j->second.data()); + if (its_key == "first") { + if (its_value == "min") { + its_first = its_min; + } else { + read_data(its_value, its_first); + } + has_first = true; + } else if (its_key == "last") { + if (its_value == "max") { + its_last = its_max; + } else { + read_data(its_value, its_last); + } + has_last = true; + } else { + VSOMEIP_WARNING << "vSomeIP Security: Security configuration: " + << " Malformed range. Contains illegal key (" + << its_key << ")"; + } + } + if (has_first && has_last && its_first <= its_last) { + its_intervals.insert( + boost::icl::discrete_interval<T_>::closed(its_first, its_last)); + } + } + } + } + + _intervals = its_intervals; +} + +void +policy_manager_impl::get_requester_policies(const std::shared_ptr<policy> _policy, + std::set<std::shared_ptr<policy> > &_requesters) const { + + std::lock_guard<std::mutex> its_lock(_policy->mutex_); + for (const auto &o : _policy->offers_) { + boost::unique_lock<boost::shared_mutex> its_policies_lock(any_client_policies_mutex_); + for (const auto &p : any_client_policies_) { + if (p == _policy) + continue; + + std::lock_guard<std::mutex> its_lock(p->mutex_); + + auto its_policy = std::make_shared<policy>(); + its_policy->credentials_ = p->credentials_; + + for (const auto &r : p->requests_) { + // o represents an offer by a service interval and its instances + // (a set of intervals) + // r represents a request by a service interval and its instances + // and methods (instance intervals mapped to interval sets of methods) + // + // Thus, r matches o if their service identifiers as well as their + // instances overlap. If r and o match, a new policy must be + // created that contains the overlapping services/instances mapping + // of r and o together with the methods from r + service_t its_o_lower, its_o_upper, its_r_lower, its_r_upper; + get_bounds(o.first, its_o_lower, its_o_upper); + get_bounds(r.first, its_r_lower, its_r_upper); + + if (its_o_lower <= its_r_upper && its_r_lower <= its_o_upper) { + auto its_service_min = std::max(its_o_lower, its_r_lower); + auto its_service_max = std::min(its_r_upper, its_o_upper); + + for (const auto &i : o.second) { + for (const auto &j : r.second) { + for (const auto &k : j.second) { + instance_t its_i_lower, its_i_upper, its_k_lower, its_k_upper; + get_bounds(i, its_i_lower, its_i_upper); + get_bounds(k, its_k_lower, its_k_upper); + + if (its_i_lower <= its_k_upper && its_k_lower <= its_i_upper) { + auto its_instance_min = std::max(its_i_lower, its_k_lower); + auto its_instance_max = std::min(its_i_upper, its_k_upper); + + boost::icl::interval_map<instance_t, + boost::icl::interval_set<method_t> > its_instances_methods; + its_instances_methods += std::make_pair( + boost::icl::interval<instance_t>::closed( + its_instance_min, its_instance_max), + j.second); + + its_policy->requests_ += std::make_pair( + boost::icl::interval<instance_t>::closed( + its_service_min, its_service_max), + its_instances_methods); + } + } + } + } + } + } + + if (!its_policy->requests_.empty()) { + _requesters.insert(its_policy); + its_policy->print(); + } + } + } +} + +void +policy_manager_impl::get_clients(uid_t _uid, gid_t _gid, + std::unordered_set<client_t> &_clients) const { + + std::lock_guard<std::mutex> its_lock(ids_mutex_); + for (const auto &i : ids_) { + if (i.second.client_type == VSOMEIP_CLIENT_UDS + && i.second.client.uds_client.user == _uid + && i.second.client.uds_client.group == _gid) + _clients.insert(i.first); + } +} + +bool +policy_manager_impl::is_policy_extension(const std::string &_path) const { + auto its_pos = _path.find("vsomeip_policy_extensions.json"); + if (its_pos != std::string::npos) { + return true; + } + return false; +} + +void +policy_manager_impl::set_policy_extension_base_path(const std::string &_path) { + auto its_pos = _path.find("vsomeip_policy_extensions.json"); + std::lock_guard<std::mutex> its_lock(policy_base_path_mutex_); + policy_base_path_ = _path.substr(0, its_pos); +} + +std::string +policy_manager_impl::get_policy_extension_path(const std::string &_client_host) const { + boost::shared_lock<boost::shared_mutex> lock(policy_extension_paths_mutex_); + return get_policy_extension_path_unlocked(_client_host); +} +//only be called after loading of the mutex +std::string +policy_manager_impl::get_policy_extension_path_unlocked(const std::string &_client_host) const { + std::string its_path(""); + + auto found_host = policy_extension_paths_.find(_client_host); + + if (found_host != policy_extension_paths_.end()) { + its_path = found_host->second.first; + } + return its_path; +} + +policy_manager_impl::policy_loaded_e +policy_manager_impl::is_policy_extension_loaded(const std::string &_client_host) const { + boost::shared_lock<boost::shared_mutex> lock(policy_extension_paths_mutex_); + + auto found_host = policy_extension_paths_.find(_client_host); + if (found_host != policy_extension_paths_.end()) { + + auto found_complete_path = found_host->second.second.find( + get_security_config_folder(found_host->second.first)); + if (found_complete_path != found_host->second.second.end()) { + if (found_complete_path->second) { + return policy_manager_impl::policy_loaded_e::POLICY_PATH_FOUND_AND_LOADED; + } else { + return policy_manager_impl::policy_loaded_e::POLICY_PATH_FOUND_AND_NOT_LOADED; + } + } + } + + // we do not have a path to load + return policy_manager_impl::policy_loaded_e::POLICY_PATH_INEXISTENT; +} + +void +policy_manager_impl::set_is_policy_extension_loaded(const std::string &_client_host, + const bool _loaded) { + boost::unique_lock<boost::shared_mutex> lock(policy_extension_paths_mutex_); + auto found_host = policy_extension_paths_.find(_client_host); + + if (found_host != policy_extension_paths_.end()) { + std::string its_folder = get_policy_extension_path_unlocked(_client_host); + std::string its_complete_folder = get_security_config_folder(its_folder); + + // if the map key of complete path folder exist, will be updated + // if not will create an new entry + found_host->second.second[its_complete_folder] = _loaded; + } +} + +std::string +policy_manager_impl::get_security_config_folder(const std::string &its_folder) const +{ + std::stringstream its_security_config_folder; + its_security_config_folder << its_folder; + +#if defined(__linux__) || defined(ANDROID) + its_security_config_folder << "/" << getuid() << "_" << getgid(); +#endif + + if (utility::is_folder(its_security_config_folder.str())) { + return its_security_config_folder.str(); + } else { + VSOMEIP_INFO << __func__<< ": Invalid folder for " << its_security_config_folder.str(); + } + return std::string(""); } std::shared_ptr<policy> @@ -33,29 +1299,175 @@ policy_manager_impl::parse_uid_gid(const byte_t* &_buffer, && its_policy->deserialize_uid_gid(_buffer, _buffer_size, _uid, _gid)); } +#endif // !VSOMEIP_DISABLE_SECURITY + bool -policy_manager_impl::is_policy_update_allowed(uint32_t _uid, std::shared_ptr<policy> &_policy) const { +policy_manager_impl::store_client_to_sec_client_mapping( + client_t _client, const vsomeip_sec_client_t *_sec_client) { + + if (_sec_client != nullptr && _sec_client->client_type == VSOMEIP_CLIENT_UDS) { + // store the client -> sec_client mapping + std::lock_guard<std::mutex> its_lock(ids_mutex_); + auto found_client = ids_.find(_client); + if (found_client != ids_.end()) { + if (!utility::compare(found_client->second, *_sec_client)) { + uid_t its_old_uid = found_client->second.client.uds_client.user; + gid_t its_old_gid = found_client->second.client.uds_client.group; + uid_t its_new_uid = _sec_client->client.uds_client.user; + gid_t its_new_gid = _sec_client->client.uds_client.group; + + VSOMEIP_WARNING << "vSomeIP Security: Client 0x" + << std::hex << _client << " with UID/GID=" + << std::dec << its_new_uid << "/" << its_new_gid + << " : Overwriting existing credentials UID/GID=" + << std::dec << its_old_uid << "/" << its_old_gid; - auto its_security = security_impl::get(); - return (its_security - && its_security->is_policy_update_allowed(_uid, _policy)); + found_client->second = *_sec_client; + return (true); + } + } else { + ids_[_client] = *_sec_client; + } + return (true); + } + + return (false); } bool -policy_manager_impl::is_policy_removal_allowed(uint32_t _uid) const { +policy_manager_impl::get_client_to_sec_client_mapping(client_t _client, + vsomeip_sec_client_t &_sec_client) { + { + // get the UID / GID of the client + std::lock_guard<std::mutex> its_lock(ids_mutex_); + if (ids_.find(_client) != ids_.end()) { + _sec_client = ids_[_client]; + return (true); + } + return (false); + } +} - auto its_security = security_impl::get(); - return (its_security - && its_security->is_policy_removal_allowed(_uid)); +bool +policy_manager_impl::remove_client_to_sec_client_mapping(client_t _client) { + + vsomeip_sec_client_t its_sec_client; + bool is_client_removed(false); + bool is_sec_client_removed(false); + { + std::lock_guard<std::mutex> its_lock(ids_mutex_); + auto found_client = ids_.find(_client); + if (found_client != ids_.end()) { + its_sec_client = found_client->second; + ids_.erase(found_client); + is_client_removed = true; + } + } + { + std::lock_guard<std::mutex> its_lock(sec_client_to_clients_mutex_); + if (is_client_removed) { + auto found_sec_client = sec_client_to_clients_.find(its_sec_client); + if (found_sec_client != sec_client_to_clients_.end()) { + auto its_client = found_sec_client->second.find(_client); + if (its_client != found_sec_client->second.end()) { + found_sec_client->second.erase(its_client); + if (found_sec_client->second.empty()) { + sec_client_to_clients_.erase(found_sec_client); + } + is_sec_client_removed = true; + } + } + } else { + for (auto it = sec_client_to_clients_.begin(); + it != sec_client_to_clients_.end(); ++it) { + auto its_client = it->second.find(_client); + if (its_client != it->second.end()) { + it->second.erase(its_client); + if (it->second.empty()) { + sec_client_to_clients_.erase(it); + } + is_sec_client_removed = true; + break; + } + } + } + } + + return (is_client_removed && is_sec_client_removed); +} + +void +policy_manager_impl::store_sec_client_to_client_mapping( + const vsomeip_sec_client_t *_sec_client, client_t _client) { + + if (_sec_client && _sec_client->client_type == VSOMEIP_CLIENT_UDS) { + // store the uid gid to clients mapping + std::lock_guard<std::mutex> its_lock(sec_client_to_clients_mutex_); + auto found_sec_client = sec_client_to_clients_.find(*_sec_client); + if (found_sec_client != sec_client_to_clients_.end()) { + found_sec_client->second.insert(_client); + } else { + std::set<client_t> mapped_clients; + mapped_clients.insert(_client); + sec_client_to_clients_.insert(std::make_pair(*_sec_client, mapped_clients)); + } + } } bool -policy_manager_impl::parse_policy(const byte_t* &_buffer, uint32_t &_buffer_size, - uint32_t &_uid, uint32_t &_gid, const std::shared_ptr<policy> &_policy) const { +policy_manager_impl::get_sec_client_to_clients_mapping( + const vsomeip_sec_client_t *_sec_client, + std::set<client_t> &_clients) { + + if (_sec_client && _sec_client->client_type == VSOMEIP_CLIENT_UDS) { + // get the clients corresponding to uid, gid + std::lock_guard<std::mutex> its_lock(sec_client_to_clients_mutex_); + auto found_sec_client = sec_client_to_clients_.find(*_sec_client); + if (found_sec_client != sec_client_to_clients_.end()) { + _clients = found_sec_client->second; + return true; + } + } + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Manage the security object +//////////////////////////////////////////////////////////////////////////////// +static std::shared_ptr<policy_manager_impl> *the_policy_manager_ptr__(nullptr); +static std::mutex the_policy_manager_mutex__; + +std::shared_ptr<policy_manager_impl> +policy_manager_impl::get() { +#if defined(__linux__) || defined(ANDROID) + std::lock_guard<std::mutex> its_lock(the_policy_manager_mutex__); +#endif + if(the_policy_manager_ptr__ == nullptr) { + the_policy_manager_ptr__ = new std::shared_ptr<policy_manager_impl>(); + } + if (the_policy_manager_ptr__ != nullptr) { + if (!(*the_policy_manager_ptr__)) { + *the_policy_manager_ptr__ = std::make_shared<policy_manager_impl>(); + } + return (*the_policy_manager_ptr__); + } + return (nullptr); +} - auto its_security = security_impl::get(); - return (its_security - && its_security->parse_policy(_buffer, _buffer_size, _uid, _gid, _policy)); +#if defined(__linux__) || defined(ANDROID) +static void security_teardown(void) __attribute__((destructor)); +static void security_teardown(void) +{ + if (the_policy_manager_ptr__ != nullptr) { + // TODO: This mutex is causing a crash due to changes in the way mutexes are defined. + // Since this function only runs on the main thread, no mutex should be needed. Leaving a + // comment pending a refactor. + // std::lock_guard<std::mutex> its_lock(the_policy_manager_mutex__); + the_policy_manager_ptr__->reset(); + delete the_policy_manager_ptr__; + the_policy_manager_ptr__ = nullptr; + } } +#endif } // namespace vsomeip_v3 |