2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 * @file ace_client.cpp
18 * @author Tomasz Swierczek (t.swierczek@samsung.com)
20 * @brief This file contains implementation of AceThinClient class
27 #include <dpl/string.h>
28 #include <dpl/optional_typedefs.h>
29 #include <boost/optional.hpp>
30 #include <dpl/log/log.h>
31 #include <dpl/singleton_safe_impl.h>
32 #include <ace-dao-ro/PromptModel.h>
34 #include <ace_popup_handler.h>
36 #include "ace_server_api.h"
37 #include "popup_response_server_api.h"
38 #include "ace-client/ace_client.h"
39 #include "ace-client/ace_client_helper.h"
40 #include <attribute_facade.h>
41 #include <ace/Request.h>
43 #include <app_manager.h>
44 #include <package_manager.h>
45 #include <privacy_checker_client.h>
47 #include <dpl/wrt-dao-ro/widget_dao_read_only.h>
48 // ACE tests need to use mock implementations
49 #ifdef ACE_CLIENT_TESTS
51 #include "AceDAOReadOnly_mock.h"
52 #include "communication_client_mock.h"
53 #include "PolicyInformationPoint_mock.h"
57 #include <ace-dao-ro/AceDAOReadOnly.h>
58 #include "SecurityCommunicationClient.h"
59 #include <ace/PolicyInformationPoint.h>
61 #endif // ACE_CLIENT_TESTS
63 IMPLEMENT_SAFE_SINGLETON(AceClient::AceThinClient)
65 ace_popup_handler_func_t popup_func = NULL;
70 // These devcaps actually are not requested in config file, so should be treaded
71 // as if were requested (access tags/WARP will block request if desired)
72 const std::string DEVCAP_EXTERNAL_NETWORK_ACCESS = "externalNetworkAccess";
73 const std::string DEVCAP_XML_HTTP_REQUEST = "XMLHttpRequest";
77 std::string AceFunctionParam::aceFunctionParamToken = "param:function";
81 enum class AceCachedPromptResult {
87 // AceThinClient implementation singleton
88 class AceThinClientImpl {
90 bool checkFunctionCall(const AceRequest& ace_request);
91 AcePreference getWidgetResourcePreference(
92 const AceResource& resource,
93 const AceWidgetHandle& handle) const;
94 bool checkPrivacy(const AceRequest& ace_request);
95 AceResourcesPreferences* getGlobalResourcesPreferences() const;
96 bool isInitialized() const;
102 bool containsNetworkDevCap(const AceRequest &ace_request);
103 bool checkFeatureList(const AceRequest& ace_request);
106 WebRuntimeImpl* m_wrt;
107 ResourceInformationImpl* m_res;
108 OperationSystemImpl* m_sys;
109 WrtSecurity::Communication::Client *m_communicationClient, *m_popupValidationClient;
111 AceSubject getSubjectForHandle(AceWidgetHandle handle) const;
112 AceCachedPromptResult getCachedPromptResult(
113 WidgetHandle widgetHandle,
115 const AceSessionId& sessionId) const;
116 bool askUser(PolicyEffect popupType,
117 const AceRequest& ace_request,
118 const AceBasicRequest& request);
120 bool validatePopupResponse(
121 const AceRequest& ace_request,
122 const AceBasicRequest& request,
124 Prompt::Validity validity = Prompt::Validity::ALWAYS);
125 mutable PolicyInformationPoint m_pip;
126 boost::optional<std::set<DPL::String>> m_grantedDevCaps;
127 std::set<std::string> m_acceptedFeatures;
130 AceThinClientImpl::AceThinClientImpl()
131 : m_wrt(new WebRuntimeImpl()),
132 m_res(new ResourceInformationImpl()),
133 m_sys(new OperationSystemImpl()),
134 m_communicationClient(NULL),
135 m_popupValidationClient(NULL),
136 m_pip(m_wrt, m_res, m_sys)
138 AceDB::AceDAOReadOnly::attachToThreadRO();
140 m_communicationClient = new WrtSecurity::Communication::Client(WrtSecurity::AceServerApi::INTERFACE_NAME());
141 m_popupValidationClient = new WrtSecurity::Communication::Client(WrtSecurity::PopupServerApi::INTERFACE_NAME());
142 } Catch (WrtSecurity::Communication::Client::Exception::SecurityCommunicationClientException) {
143 if(m_communicationClient) delete m_communicationClient;
144 if(m_popupValidationClient) delete m_popupValidationClient;
148 ReThrowMsg(AceThinClient::Exception::AceThinClientException,
149 "Failed to call security daemon");
153 AceThinClientImpl::~AceThinClientImpl()
155 Assert(NULL != m_communicationClient);
156 Assert(NULL != m_popupValidationClient);
157 delete m_communicationClient;
158 delete m_popupValidationClient;
162 m_communicationClient = NULL;
163 m_popupValidationClient = NULL;
164 AceDB::AceDAOReadOnly::detachFromThread();
168 bool AceThinClientImpl::isInitialized() const
170 return NULL != m_communicationClient && NULL != m_popupValidationClient;
173 bool AceThinClientImpl::containsNetworkDevCap(const AceRequest &ace_request)
175 AceDeviceCap deviceCap = ace_request.deviceCapabilities;
176 for (size_t j=0; j<deviceCap.devcapsCount; ++j) {
177 if (!deviceCap.devCapNames[j]) {
180 if (DEVCAP_XML_HTTP_REQUEST == deviceCap.devCapNames[j]
181 || DEVCAP_EXTERNAL_NETWORK_ACCESS == deviceCap.devCapNames[j])
189 bool AceThinClientImpl::checkFeatureList(const AceRequest& ace_request)
191 for (size_t i=0; i<ace_request.apiFeatures.count; ++i) {
192 Assert(ace_request.apiFeatures.apiFeature[i]);
193 std::string featureName(ace_request.apiFeatures.apiFeature[i]);
194 LogInfo("Api feature: " << featureName);
195 if (0 != m_acceptedFeatures.count(featureName)) {
198 LogInfo("Api-feature was not requested in widget config: " <<
204 bool AceThinClientImpl::checkPrivacy(const AceRequest& ace_request)
208 WrtDB::WidgetDAOReadOnly dao(ace_request.widgetHandle);
209 std::string tzPkgId = DPL::ToUTF8String(dao.getTizenPkgId());
211 LogInfo("Checking pkg_id : " << tzPkgId.c_str());
213 for (size_t i = 0; i < ace_request.apiFeatures.count; ++i) {
214 res = privacy_checker_check_package_by_privilege(tzPkgId.c_str(), ace_request.apiFeatures.apiFeature[i]);
215 LogInfo(" privilege : " << ace_request.apiFeatures.apiFeature[i] << " : " << ((res == PRIV_MGR_ERROR_SUCCESS) ? "true" : "false"));
216 if (res != PRIV_MGR_ERROR_SUCCESS)
223 bool AceThinClientImpl::checkFunctionCall(const AceRequest& ace_request)
227 // fill the m_grantedDevCaps, if not yet initialized
228 // TODO: This is not so pretty. AceThinClient is not explicitly
229 // tied to a widget handle, yet we assume it is always used
230 // with the same handle. This will be amended in a future
231 // refactoring (already planned).
232 if (!m_grantedDevCaps) {
233 m_grantedDevCaps = std::set<DPL::String>();
234 m_acceptedFeatures.clear();
236 AceDB::FeatureNameVector fvector;
238 AceDB::AceDAOReadOnly::getAcceptedFeature(ace_request.widgetHandle, &fvector);
239 } Catch (AceDB::AceDAOReadOnly::Exception::DatabaseError) {
240 LogError("Failed to read DB");
243 for(size_t i=0; i<fvector.size(); ++i) {
244 m_acceptedFeatures.insert(DPL::ToUTF8String(fvector[i]));
248 AceSubject subject = getSubjectForHandle(ace_request.widgetHandle);
250 // Create function params
251 const AceDeviceCap& devcaps = ace_request.deviceCapabilities;
253 LogInfo("Checking against config requested api-features.");
255 // Network device caps are not connected with api-features.
256 // We must pass empty api-feature when network dev cap is set.
257 if (!containsNetworkDevCap(ace_request) && !checkFeatureList(ace_request)) {
261 AceFunctionParams functionParams(devcaps.devcapsCount);
262 for (size_t i = 0; i < devcaps.devcapsCount; ++i) {
263 AceFunctionParam functionParam;
264 functionParam.addAttribute(AceFunctionParam::aceFunctionParamToken,
265 NULL == ace_request.functionName ?
266 "" : ace_request.functionName);
267 if (devcaps.paramsCount) {
268 Assert(devcaps.params);
269 for (size_t j = 0; j < devcaps.params[i].count; ++j) {
270 Assert(devcaps.params[i].param &&
271 devcaps.params[i].param[j].name &&
272 devcaps.params[i].param[j].value);
273 functionParam.addAttribute(
274 std::string(devcaps.params[i].param[j].name),
275 std::string(devcaps.params[i].param[j].value));
278 functionParams.push_back(functionParam);
281 // Convert AceRequest to array of AceBasicRequests
282 AceBasicRequests requests;
284 for (size_t i = 0; i < devcaps.devcapsCount; ++i) {
285 // Adding dev cap name here as resource id
286 Assert(devcaps.devCapNames[i]);
287 LogInfo("Device cap: " << devcaps.devCapNames[i]);
288 AceBasicRequest request(subject,
289 devcaps.devCapNames[i],
291 requests.push_back(request);
294 // true means access granted, false - denied
297 FOREACH(it, requests){
298 // Getting attributes from ACE DAO
299 AceBasicRequest& request = *it;
300 AceDB::BaseAttributeSet attributeSet;
301 AceDB::AceDAOReadOnly::getAttributes(&attributeSet);
303 // If true, we need to make popup IPC and ask user for decision
304 bool askPopup = false;
305 // If true, we need to make IPC to security daemon for policy
306 // decision on granting access
307 bool askServer = false;
308 // If askPopup == true, this is the kind of popup to be opened
309 PolicyEffect popupType = PolicyEffect::PROMPT_ONESHOT;
311 if (attributeSet.empty()) {
312 // Treat this case as missed cache - ask security daemon
313 LogInfo("Empty attribute set");
316 // Filling attributes with proper values
317 FunctionParamImpl params;
318 AceParamKeys keys = request.getFunctionParam().getKeys();
319 AceParamValues values = request.getFunctionParam().getValues();
320 for (size_t i = 0; i < keys.size(); ++i) {
321 params.addAttribute(keys[i], values[i]);
323 Request req(ace_request.widgetHandle,
324 WidgetExecutionPhase_Invoke,
326 req.addDeviceCapability(request.getResourceId());
328 m_pip.getAttributesValues(&req, &attributeSet);
330 // Getting cached policy result
331 OptionalExtendedPolicyResult exPolicyResult =
332 AceDB::AceDAOReadOnly::getPolicyResult(attributeSet);
334 if (!exPolicyResult) {
335 // Missed cache - ask security daemon
336 LogInfo("Missed policy result cache");
339 // Cached value found - now interpret it
340 LogInfo("Result in cache");
341 OptionalPolicyEffect effect = exPolicyResult->policyResult.getEffect();
343 // PolicyDecision is UNDETERMINED or NOT_APPLICABLE
346 } else if (*effect == PolicyEffect::DENY) {
350 } else if (*effect == PolicyEffect::PERMIT) {
352 if (m_grantedDevCaps->find(
353 DPL::FromASCIIString(request.getResourceId()))
354 != m_grantedDevCaps->end())
360 // Check for cached popup response
361 LogInfo("Checking cached popup response");
362 AceCachedPromptResult promptCached =
363 getCachedPromptResult(ace_request.widgetHandle,
364 exPolicyResult->ruleId,
365 ace_request.sessionId);
366 if (promptCached == AceCachedPromptResult::PERMIT) {
367 // Granted by previous popup
368 LogDebug("Cache found OK");
369 if (m_grantedDevCaps->find(
370 DPL::FromASCIIString(request.getResourceId()))
371 != m_grantedDevCaps->end())
373 LogDebug("SMACK given previously");
376 if (*effect != PolicyEffect::PROMPT_BLANKET) {
377 // This should not happen.
378 LogDebug("This should not happen.");
382 if (!validatePopupResponse(ace_request,
384 LogDebug("Daemon has not validated response.");
388 // Access granted, move on to next request
389 LogDebug("SMACK granted, all OK");
390 m_grantedDevCaps->insert(
391 DPL::FromASCIIString(
392 request.getResourceId()));
397 if (promptCached == AceCachedPromptResult::DENY) {
398 // Access denied by earlier popup
402 if (promptCached == AceCachedPromptResult::ASK_POPUP) {
411 // IPC to security daemon
412 // here we must check if we have a SMACK permission for
413 // the device cap requested
414 LogInfo("Asking security daemon");
415 int serializedPolicyResult = 0;
417 m_communicationClient->call(WrtSecurity::AceServerApi::CHECK_ACCESS_METHOD(),
418 ace_request.widgetHandle,
419 request.getSubjectId(),
420 request.getResourceId(),
421 request.getFunctionParam().getKeys(),
422 request.getFunctionParam().getValues(),
423 ace_request.sessionId,
424 &serializedPolicyResult);
425 } Catch (WrtSecurity::Communication::Client::Exception::SecurityCommunicationClientException) {
426 ReThrowMsg(AceThinClient::Exception::AceThinClientException,
427 "Failed to call security daemon");
429 PolicyResult policyResult = PolicyResult::
430 deserialize(serializedPolicyResult);
431 OptionalPolicyEffect effect = policyResult.getEffect();
433 // PolicyDecision is UNDETERMINED or NOT_APPLICABLE
437 if (*effect == PolicyEffect::DENY) {
442 if (*effect == PolicyEffect::PERMIT) {
443 // Access granted, move on to next request
444 m_grantedDevCaps->insert(
445 DPL::FromASCIIString(request.getResourceId()));
449 // Policy says: ask user - setup popup kind
455 result = askUser(popupType, ace_request, request);
459 LogInfo("Result: " << (result ? "GRANTED" : "DENIED"));
463 bool AceThinClientImpl::askUser(PolicyEffect popupType,
464 const AceRequest& ace_request,
465 const AceBasicRequest& request)
467 LogInfo("Asking popup");
468 Assert(NULL != popup_func);
470 const AceFunctionParam& fParam = request.getFunctionParam();
471 AceParamKeys keys = fParam.getKeys();
472 AceParamValues values = fParam.getValues();
474 ace_popup_t ace_popup_type;
475 ace_resource_t resource = const_cast<ace_session_id_t>(
476 request.getResourceId().c_str());
477 ace_session_id_t session = const_cast<ace_session_id_t>(
478 ace_request.sessionId.c_str());;
479 ace_param_list_t parameters;
480 ace_widget_handle_t handle = ace_request.widgetHandle;
482 parameters.count = keys.size();
483 parameters.items = new ace_param_t[parameters.count];
485 for (i = 0; i < parameters.count; ++i) {
486 parameters.items[i].name =
487 const_cast<ace_string_t>(keys[i].c_str());
488 parameters.items[i].value =
489 const_cast<ace_string_t>(values[i].c_str());
493 case PolicyEffect::PROMPT_ONESHOT: {
494 ace_popup_type = ACE_ONESHOT;
496 case PolicyEffect::PROMPT_SESSION: {
497 ace_popup_type = ACE_SESSION;
499 case PolicyEffect::PROMPT_BLANKET: {
500 ace_popup_type = ACE_BLANKET;
503 LogError("Unknown popup type passed!");
504 LogError("Maybe effect isn't a popup?");
505 LogError("Effect number is: " << static_cast<int>(popupType));
509 ace_bool_t answer = ACE_FALSE;
510 ace_return_t ret = popup_func(ace_popup_type,
517 delete [] parameters.items;
520 LogError("Error in popup handler");
524 if (ACE_TRUE == answer) {
525 m_grantedDevCaps->insert(
526 DPL::FromASCIIString(request.getResourceId()));
533 bool AceThinClientImpl::validatePopupResponse(
534 const AceRequest& ace_request,
535 const AceBasicRequest& request,
537 Prompt::Validity validity
540 bool response = false;
542 m_popupValidationClient->call(
543 WrtSecurity::PopupServerApi::VALIDATION_METHOD(),
545 static_cast<int>(validity),
546 ace_request.widgetHandle,
547 request.getSubjectId(),
548 request.getResourceId(),
549 request.getFunctionParam().getKeys(),
550 request.getFunctionParam().getValues(),
551 ace_request.sessionId,
553 } Catch (WrtSecurity::Communication::Client::Exception::SecurityCommunicationClientException) {
554 ReThrowMsg(AceThinClient::Exception::AceThinClientException,
555 "Failed to call security daemon");
560 AcePreference AceThinClientImpl::getWidgetResourcePreference (
561 const AceResource& resource,
562 const AceWidgetHandle& handle) const
564 return toAcePreference(
565 AceDB::AceDAOReadOnly::getWidgetDevCapSetting(resource, handle));
568 AceResourcesPreferences* AceThinClientImpl::getGlobalResourcesPreferences()
571 AceDB::PreferenceTypesMap globalSettingsMap;
572 AceResourcesPreferences* acePreferences = new AceResourcesPreferences();
573 AceDB::AceDAOReadOnly::getDevCapSettings(&globalSettingsMap);
574 FOREACH(it, globalSettingsMap) {
575 acePreferences->insert(
576 AceResurcePreference((*it).first,
577 toAcePreference((*it).second)));
579 return acePreferences;
582 AceSubject AceThinClientImpl::getSubjectForHandle(AceWidgetHandle handle) const
586 return AceDB::AceDAOReadOnly::getGUID(handle);
588 catch (AceDB::AceDAOReadOnly::Exception::DatabaseError& /*ex*/)
590 LogError("Couldn't find GIUD for handle " << handle);
595 AceCachedPromptResult AceThinClientImpl::getCachedPromptResult(
596 WidgetHandle widgetHandle,
598 const AceSessionId& sessionId) const
600 OptionalCachedPromptDecision promptDecision =
601 AceDB::AceDAOReadOnly::getPromptDecision(
604 if (!promptDecision) {
605 LogDebug("No cache");
606 return AceCachedPromptResult::ASK_POPUP;
608 // These should not be stored in DB!
609 Assert(PromptDecision::ALLOW_THIS_TIME
610 != (*promptDecision).decision);
611 Assert(PromptDecision::DENY_THIS_TIME
612 != (*promptDecision).decision);
613 if ((*promptDecision).decision ==
614 PromptDecision::ALLOW_ALWAYS) {
615 // Access granted via earlier popup
616 LogDebug("ALLOW_ALWAYS");
617 return AceCachedPromptResult::PERMIT;
619 if ((*promptDecision).decision ==
620 PromptDecision::DENY_ALWAYS) {
621 LogDebug("DENY_ALWAYS");
622 // Access denied via earlier popup
623 return AceCachedPromptResult::DENY;
625 // Only thing left is per session prompts
626 if (!(*promptDecision).session) {
627 LogDebug("NO SESSION");
628 return AceCachedPromptResult::ASK_POPUP;
630 AceSessionId cachedSessionId = DPL::ToUTF8String(*((*promptDecision).session));
631 if ((*promptDecision).decision ==
632 PromptDecision::ALLOW_FOR_SESSION) {
633 if (cachedSessionId == sessionId) {
634 // Access granted for this session.
635 LogDebug("SESSION OK, PERMIT");
636 return AceCachedPromptResult::PERMIT;
638 LogDebug("SESSION NOT OK, ASKING");
639 return AceCachedPromptResult::ASK_POPUP;
642 if ((*promptDecision).decision ==
643 PromptDecision::DENY_FOR_SESSION) {
644 if (cachedSessionId == sessionId) {
645 // Access denied for this session.
646 LogDebug("SESSION OK, DENY");
647 return AceCachedPromptResult::DENY;
649 LogDebug("SESSION NOT OK, ASKING");
650 return AceCachedPromptResult::ASK_POPUP;
654 LogDebug("NO RESULT, ASKING");
655 return AceCachedPromptResult::ASK_POPUP;
660 bool AceThinClient::checkFunctionCall(
661 const AceRequest& ace_request) const
663 return m_impl->checkFunctionCall(ace_request);
666 bool AceThinClient::checkPrivacy(
667 const AceRequest& ace_request) const
669 return m_impl->checkPrivacy(ace_request);
672 AcePreference AceThinClient::getWidgetResourcePreference(
673 const AceResource& resource,
674 const AceWidgetHandle& handle) const
676 return m_impl->getWidgetResourcePreference(
680 AceResourcesPreferences* AceThinClient::getGlobalResourcesPreferences()
683 return m_impl->getGlobalResourcesPreferences();
686 AceThinClient::AceThinClient()
688 m_impl = new AceThinClientImpl();
691 AceThinClient::~AceThinClient()
693 Assert(NULL != m_impl);
697 bool AceThinClient::isInitialized() const
699 return NULL != m_impl && m_impl->isInitialized();
703 } // namespace AceClient