diff --git a/SecurityAgent/AccessControlList.cpp b/SecurityAgent/AccessControlList.cpp new file mode 100644 index 0000000000..a011096e4e --- /dev/null +++ b/SecurityAgent/AccessControlList.cpp @@ -0,0 +1,30 @@ +/* + * If not stated otherwise in this file or this component's LICENSE file the + * following copyright and licenses apply: + * + * Copyright 2020 RDK Management + * + * 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 "Module.h" +#include "AccessControlList.h" + +namespace WPEFramework { + + ENUM_CONVERSION_BEGIN(Plugin::AccessControlList::mode) + { Plugin::AccessControlList::ALLOWED, _TXT("allowed") }, + { Plugin::AccessControlList::BLOCKED, _TXT("blocked") }, + ENUM_CONVERSION_END(Plugin::AccessControlList::mode) + +} \ No newline at end of file diff --git a/SecurityAgent/AccessControlList.h b/SecurityAgent/AccessControlList.h index cef847dbd9..761af43df0 100644 --- a/SecurityAgent/AccessControlList.h +++ b/SecurityAgent/AccessControlList.h @@ -26,18 +26,18 @@ // helper functions namespace { - void ReplaceString(std::string& subject, const std::string& search,const std::string& replace) + void ReplaceString(string& subject, const string& search,const string& replace) { size_t pos = 0; - while ((pos = subject.find(search, pos)) != std::string::npos) { + while ((pos = subject.find(search, pos)) != string::npos) { subject.replace(pos, search.length(), replace); pos += replace.length(); } } - std::string CreateRegex(const std::string& input) + string CreateRegex(const string& input) { - std::string regex = input; + string regex = input; // order of replacing is important ReplaceString(regex,"*","^[a-zA-Z0-9.]+$"); @@ -46,9 +46,9 @@ namespace { return regex; } - std::string CreateUrlRegex(const std::string& input) + string CreateUrlRegex(const string& input) { - std::string regex = input; + string regex = input; // order of replacing is important ReplaceString(regex,"/","\\/"); @@ -63,7 +63,6 @@ namespace { return regex; } - } namespace WPEFramework { @@ -73,51 +72,98 @@ namespace Plugin { //if Block then check for Block[] and block if present //else must be explicitly allowed + // "xreapps.net": { + // "thunder": { + // "default": "blocked", + // "DeviceInfo": { + // "default": "allowed", + // "methods": [ "register", "unregister" ] + // } + // } + // }, + class EXTERNAL AccessControlList { + public: + enum mode { + BLOCKED, + ALLOWED + }; private: class EXTERNAL JSONACL : public Core::JSON::Container { public: - class Config : public Core::JSON::Container { + class Plugins : public Core::JSON::Container { + public: + class Rules : public Core::JSON::Container { + public: + Rules(const Rules&) = delete; + Rules& operator=(const Rules&) = delete; + + Rules() + : Core::JSON::Container() + , Default(BLOCKED) + , Methods() + { + Add(_T("default"), &Default); + Add(_T("methods"), &Methods); + } + ~Rules() override + { + } + + public: + Core::JSON::EnumType Default; + Core::JSON::ArrayType Methods; + }; + + using PluginsMap = std::map; + public: - Config(const Config&) = delete; - Config& operator=(const Config&) = delete; + using Iterator = Core::IteratorMapType; + + Plugins(const Plugins&) = delete; + Plugins& operator=(const Plugins&) = delete; - Config() + Plugins() + : Core::JSON::Container() + , Default(BLOCKED) + , _plugins() { - Add(_T("allow"), &Allow); - Add(_T("block"), &Block); + Add(_T("default"), &Default); } - virtual ~Config() + ~Plugins() override { } public: - Core::JSON::ArrayType Allow; - Core::JSON::ArrayType Block; - }; - class Role : public Core::JSON::Container { - public: - Role(const Role&) = delete; - Role& operator=(const Role&) = delete; + Core::JSON::EnumType Default; - Role() - : Configuration() + inline Iterator Elements() const { - Add(_T("thunder"), &Configuration); + return (Iterator(_plugins)); } - virtual ~Role() + + private: + virtual bool Request(const TCHAR label[]) { + if (_plugins.find(label) == _plugins.end()) { + auto element = _plugins.emplace(std::piecewise_construct, + std::forward_as_tuple(label), + std::forward_as_tuple()); + Add(element.first->first.c_str(), &(element.first->second)); + } + return (true); } - public: - Config Configuration; + private: + PluginsMap _plugins; }; + class Roles : public Core::JSON::Container { private: - using RolesMap = std::map; + using RolesMap = std::map; public: - using Iterator = Core::IteratorMapType; + using Iterator = Core::IteratorMapType; Roles(const Roles&) = delete; Roles& operator=(const Roles&) = delete; @@ -205,23 +251,63 @@ namespace Plugin { public: class Filter { + private: + class Plugin { + public: + Plugin() = delete; + Plugin(const Plugin&) = delete; + Plugin& operator= (const Plugin&) = delete; + + Plugin (const JSONACL::Plugins::Rules& rules) + : _defaultBlocked(rules.Default.Value() == mode::BLOCKED) + , _methods() { + Core::JSON::ArrayType::ConstIterator index(rules.Methods.Elements()); + while (index.Next() == true) { + string str = index.Current().Value(); + _methods.emplace_back(CreateRegex(str)); + } + } + ~Plugin() { + } + + public: + bool Allowed(const string& method) const + { + bool found = false; + + std::list::const_iterator index(_methods.begin()); + + while ((index != _methods.end()) && (found == false)) { + std::regex expression(index->c_str()); + std::smatch matchList; + found = std::regex_search(method, matchList, expression); + if (found == false) { + index++; + } + } + return !(_defaultBlocked ^ found); + } + + private: + bool _defaultBlocked; + std::list _methods; + }; + public: Filter() = delete; Filter(const Filter&) = delete; Filter& operator=(const Filter&) = delete; - Filter(const JSONACL::Config& filter) + Filter(const JSONACL::Plugins& plugins) + : _defaultBlocked(plugins.Default.Value() == mode::BLOCKED) + , _plugins() { - Core::JSON::ArrayType::ConstIterator index(filter.Allow.Elements()); - std::string str; - while (index.Next() == true) { - str = index.Current().Value(); - _allow.emplace_back(CreateRegex(str)); - } - index = (filter.Block.Elements()); + JSONACL::Plugins::Iterator index(plugins.Elements()); + while (index.Next() == true) { - str = index.Current().Value(); - _block.emplace_back(CreateRegex(str)); + _plugins.emplace(std::piecewise_construct, + std::forward_as_tuple(CreateRegex(index.Key())), + std::forward_as_tuple(index.Current())); } } ~Filter() @@ -229,34 +315,26 @@ namespace Plugin { } public: - bool Allowed(const string& method) const + bool Allowed(const string callsign, const string& method) const { - bool allowed = false; - if (_allowSet) { - std::list::const_iterator index(_allow.begin()); - while ((index != _allow.end()) && (allowed == false)) { - std::regex expression(index->c_str()); - std::smatch matchList; - allowed = std::regex_search(method, matchList, expression); - index++; - } - } else { - allowed = true; - std::list::const_iterator index(_block.begin()); - while ((index != _block.end()) && (allowed == true)) { - std::regex expression(index->c_str()); - std::smatch matchList; - allowed = !std::regex_search(method, matchList, expression); + bool pluginFound = false; + + std::map::const_iterator index(_plugins.begin()); + while ((index != _plugins.end()) && (pluginFound == false)) { + std::regex expression(index->first.c_str()); + std::smatch matchList; + pluginFound = std::regex_search(callsign, matchList, expression); + if (pluginFound == false) { index++; } } - return (allowed); + + return (pluginFound == false ? !_defaultBlocked : index->second.Allowed(method)); } private: - bool _allowSet; - std::list _allow; - std::list _block; + bool _defaultBlocked; + std::map _plugins; }; using URLList = std::list>; @@ -308,7 +386,9 @@ namespace Plugin { if (std::regex_search(URL, matchList, expression) == true) { result = &(index->second); } - index++; + else { + index++; + } } return (result); @@ -333,7 +413,7 @@ namespace Plugin { _filterMap.emplace(std::piecewise_construct, std::forward_as_tuple(roleName), - std::forward_as_tuple(rolesIndex.Current().Configuration)); + std::forward_as_tuple(rolesIndex.Current())); } Core::JSON::ArrayType::Iterator index = controlList.Groups.Elements(); @@ -354,7 +434,7 @@ namespace Plugin { Filter& entry(selectedFilter->second); // create regex for url - std::string url_regex = CreateUrlRegex(index.Current().URL.Value()); + string url_regex = CreateUrlRegex(index.Current().URL.Value()); _urlMap.emplace_back(std::pair( url_regex, entry)); diff --git a/SecurityAgent/CMakeLists.txt b/SecurityAgent/CMakeLists.txt index 40b35823dc..a257687fdf 100644 --- a/SecurityAgent/CMakeLists.txt +++ b/SecurityAgent/CMakeLists.txt @@ -22,6 +22,7 @@ find_package(${NAMESPACE}Plugins REQUIRED) find_package(CompileSettingsDebug CONFIG REQUIRED) add_library(${MODULE_NAME} SHARED + AccessControlList.cpp SecurityAgent.cpp SecurityContext.cpp SecurityAgentJsonRpc.cpp diff --git a/SecurityAgent/SecurityAgent.cpp b/SecurityAgent/SecurityAgent.cpp index 33150b8a1c..6d031971dc 100644 --- a/SecurityAgent/SecurityAgent.cpp +++ b/SecurityAgent/SecurityAgent.cpp @@ -211,6 +211,7 @@ namespace Plugin { if (index.Next() == true) { // We might be receiving a plugin download request. + #ifdef SECURITY_TESTING_MODE if ((request.Verb == Web::Request::HTTP_PUT) && (request.HasBody() == true)) { if (index.Current() == _T("Token")) { Core::ProxyType data(request.Body()); @@ -224,12 +225,15 @@ namespace Plugin { result->Body(token); result->ContentType = Web::MIMETypes::MIME_TEXT; - result->ErrorCode = Core::ERROR_NONE; + result->ErrorCode = Web::STATUS_OK; result->Message = "Ok"; } } } - } else if ( (request.Verb == Web::Request::HTTP_GET) && (index.Current() == _T("Valid")) ) { + } else + #endif + + if ( (request.Verb == Web::Request::HTTP_GET) && (index.Current() == _T("Valid")) ) { result->ErrorCode = Web::STATUS_FORBIDDEN; result->Message = _T("Missing token"); diff --git a/SecurityAgent/SecurityAgent.h b/SecurityAgent/SecurityAgent.h index 5635e5520c..851a3f4403 100644 --- a/SecurityAgent/SecurityAgent.h +++ b/SecurityAgent/SecurityAgent.h @@ -152,7 +152,9 @@ namespace Plugin { // ------------------------------------------------------------------------------------------------------- void RegisterAll(); void UnregisterAll(); - uint32_t endpoint_createtoken(const JsonData::SecurityAgent::CreatetokenParamsData& params, JsonData::SecurityAgent::CreatetokenResultInfo& response); + #ifdef SECURITY_TESTING_MODE + uint32_t endpoint_createtoken(const JsonData::SecurityAgent::CreatetokenParamsData& params, JsonData::SecurityAgent::CreatetokenResultInfo& response); + #endif // DEBUG uint32_t endpoint_validate(const JsonData::SecurityAgent::CreatetokenResultInfo& params, JsonData::SecurityAgent::ValidateResultData& response); diff --git a/SecurityAgent/SecurityAgent.vcxproj b/SecurityAgent/SecurityAgent.vcxproj index 8bea3e9a0c..e824b55109 100644 --- a/SecurityAgent/SecurityAgent.vcxproj +++ b/SecurityAgent/SecurityAgent.vcxproj @@ -19,6 +19,7 @@ + @@ -117,9 +118,9 @@ Level3 Disabled true - _CRT_SECURE_NO_WARNINGS;_DEBUG;SECURITYOFFICER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + SECURITY_TESTING_MODE;_CRT_SECURE_NO_WARNINGS;_DEBUG;SECURITYOFFICER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true - $(ProjectDir)../../;$(SolutionDir);$(SolutionDir)src/base + $(ProjectDir)../../;$(SolutionDir)thirdparty/windows/include;$(SolutionDir)thirdparty/windows/include/zlib;$(SolutionDir);$(SolutionDir)src/base Windows @@ -133,9 +134,9 @@ Level3 Disabled true - _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;SECURITYOFFICER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + SECURITY_TESTING_MODE;_CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;SECURITYOFFICER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true - $(ProjectDir)../../;$(SolutionDir);$(SolutionDir)src/base + $(ProjectDir)../../;$(SolutionDir)thirdparty/windows/include;$(SolutionDir)thirdparty/windows/include/zlib;$(SolutionDir);$(SolutionDir)src/base Windows @@ -151,9 +152,9 @@ true true true - _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;SECURITYOFFICER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + SECURITY_TESTING_MODE;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;SECURITYOFFICER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true - $(ProjectDir)../../;$(SolutionDir);$(SolutionDir)src/base + $(ProjectDir)../../;$(SolutionDir)thirdparty/windows/include;$(SolutionDir)thirdparty/windows/include/zlib;$(SolutionDir);$(SolutionDir)src/base Windows @@ -171,9 +172,9 @@ true true true - _CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + SECURITY_TESTING_MODE;_CRT_SECURE_NO_WARNINGS;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) true - $(ProjectDir)../../;$(SolutionDir);$(SolutionDir)src/base + $(ProjectDir)../../;$(SolutionDir)thirdparty/windows/include;$(SolutionDir)thirdparty/windows/include/zlib;$(SolutionDir);$(SolutionDir)src/base Windows diff --git a/SecurityAgent/SecurityAgent.vcxproj.filters b/SecurityAgent/SecurityAgent.vcxproj.filters index fffcdd9023..6fe8c86ab4 100644 --- a/SecurityAgent/SecurityAgent.vcxproj.filters +++ b/SecurityAgent/SecurityAgent.vcxproj.filters @@ -13,6 +13,9 @@ Source Files + + Source Files + diff --git a/SecurityAgent/SecurityAgentJsonRpc.cpp b/SecurityAgent/SecurityAgentJsonRpc.cpp index 369d31beb3..50562896c9 100644 --- a/SecurityAgent/SecurityAgentJsonRpc.cpp +++ b/SecurityAgent/SecurityAgentJsonRpc.cpp @@ -32,19 +32,25 @@ namespace Plugin { void SecurityAgent::RegisterAll() { + #ifdef SECURITY_TESTING_MODE Register(_T("createtoken"), &SecurityAgent::endpoint_createtoken, this); + #endif + Register(_T("validate"), &SecurityAgent::endpoint_validate, this); } void SecurityAgent::UnregisterAll() { Unregister(_T("validate")); + #ifdef SECURITY_TESTING_MODE Unregister(_T("createtoken")); + #endif } // API implementation // + #ifdef SECURITY_TESTING_MODE // Method: createtoken - Creates Token // Return codes: // - ERROR_NONE: Success @@ -64,6 +70,7 @@ namespace Plugin { return result; } + #endif // Method: validate - Creates Token // Return codes: diff --git a/SecurityAgent/SecurityContext.cpp b/SecurityAgent/SecurityContext.cpp index fab8308ac3..34d937f053 100644 --- a/SecurityAgent/SecurityContext.cpp +++ b/SecurityAgent/SecurityContext.cpp @@ -53,9 +53,10 @@ namespace WPEFramework { namespace Plugin { SecurityContext::SecurityContext(const AccessControlList* acl, const uint16_t length, const uint8_t payload[]) - : _accessControlList(nullptr) + : _token(string(reinterpret_cast(payload), length)) + , _accessControlList(nullptr) { - _context.FromString(string(reinterpret_cast(payload), length)); + _context.FromString(_token); if ( (_context.URL.IsSet() == true) && (acl != nullptr) ) { _accessControlList = acl->FilterMapFromURL(_context.URL.Value()); @@ -77,9 +78,8 @@ namespace Plugin { { bool allowed = (_accessControlList != nullptr); - if (allowed == true) { - - } + if (allowed == true) { + } return (allowed); } @@ -87,7 +87,13 @@ namespace Plugin { //! Allow a JSONRPC message to be checked before it is offered for processing. bool SecurityContext::Allowed(const Core::JSONRPC::Message& message) const /* override */ { - return ((_accessControlList != nullptr) && (_accessControlList->Allowed(message.Callsign()))); + return ((_accessControlList != nullptr) && (_accessControlList->Allowed(message.Callsign(), message.Method()))); } + + string SecurityContext::Token() const /* override */ + { + return (_token); + } + } } diff --git a/SecurityAgent/SecurityContext.h b/SecurityAgent/SecurityContext.h index c5f92e0518..8685e5dd08 100644 --- a/SecurityAgent/SecurityContext.h +++ b/SecurityAgent/SecurityContext.h @@ -69,6 +69,8 @@ namespace Plugin { //! Allow a JSONRPC message to be checked before it is offered for processing. bool Allowed(const Core::JSONRPC::Message& message) const override; + string Token() const override; + private: // Build QueryInterface implementation, specifying all possible interfaces to be returned. BEGIN_INTERFACE_MAP(SecurityOfficer) @@ -76,6 +78,7 @@ namespace Plugin { END_INTERFACE_MAP private: + string _token; Payload _context; const AccessControlList::Filter* _accessControlList; }; diff --git a/SecurityAgent/TestPage.html b/SecurityAgent/TestPage.html new file mode 100644 index 0000000000..a7cb19da4d --- /dev/null +++ b/SecurityAgent/TestPage.html @@ -0,0 +1,83 @@ + + + + + + + +token body + +
+
+ +callsign +
+method
+params
+ + + + + diff --git a/SecurityAgent/example_acl.json b/SecurityAgent/example_acl.json index 0991e5ebbd..eec0089369 100644 --- a/SecurityAgent/example_acl.json +++ b/SecurityAgent/example_acl.json @@ -40,90 +40,6 @@ "url": "*://*.comcast.com", "role": "comcast" }, - { - "url": "*://*.comcast.com:*", - "role": "comcast" - }, - { - "url": "*://*.comcast.net", - "role": "comcast" - }, - { - "url": "*://*.comcast.net:*", - "role": "comcast" - }, - { - "url": "*.x1.xcal.tv", - "role": "comcast" - }, - { - "url": "*.x1.xcal.tv:*", - "role": "comcast" - }, - { - "url": "*.x1-app.xcr.comcast.net", - "role": "comcast" - }, - { - "url": "*.x1-app.xcr.comcast.net:*", - "role": "comcast" - }, - { - "url": "*://www.pxscene.org", - "role": "pxscene.org" - }, - { - "url": "*://www.pxscene.org:*", - "role": "pxscene.org" - }, - { - "url": "*://pxscene.org", - "role": "pxscene.org" - }, - { - "url": "*://pxscene.org:*", - "role": "pxscene.org" - }, - { - "url": "*://www.sparkui.org", - "role": "sparkui.org" - }, - { - "url": "*://www.sparkui.org:*", - "role": "sparkui.org" - }, - { - "url": "*://sparkui.org", - "role": "sparkui.org" - }, - { - "url": "*://sparkui.org:*", - "role": "sparkui.org" - }, - { - "url": "*://xre2-apps.cvs-a.ula.comcast.net", - "role": "pxscene-samples" - }, - { - "url": "*://xre2-apps.cvs-a.ula.comcast.net:*", - "role": "pxscene-samples" - }, - { - "url": "*://px-apps.sys.comcast.net", - "role": "pxscene-samples" - }, - { - "url": "*://px-apps.sys.comcast.net:*", - "role": "pxscene-samples" - }, - { - "url": "*://*.xreapps.net", - "role": "xreapps.net" - }, - { - "url": "*://tvxcts-c5-c00001-b.ch.tvx.comcast.com", - "role": "cts" - }, { "url": "*://metrological.com", "role": "metrological" @@ -135,85 +51,26 @@ ], "roles": { "default" : { - "thunder": { - "block" : [ - "*" - ] - } - }, - "xreapps.net": { - "thunder": { - "allow": [ - "*" - ], - "block" : [ - "DeviceInfo" - ] - } + "default" : "blocked" }, - "local": { - "thunder": { - "allow": [ - "*" - ], - "block" : [ - ] - } - }, - "comcast": { - "thunder": { - "allow": [ - "DeviceInfo" - ], - "block" : [ - ] - } + "local" : { + "default" : "allowed" }, "metrological": { - "thunder": { - "allow": [ - "*" - ], - "block" : [ - ] - } - }, - "pxscene.org": { - "thunder": { - "allow": [ - "*" - ], - "block" : [ - ] - } - }, - "pxscene-samples": { - "thunder": { - "allow": [ - "*" - ], - "block" : [ - ] - } - }, - "cts": { - "thunder": { - "allow": [ - "*" - ], - "block" : [ - "DeviceInfo" - ] - } + "default": "blocked", + "DeviceInfo": { + "default": "allowed", + "methods": [ "register", "unregister" ] + }, + "JSONRPCPlugin": { + "default": "blocked", + "methods": [ "time", "status" ] + } }, - "sparkui.org": { - "thunder": { - "allow": [ - "*" - ], - "block" : [ - "DeviceInfo" - ] + "comcast": { + "default": "blocked", + "Compositor": { + "default": "allowed" } } }