48d7f8efe6613b8b3cedcc22163e810a40ba5c20
[framework/security/security-server.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
41 namespace {
42
43 Request::ApplicationType getAppType(const Request *request) {
44     AceDB::AppTypes appType =
45         AceDB::AceDAOReadOnly::getWidgetType(request->getWidgetHandle());
46     switch (appType) {
47     case AceDB::AppTypes::Tizen:
48         LogDebug("==== Found Tizen application. ====");
49         return Request::APP_TYPE_TIZEN;
50     case AceDB::AppTypes::WAC20:
51         LogDebug("==== Found Wac20 application. ====");
52         return Request::APP_TYPE_WAC20;
53     default:
54         LogDebug("==== Unknown application type. ====");
55     }
56     return Request::APP_TYPE_UNKNOWN;
57 }
58
59 } // anonymous namespace
60
61 void SecurityLogic::initialize() {
62     AceDB::AceDAO::attachToThreadRW();
63     m_policyEnforcementPoint.initialize(new WebRuntimeImpl(),
64                                         new ResourceInformationImpl(),
65                                         new OperationSystemImpl());
66 }
67
68 void SecurityLogic::terminate() {
69     m_policyEnforcementPoint.terminate();
70     AceDB::AceDAO::detachFromThread();
71 }
72
73
74 void SecurityLogic::grantPlatformAccess(const Request& request)
75 {
76     (void)request;
77 #ifdef WRT_SMACK_ENABLED
78     try {
79         unsigned long long id =
80             static_cast<unsigned long long>(request.getWidgetHandle());
81         Request::DeviceCapabilitySet dc = request.getDeviceCapabilitySet();
82
83         size_t i,size = dc.size();
84         std::unique_ptr<const char*[]> array(new const char*[size+1]);
85
86         array[size] = NULL;
87         auto it = dc.begin();
88
89         for(i=0; (i<size) && (it!=dc.end()); ++i,++it) {
90             array[i] = it->c_str();
91         }
92         int ret = wrt_permissions_add(id, array.get());
93         if (PC_OPERATION_SUCCESS != ret) {
94             LogError("smack rules couldn't be granted");
95         }
96     } catch (std::bad_alloc&) {
97         LogError("smack rules couldn't be granted: memory allocation failed");
98     }
99 #endif
100 }
101
102 PolicyResult SecurityLogic::checkFunctionCall(Request* request)
103 {
104     Assert(NULL != request);
105
106     LogDebug("=== Check widget existance ===");
107     Try {
108         request->setAppType(getAppType(request));
109     } Catch (AceDB::AceDAOReadOnly::Exception::DatabaseError) {
110         LogError("==== Couldn't find widget for handle: " <<
111             request->getWidgetHandle() << ". Access denied. ====");
112         return PolicyEffect::DENY;
113     }
114
115     PolicyResult aceResult = m_policyEnforcementPoint.check(*request).policyResult;
116
117     if (aceResult == PolicyEffect::PERMIT) {
118         grantPlatformAccess(*request);
119         return PolicyEffect::PERMIT;
120     } else if (aceResult == PolicyEffect::PROMPT_ONESHOT ||
121                aceResult == PolicyEffect::PROMPT_SESSION ||
122                aceResult == PolicyEffect::PROMPT_BLANKET ||
123                aceResult == PolicyDecision::NOT_APPLICABLE ||
124                aceResult == PolicyResult::UNDETERMINED)
125     {
126         // TODO: check stored user answers!!!
127         // if necessary, grant SMACK rules
128         // return appropriately - the following is a dummy:
129         return aceResult;
130     } else {
131         return PolicyEffect::DENY;
132     }
133 }
134
135 PolicyResult SecurityLogic::checkFunctionCall(Request* request, const std::string &sessionId)
136 {
137     Assert(NULL != request);
138     LogDebug("=== Check existance of widget === ");
139     Try {
140         request->setAppType(getAppType(request));
141     } Catch (AceDB::AceDAOReadOnly::Exception::DatabaseError) {
142         LogError("==== Couldn't find widget for handle: " <<
143             request->getWidgetHandle() << ". Access denied. ====");
144         return PolicyEffect::DENY;
145     }
146
147     ExtendedPolicyResult exAceResult = m_policyEnforcementPoint.check(*request);
148     PolicyResult aceResult = exAceResult.policyResult;
149
150     LogDebug("Result returned by policy " << aceResult << ". RuleID: " << exAceResult.ruleId);
151
152     if (aceResult == PolicyEffect::PERMIT) {
153         LogDebug("Grant access.");
154         grantPlatformAccess(*request);
155         return PolicyEffect::PERMIT;
156     }
157
158     if (aceResult == PolicyEffect::PROMPT_ONESHOT ||
159         aceResult == PolicyEffect::DENY)
160     {
161         return aceResult;
162     }
163
164     OptionalCachedPromptDecision decision = AceDB::AceDAOReadOnly::getPromptDecision(
165         request->getWidgetHandle(),
166         exAceResult.ruleId);
167
168     if (decision.IsNull()) {
169         LogDebug("No CachedPromptDecision found.");
170         return aceResult;
171     }
172
173     if (aceResult == PolicyEffect::PROMPT_BLANKET) {
174         if (decision->decision == PromptDecision::ALLOW_ALWAYS) {
175             LogDebug("Found user decision. Result changed to PERMIT. Access granted");
176             grantPlatformAccess(*request);
177             return PolicyEffect::PERMIT;
178         }
179         if (decision->decision == PromptDecision::DENY_ALWAYS) {
180             LogDebug("Found user decision. Result changed to DENY.");
181             return PolicyEffect::DENY;
182         }
183         if (decision->decision == PromptDecision::ALLOW_FOR_SESSION
184             && !(decision->session.IsNull())
185             && sessionId == DPL::ToUTF8String(*(decision->session)))
186         {
187             LogDebug("Result changed to PERMIT. Access granted.");
188             grantPlatformAccess(*request);
189             return PolicyEffect::PERMIT;
190         }
191         if (decision->decision == PromptDecision::DENY_FOR_SESSION
192             && !(decision->session.IsNull())
193             && sessionId == DPL::ToUTF8String(*(decision->session)))
194         {
195             LogDebug("Found user decision. Result changed to DENY.");
196             return PolicyEffect::DENY;
197         }
198         return aceResult;
199     }
200
201     if (aceResult == PolicyEffect::PROMPT_SESSION) {
202         if (decision->decision == PromptDecision::ALLOW_FOR_SESSION
203             && !(decision->session.IsNull())
204             && sessionId == DPL::ToUTF8String(*(decision->session)))
205         {
206             LogDebug("Found user decision. Result changed to PERMIT. Access granted.");
207             grantPlatformAccess(*request);
208             return PolicyEffect::PERMIT;
209         }
210         if (decision->decision == PromptDecision::DENY_FOR_SESSION
211             && !(decision->session.IsNull())
212             && sessionId == DPL::ToUTF8String(*(decision->session)))
213         {
214             LogDebug("Found user decision. Result changed to DENY.");
215             return PolicyEffect::DENY;
216         }
217         return aceResult;
218     }
219
220     // This should not happend - all PolicyEffect values were supported before.
221     // This mean that someone has modyfied PolicyEffect enum. SPANK SPANK SPANK
222     LogError("Unsupported PolicyEffect!");
223     return PolicyEffect::DENY;
224 }
225
226 void SecurityLogic::validatePopupResponse(Request* request,
227                                           bool allowed,
228                                           Prompt::Validity validity,
229                                           const std::string& sessionId,
230                                           bool* retValue)
231 {
232     Assert(NULL != retValue);
233     Assert(NULL != request);
234
235     LogDebug("Start");
236     LogDebug("User answered: " << allowed << " with validity: " << validity);
237     LogDebug("Check widget existance");
238     Try {
239         request->setAppType(getAppType(request));
240     } Catch (AceDB::AceDAOReadOnly::Exception::DatabaseError) {
241         LogError("==== Couldn't find widget for handle: " <<
242             request->getWidgetHandle() << ". Access denied. ====");
243         retValue = false;
244         return;
245     }
246
247     *retValue = false;
248     OptionalExtendedPolicyResult extendedAceResult =
249         m_policyEnforcementPoint.checkFromCache(*request);
250     if (extendedAceResult.IsNull()) {
251         LogDebug("No cached policy result - but it should be here");
252         LogDebug("returning " << *retValue);
253         return;
254     }
255
256     PolicyResult aceResult = extendedAceResult->policyResult;
257     if (aceResult == PolicyEffect::DENY) {
258         LogDebug("returning " << *retValue);
259         return;
260     }
261     if (aceResult == PolicyEffect::PERMIT) {
262         // TODO  we were asked for prompt validation
263         // but we got that no prompt should be opened - is this OK?
264         // (this is on the diagram in wiki)
265         *retValue = true;
266     } else if (aceResult == PolicyEffect::PROMPT_ONESHOT ||
267                aceResult == PolicyEffect::PROMPT_SESSION ||
268                aceResult == PolicyEffect::PROMPT_BLANKET)
269     {
270         Request::DeviceCapabilitySet devCaps =
271                 request->getDeviceCapabilitySet();
272
273         FOREACH (it, devCaps) {
274             Request::DeviceCapability resourceId = *it;
275             LogDebug("Recheck: " << *it);
276             // 1) check if per-widget settings permit
277             AceDB::PreferenceTypes wgtPref =
278                 AceDB::AceDAOReadOnly::getWidgetDevCapSetting(
279                     resourceId,
280                     request->getWidgetHandle());
281             if (AceDB::PreferenceTypes::PREFERENCE_DENY == wgtPref) {
282                 LogDebug("returning " << *retValue);
283                 return;
284             }
285             // 2) check if per-dev-cap settings permit
286             AceDB::PreferenceTypes resPerf =
287                 AceDB::AceDAOReadOnly::getDevCapSetting(resourceId);
288             if (AceDB::PreferenceTypes::PREFERENCE_DENY == resPerf) {
289                 LogDebug("returning " << *retValue);
290                 return;
291             }
292
293             // 3) check for stored propmt answer - should not be there
294             // TODO  - is this check necessary?
295             AceDB::BaseAttributeSet attributes;
296             AceDB::AceDAOReadOnly::getAttributes(&attributes);
297             Request req(request->getWidgetHandle(),
298                         request->getExecutionPhase());
299             req.addDeviceCapability(resourceId);
300             PolicyInformationPoint *pip =
301                 m_policyEnforcementPoint.getPip();
302
303             Assert(NULL != pip);
304
305             pip->getAttributesValues(&req, &attributes);
306             auto attrHash = AceDB::AceDaoConversions::convertToHash(attributes);
307
308             // 4) validate consistency of answer with policy result
309             Prompt::Validity clampedValidity =
310                     clampPromptValidity(validity, *(aceResult.getEffect()));
311
312             // 5) store answer in database if appropriate
313             // TODO  how about userParam? sessionId?
314             DPL::String userParam = DPL::FromUTF8String(sessionId);
315             DPL::OptionalString sessionOptional =
316                 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