tizen 2.3 release
[framework/web/wearable/wrt-security.git] / src / services / ace / logic / security_logic.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 /**
17  * This class simply redirects the access requests to access control engine.
18  * The aim is to hide access control engine specific details from WRT modules.
19  * It also implements WRT_INTERFACE.h interfaces, so that ACE could access
20  * WRT specific and other information during the decision making.
21  *
22  * @file    security_controller.h
23  # @author  Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
24  * @author  Ming Jin(ming79.jin@samsung.com)
25  * @author  Piotr Kozbial (p.kozbial@samsung.com)
26  * @version 1.0
27  * @brief   Header file for security logic
28  */
29
30 #include <security_logic.h>
31 #include <attribute_facade.h>
32 #ifdef WRT_SMACK_ENABLED
33 #include <privilege-control.h>
34 #endif
35 #include <ace-dao-rw/AceDAO.h>
36 #include <ace-dao-ro/AceDAOConversions.h>
37 #include <ace/PolicyInformationPoint.h>
38 #include <ace/PromptDecision.h>
39 #include <dpl/log/log.h>
40 #include <dpl/foreach.h>
41
42 namespace {
43
44 Request::ApplicationType getAppType(const Request *request) {
45     AceDB::AppTypes appType =
46         AceDB::AceDAOReadOnly::getWidgetType(request->getWidgetHandle());
47     switch (appType) {
48     case AceDB::AppTypes::Tizen:
49         LogDebug("==== Found Tizen application. ====");
50         return Request::APP_TYPE_TIZEN;
51     case AceDB::AppTypes::WAC20:
52         LogDebug("==== Found Wac20 application. ====");
53         return Request::APP_TYPE_WAC20;
54     default:
55         LogDebug("==== Unknown application type. ====");
56     }
57     return Request::APP_TYPE_UNKNOWN;
58 }
59
60 } // anonymous namespace
61
62 void SecurityLogic::initialize() {
63     AceDB::AceDAO::attachToThreadRW();
64     m_policyEnforcementPoint.initialize(new WebRuntimeImpl(),
65                                         new ResourceInformationImpl(),
66                                         new OperationSystemImpl());
67 }
68
69 void SecurityLogic::terminate() {
70     m_policyEnforcementPoint.terminate();
71     AceDB::AceDAO::detachFromThread();
72 }
73
74
75 void SecurityLogic::grantPlatformAccess(const Request& request)
76 {
77     (void)request;
78 #ifdef WRT_SMACK_ENABLED
79     try {
80         unsigned long long id =
81             static_cast<unsigned long long>(request.getWidgetHandle());
82         Request::DeviceCapabilitySet dc = request.getDeviceCapabilitySet();
83
84         size_t i,size = dc.size();
85         std::unique_ptr<const char*[]> array(new const char*[size+1]);
86
87         array[size] = NULL;
88         auto it = dc.begin();
89
90         for(i=0; (i<size) && (it!=dc.end()); ++i,++it) {
91             array[i] = it->c_str();
92         }
93         int ret = wrt_permissions_add(id, array.get());
94         if (PC_OPERATION_SUCCESS != ret) {
95             LogError("smack rules couldn't be granted");
96         }
97     } catch (std::bad_alloc&) {
98         LogError("smack rules couldn't be granted: memory allocation failed");
99     }
100 #endif
101 }
102
103 PolicyResult SecurityLogic::checkFunctionCall(Request* request)
104 {
105     Assert(NULL != request);
106
107     LogDebug("=== Check widget existance ===");
108     Try {
109         request->setAppType(getAppType(request));
110     } Catch (AceDB::AceDAOReadOnly::Exception::DatabaseError) {
111         LogError("==== Couldn't find widget for handle: " <<
112             request->getWidgetHandle() << ". Access denied. ====");
113         return PolicyEffect::DENY;
114     }
115
116     PolicyResult aceResult = m_policyEnforcementPoint.check(*request).policyResult;
117
118     if (aceResult == PolicyEffect::PERMIT) {
119         grantPlatformAccess(*request);
120         return PolicyEffect::PERMIT;
121     } else if (aceResult == PolicyEffect::PROMPT_ONESHOT ||
122                aceResult == PolicyEffect::PROMPT_SESSION ||
123                aceResult == PolicyEffect::PROMPT_BLANKET ||
124                aceResult == PolicyDecision::NOT_APPLICABLE ||
125                aceResult == PolicyResult::UNDETERMINED)
126     {
127         // TODO: check stored user answers!!!
128         // if necessary, grant SMACK rules
129         // return appropriately - the following is a dummy:
130         return aceResult;
131     } else {
132         return PolicyEffect::DENY;
133     }
134 }
135
136 PolicyResult SecurityLogic::checkFunctionCall(Request* request, const std::string &sessionId)
137 {
138     Assert(NULL != request);
139     LogDebug("=== Check existance of widget === ");
140     Try {
141         request->setAppType(getAppType(request));
142     } Catch (AceDB::AceDAOReadOnly::Exception::DatabaseError) {
143         LogError("==== Couldn't find widget for handle: " <<
144             request->getWidgetHandle() << ". Access denied. ====");
145         return PolicyEffect::DENY;
146     }
147
148     ExtendedPolicyResult exAceResult = m_policyEnforcementPoint.check(*request);
149     PolicyResult aceResult = exAceResult.policyResult;
150
151     LogDebug("Result returned by policy " << aceResult << ". RuleID: " << exAceResult.ruleId);
152
153     if (aceResult == PolicyEffect::PERMIT) {
154         LogDebug("Grant access.");
155         grantPlatformAccess(*request);
156         return PolicyEffect::PERMIT;
157     }
158
159     if (aceResult == PolicyEffect::PROMPT_ONESHOT ||
160         aceResult == PolicyEffect::DENY)
161     {
162         return aceResult;
163     }
164
165     OptionalCachedPromptDecision decision = AceDB::AceDAOReadOnly::getPromptDecision(
166         request->getWidgetHandle(),
167         exAceResult.ruleId);
168
169     if (!decision) {
170         LogDebug("No CachedPromptDecision found.");
171         return aceResult;
172     }
173
174     if (aceResult == PolicyEffect::PROMPT_BLANKET) {
175         if (decision->decision == PromptDecision::ALLOW_ALWAYS) {
176             LogDebug("Found user decision. Result changed to PERMIT. Access granted");
177             grantPlatformAccess(*request);
178             return PolicyEffect::PERMIT;
179         }
180         if (decision->decision == PromptDecision::DENY_ALWAYS) {
181             LogDebug("Found user decision. Result changed to DENY.");
182             return PolicyEffect::DENY;
183         }
184         if (decision->decision == PromptDecision::ALLOW_FOR_SESSION
185             && !!(decision->session)
186             && sessionId == DPL::ToUTF8String(*(decision->session)))
187         {
188             LogDebug("Result changed to PERMIT. Access granted.");
189             grantPlatformAccess(*request);
190             return PolicyEffect::PERMIT;
191         }
192         if (decision->decision == PromptDecision::DENY_FOR_SESSION
193             && !!(decision->session)
194             && sessionId == DPL::ToUTF8String(*(decision->session)))
195         {
196             LogDebug("Found user decision. Result changed to DENY.");
197             return PolicyEffect::DENY;
198         }
199         return aceResult;
200     }
201
202     if (aceResult == PolicyEffect::PROMPT_SESSION) {
203         if (decision->decision == PromptDecision::ALLOW_FOR_SESSION
204             && !!(decision->session)
205             && sessionId == DPL::ToUTF8String(*(decision->session)))
206         {
207             LogDebug("Found user decision. Result changed to PERMIT. Access granted.");
208             grantPlatformAccess(*request);
209             return PolicyEffect::PERMIT;
210         }
211         if (decision->decision == PromptDecision::DENY_FOR_SESSION
212             && !!(decision->session)
213             && sessionId == DPL::ToUTF8String(*(decision->session)))
214         {
215             LogDebug("Found user decision. Result changed to DENY.");
216             return PolicyEffect::DENY;
217         }
218         return aceResult;
219     }
220
221     // This should not happend - all PolicyEffect values were supported before.
222     // This mean that someone has modyfied PolicyEffect enum. SPANK SPANK SPANK
223     LogError("Unsupported PolicyEffect!");
224     return PolicyEffect::DENY;
225 }
226
227 void SecurityLogic::validatePopupResponse(Request* request,
228                                           bool allowed,
229                                           Prompt::Validity validity,
230                                           const std::string& sessionId,
231                                           bool* retValue)
232 {
233     Assert(NULL != retValue);
234     Assert(NULL != request);
235
236     LogDebug("Start");
237     LogDebug("User answered: " << allowed << " with validity: " << validity);
238     LogDebug("Check widget existance");
239     Try {
240         request->setAppType(getAppType(request));
241     } Catch (AceDB::AceDAOReadOnly::Exception::DatabaseError) {
242         LogError("==== Couldn't find widget for handle: " <<
243             request->getWidgetHandle() << ". Access denied. ====");
244         retValue = false;
245         return;
246     }
247
248     *retValue = false;
249     OptionalExtendedPolicyResult extendedAceResult =
250         m_policyEnforcementPoint.checkFromCache(*request);
251     if (!extendedAceResult) {
252         LogDebug("No cached policy result - but it should be here");
253         LogDebug("returning " << *retValue);
254         return;
255     }
256
257     PolicyResult aceResult = extendedAceResult->policyResult;
258     if (aceResult == PolicyEffect::DENY) {
259         LogDebug("returning " << *retValue);
260         return;
261     }
262     if (aceResult == PolicyEffect::PERMIT) {
263         // TODO  we were asked for prompt validation
264         // but we got that no prompt should be opened - is this OK?
265         // (this is on the diagram in wiki)
266         *retValue = true;
267     } else if (aceResult == PolicyEffect::PROMPT_ONESHOT ||
268                aceResult == PolicyEffect::PROMPT_SESSION ||
269                aceResult == PolicyEffect::PROMPT_BLANKET)
270     {
271         Request::DeviceCapabilitySet devCaps =
272                 request->getDeviceCapabilitySet();
273
274         FOREACH (it, devCaps) {
275             Request::DeviceCapability resourceId = *it;
276             LogDebug("Recheck: " << *it);
277             // 1) check if per-widget settings permit
278             AceDB::PreferenceTypes wgtPref =
279                 AceDB::AceDAOReadOnly::getWidgetDevCapSetting(
280                     resourceId,
281                     request->getWidgetHandle());
282             if (AceDB::PreferenceTypes::PREFERENCE_DENY == wgtPref) {
283                 LogDebug("returning " << *retValue);
284                 return;
285             }
286             // 2) check if per-dev-cap settings permit
287             AceDB::PreferenceTypes resPerf =
288                 AceDB::AceDAOReadOnly::getDevCapSetting(resourceId);
289             if (AceDB::PreferenceTypes::PREFERENCE_DENY == resPerf) {
290                 LogDebug("returning " << *retValue);
291                 return;
292             }
293
294             // 3) check for stored propmt answer - should not be there
295             // TODO  - is this check necessary?
296             AceDB::BaseAttributeSet attributes;
297             AceDB::AceDAOReadOnly::getAttributes(&attributes);
298             Request req(request->getWidgetHandle(),
299                         request->getExecutionPhase());
300             req.addDeviceCapability(resourceId);
301             PolicyInformationPoint *pip =
302                 m_policyEnforcementPoint.getPip();
303
304             Assert(NULL != pip);
305
306             pip->getAttributesValues(&req, &attributes);
307             auto attrHash = AceDB::AceDaoConversions::convertToHash(attributes);
308
309             // 4) validate consistency of answer with policy result
310             Prompt::Validity clampedValidity =
311                     clampPromptValidity(validity, *(aceResult.getEffect()));
312
313             // 5) store answer in database if appropriate
314             // TODO  how about userParam? sessionId?
315             DPL::String userParam = DPL::FromUTF8String(sessionId);
316             boost::optional<DPL::String> sessionOptional = DPL::FromUTF8String(sessionId);
317
318             switch (clampedValidity) {
319             case Prompt::Validity::ALWAYS: {
320                 AceDB::AceDAO::setPromptDecision(
321                     request->getWidgetHandle(),
322                     extendedAceResult->ruleId,
323                     sessionOptional,
324                     allowed ?
325                     PromptDecision::ALLOW_ALWAYS :
326                     PromptDecision::DENY_ALWAYS);
327                 break; }
328             case Prompt::Validity::SESSION: {
329                 AceDB::AceDAO::setPromptDecision(
330                     request->getWidgetHandle(),
331                     extendedAceResult->ruleId,
332                     sessionOptional,
333                     allowed ?
334                     PromptDecision::ALLOW_FOR_SESSION :
335                     PromptDecision::DENY_FOR_SESSION);
336                 break; }
337
338             case Prompt::Validity::ONCE: {
339                 LogInfo("Validity ONCE, not saving prompt decision to cache");
340                 break; }
341             }
342
343         }
344         // access granted!
345         *retValue = allowed;
346     }
347     if (*retValue) {
348         // 6) grant smack label if not granted yet
349         grantPlatformAccess(*request);
350     }
351     LogDebug("Finish");
352     LogDebug("returning " << *retValue);
353 }
354
355 void SecurityLogic::updatePolicy()
356 {
357     LogDebug("SecurityLogic::updatePolicy");
358     m_policyEnforcementPoint.updatePolicy();
359 }
360
361 Prompt::Validity SecurityLogic::clampPromptValidity(
362         Prompt::Validity validity,
363         PolicyEffect effect)
364 {
365     switch (effect) {
366     case PolicyEffect::PROMPT_BLANKET: {
367         return validity; }
368     case PolicyEffect::PROMPT_SESSION: {
369         if (Prompt::Validity::ALWAYS == validity) {
370             LogInfo("ALWAYS returned from prompt in PROMPT_SESSION");
371             return Prompt::Validity::SESSION;
372         }
373         return validity; }
374     case PolicyEffect::PROMPT_ONESHOT: {
375         if (Prompt::Validity::ONCE != validity) {
376             LogInfo("Not ONCE returned from prompt in PROMPT_ONESHOT");
377         }
378         return Prompt::Validity::ONCE; }
379     case PolicyEffect::DENY:
380     case PolicyEffect::PERMIT:
381     default: {// other options - should not happen
382         LogError("This kind of policy effect does not deal with prompts");
383         return Prompt::Validity::ONCE;  }
384     }
385 }
386