4d4ec750fa4e56a86ab8ec847282073dd2ee5e14
[platform/core/security/security-manager.git] / src / common / cynara.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Rafal Krypa <r.krypa@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18 /*
19  * @file        cynara.cpp
20  * @author      Rafal Krypa <r.krypa@samsung.com>
21  * @brief       Wrapper class for Cynara interface
22  */
23
24 #include <cstring>
25 #include "cynara.h"
26
27 #include <dpl/log/log.h>
28
29 namespace SecurityManager {
30
31 /**
32  * Rules for apps and users are organized into set of buckets stored in Cynara.
33  * Bucket is set of rules (app, uid, privilege) -> (DENY, ALLOW, BUCKET, ...).
34  *  |------------------------|
35  *  |      <<allow>>         |
36  *  |   PRIVACY_MANAGER      |
37  *  |------------------------|
38  *  |  A    U   P      policy|
39  *  |------------------------|
40  *  | app1 uid1 priv1  DENY  |
41  *  |  *   uid2 priv2  DENY  |
42  *  |  * * *      Bucket:MAIN|
43  *  |------------------------|
44  *
45  * For details about buckets see Cynara documentation.
46  *
47  * Security Manager currently defines 8 buckets:
48  * - PRIVACY_MANAGER - first bucket during search (which is actually default bucket
49  *   with empty string as id). If user specifies his preference then required rule
50  *   is created here.
51  * - MAIN            - holds rules denied by manufacturer, redirects to MANIFESTS
52  *   bucket and holds entries for each user pointing to User Type
53  *   specific buckets
54  * - MANIFESTS       - stores rules needed by installed apps (from package
55  *   manifest)
56  * - USER_TYPE_ADMIN
57  * - USER_TYPE_SYSTEM
58  * - USER_TYPE_NORMAL
59  * - USER_TYPE_GUEST - they store privileges from templates for apropriate
60  *   user type. ALLOW rules only.
61  * - ADMIN           - stores custom rules introduced by device administrator.
62  *   Ignored if no matching rule found.
63  *
64  * Below is basic layout of buckets:
65  *
66  *  |------------------------|
67  *  |      <<allow>>         |
68  *  |   PRIVACY_MANAGER      |
69  *  |                        |
70  *  |  * * *      Bucket:MAIN|                         |------------------|
71  *  |------------------------|                         |      <<deny>>    |
72  *             |                                    |->|     MANIFESTS    |
73  *             -----------------                    |  |                  |
74  *                             |                    |  |------------------|
75  *                             V                    |
76  *                     |------------------------|   |
77  *                     |       <<deny>>         |---|
78  *                     |         MAIN           |
79  * |---------------|   |                        |     |-------------------|
80  * |    <<deny>>   |<--| * * *  Bucket:MANIFESTS|---->|      <<deny>>     |
81  * | USER_TYPE_SYST|   |------------------------|     |  USER_TYPE_NORMAL |
82  * |               |        |              |          |                   |
83  * |---------------|        |              |          |-------------------|
84  *        |                 |              |                    |
85  *        |                 V              V                    |
86  *        |      |---------------|      |---------------|       |
87  *        |      |    <<deny>>   |      |    <<deny>>   |       |
88  *        |      |USER_TYPE_GUEST|      |USER_TYPE_ADMIN|       |
89  *        |      |               |      |               |       |
90  *        |      |---------------|      |---------------|       |
91  *        |              |                      |               |
92  *        |              |----             -----|               |
93  *        |                  |             |                    |
94  *        |                  V             V                    |
95  *        |                |------------------|                 |
96  *        |------------->  |     <<none>>     | <---------------|
97  *                         |       ADMIN      |
98  *                         |                  |
99  *                         |------------------|
100  *
101  */
102 CynaraAdmin::BucketsMap CynaraAdmin::Buckets =
103 {
104     { Bucket::PRIVACY_MANAGER, std::string(CYNARA_ADMIN_DEFAULT_BUCKET)},
105     { Bucket::MAIN, std::string("MAIN")},
106     { Bucket::USER_TYPE_ADMIN, std::string("USER_TYPE_ADMIN")},
107     { Bucket::USER_TYPE_NORMAL, std::string("USER_TYPE_NORMAL")},
108     { Bucket::USER_TYPE_GUEST, std::string("USER_TYPE_GUEST") },
109     { Bucket::USER_TYPE_SYSTEM, std::string("USER_TYPE_SYSTEM")},
110     { Bucket::ADMIN, std::string("ADMIN")},
111     { Bucket::MANIFESTS, std::string("MANIFESTS")},
112 };
113
114
115 CynaraAdminPolicy::CynaraAdminPolicy(const std::string &client, const std::string &user,
116         const std::string &privilege, Operation operation,
117         const std::string &bucket)
118 {
119     this->client = strdup(client.c_str());
120     this->user = strdup(user.c_str());
121     this->privilege = strdup(privilege.c_str());
122     this->bucket = strdup(bucket.c_str());
123
124     if (this->bucket == nullptr || this->client == nullptr ||
125         this->user == nullptr || this->privilege == nullptr) {
126         free(this->bucket);
127         free(this->client);
128         free(this->user);
129         free(this->privilege);
130         ThrowMsg(CynaraException::OutOfMemory,
131                 std::string("Error in CynaraAdminPolicy allocation."));
132     }
133
134     this->result = static_cast<int>(operation);
135     this->result_extra = nullptr;
136 }
137
138 CynaraAdminPolicy::CynaraAdminPolicy(const std::string &client, const std::string &user,
139     const std::string &privilege, const std::string &goToBucket,
140     const std::string &bucket)
141 {
142     this->bucket = strdup(bucket.c_str());
143     this->client = strdup(client.c_str());
144     this->user = strdup(user.c_str());
145     this->privilege = strdup(privilege.c_str());
146     this->result_extra = strdup(goToBucket.c_str());
147     this->result = CYNARA_ADMIN_BUCKET;
148
149     if (this->bucket == nullptr || this->client == nullptr ||
150         this->user == nullptr || this->privilege == nullptr ||
151         this->result_extra == nullptr) {
152         free(this->bucket);
153         free(this->client);
154         free(this->user);
155         free(this->privilege);
156         free(this->result_extra);
157         ThrowMsg(CynaraException::OutOfMemory,
158                 std::string("Error in CynaraAdminPolicy allocation."));
159     }
160 }
161
162 CynaraAdminPolicy::CynaraAdminPolicy(CynaraAdminPolicy &&that)
163 {
164     bucket = that.bucket;
165     client = that.client;
166     user = that.user;
167     privilege = that.privilege;
168     result_extra = that.result_extra;
169     result = that.result;
170
171     that.bucket = nullptr;
172     that.client = nullptr;
173     that.user = nullptr;
174     that.privilege = nullptr;
175     that.result_extra = nullptr;
176 }
177
178 CynaraAdminPolicy::~CynaraAdminPolicy()
179 {
180     free(this->bucket);
181     free(this->client);
182     free(this->user);
183     free(this->privilege);
184     free(this->result_extra);
185 }
186
187 static bool checkCynaraError(int result, const std::string &msg)
188 {
189     switch (result) {
190         case CYNARA_API_SUCCESS:
191         case CYNARA_API_ACCESS_ALLOWED:
192             return true;
193         case CYNARA_API_ACCESS_DENIED:
194             return false;
195         case CYNARA_API_OUT_OF_MEMORY:
196             ThrowMsg(CynaraException::OutOfMemory, msg);
197         case CYNARA_API_INVALID_PARAM:
198             ThrowMsg(CynaraException::InvalidParam, msg);
199         case CYNARA_API_SERVICE_NOT_AVAILABLE:
200             ThrowMsg(CynaraException::ServiceNotAvailable, msg);
201         case CYNARA_API_BUCKET_NOT_FOUND:
202             ThrowMsg(CynaraException::BucketNotFound, msg);
203         default:
204             ThrowMsg(CynaraException::UnknownError, msg);
205     }
206 }
207
208 CynaraAdmin::CynaraAdmin()
209 {
210     checkCynaraError(
211         cynara_admin_initialize(&m_CynaraAdmin),
212         "Cannot connect to Cynara administrative interface.");
213 }
214
215 CynaraAdmin::~CynaraAdmin()
216 {
217     cynara_admin_finish(m_CynaraAdmin);
218 }
219
220 CynaraAdmin &CynaraAdmin::getInstance()
221 {
222     static CynaraAdmin cynaraAdmin;
223     return cynaraAdmin;
224 }
225
226 void CynaraAdmin::SetPolicies(const std::vector<CynaraAdminPolicy> &policies)
227 {
228     std::vector<const struct cynara_admin_policy *> pp_policies(policies.size() + 1);
229
230     LogDebug("Sending " << policies.size() << " policies to Cynara");
231     for (std::size_t i = 0; i < policies.size(); ++i) {
232         pp_policies[i] = static_cast<const struct cynara_admin_policy *>(&policies[i]);
233         LogDebug("policies[" << i << "] = {" <<
234             ".bucket = " << pp_policies[i]->bucket << ", " <<
235             ".client = " << pp_policies[i]->client << ", " <<
236             ".user = " << pp_policies[i]->user << ", " <<
237             ".privilege = " << pp_policies[i]->privilege << ", " <<
238             ".result = " << pp_policies[i]->result << ", " <<
239             ".result_extra = " << pp_policies[i]->result_extra << "}");
240     }
241
242     pp_policies[policies.size()] = nullptr;
243
244     checkCynaraError(
245         cynara_admin_set_policies(m_CynaraAdmin, pp_policies.data()),
246         "Error while updating Cynara policy.");
247 }
248
249 void CynaraAdmin::UpdateAppPolicy(
250     const std::string &label,
251     const std::string &user,
252     const std::vector<std::string> &oldPrivileges,
253     const std::vector<std::string> &newPrivileges)
254 {
255     std::vector<CynaraAdminPolicy> policies;
256
257     // Perform sort-merge join on oldPrivileges and newPrivileges.
258     // Assume that they are already sorted and without duplicates.
259     auto oldIter = oldPrivileges.begin();
260     auto newIter = newPrivileges.begin();
261
262     while (oldIter != oldPrivileges.end() && newIter != newPrivileges.end()) {
263         int compare = oldIter->compare(*newIter);
264         if (compare == 0) {
265             LogDebug("(user = " << user << " label = " << label << ") " <<
266                 "keeping privilege " << *newIter);
267             ++oldIter;
268             ++newIter;
269             continue;
270         } else if (compare < 0) {
271             LogDebug("(user = " << user << " label = " << label << ") " <<
272                 "removing privilege " << *oldIter);
273             policies.push_back(CynaraAdminPolicy(label, user, *oldIter,
274                     CynaraAdminPolicy::Operation::Delete,
275                     Buckets.at(Bucket::MANIFESTS)));
276             ++oldIter;
277         } else {
278             LogDebug("(user = " << user << " label = " << label << ") " <<
279                 "adding privilege " << *newIter);
280             policies.push_back(CynaraAdminPolicy(label, user, *newIter,
281                     CynaraAdminPolicy::Operation::Allow,
282                     Buckets.at(Bucket::MANIFESTS)));
283             ++newIter;
284         }
285     }
286
287     for (; oldIter != oldPrivileges.end(); ++oldIter) {
288         LogDebug("(user = " << user << " label = " << label << ") " <<
289             "removing privilege " << *oldIter);
290         policies.push_back(CynaraAdminPolicy(label, user, *oldIter,
291                     CynaraAdminPolicy::Operation::Delete,
292                     Buckets.at(Bucket::MANIFESTS)));
293     }
294
295     for (; newIter != newPrivileges.end(); ++newIter) {
296         LogDebug("(user = " << user << " label = " << label << ") " <<
297             "adding privilege " << *newIter);
298         policies.push_back(CynaraAdminPolicy(label, user, *newIter,
299                     CynaraAdminPolicy::Operation::Allow,
300                     Buckets.at(Bucket::MANIFESTS)));
301     }
302
303     SetPolicies(policies);
304 }
305
306 void CynaraAdmin::UserInit(uid_t uid, security_manager_user_type userType)
307 {
308     Bucket bucket;
309     std::vector<CynaraAdminPolicy> policies;
310
311     switch (userType) {
312         case SM_USER_TYPE_SYSTEM:
313             bucket = Bucket::USER_TYPE_SYSTEM;
314             break;
315         case SM_USER_TYPE_ADMIN:
316             bucket = Bucket::USER_TYPE_ADMIN;
317             break;
318         case SM_USER_TYPE_GUEST:
319             bucket = Bucket::USER_TYPE_GUEST;
320             break;
321         case SM_USER_TYPE_NORMAL:
322             bucket = Bucket::USER_TYPE_NORMAL;
323             break;
324         case SM_USER_TYPE_ANY:
325         case SM_USER_TYPE_NONE:
326         case SM_USER_TYPE_END:
327         default:
328             ThrowMsg(CynaraException::InvalidParam, "User type incorrect");
329     }
330
331     policies.push_back(CynaraAdminPolicy(CYNARA_ADMIN_WILDCARD,
332                                             std::to_string(static_cast<unsigned int>(uid)),
333                                             CYNARA_ADMIN_WILDCARD,
334                                             Buckets.at(bucket),
335                                             Buckets.at(Bucket::MAIN)));
336
337     CynaraAdmin::getInstance().SetPolicies(policies);
338 }
339
340 void CynaraAdmin::ListPolicies(
341     const std::string &bucketName,
342     const std::string &appId,
343     const std::string &user,
344     const std::string &privilege,
345     std::vector<CynaraAdminPolicy> &policies)
346 {
347     struct cynara_admin_policy ** pp_policies = nullptr;
348
349     checkCynaraError(
350         cynara_admin_list_policies(m_CynaraAdmin, bucketName.c_str(), appId.c_str(),
351             user.c_str(), privilege.c_str(), &pp_policies),
352         "Error while getting list of policies for bucket: " + bucketName);
353
354     for (std::size_t i = 0; pp_policies[i] != nullptr; i++) {
355         policies.push_back(std::move(*static_cast<CynaraAdminPolicy*>(pp_policies[i])));
356
357         free(pp_policies[i]);
358     }
359
360     free(pp_policies);
361
362 }
363
364 void CynaraAdmin::EmptyBucket(const std::string &bucketName, bool recursive, const std::string &client,
365     const std::string &user, const std::string &privilege)
366 {
367     checkCynaraError(
368         cynara_admin_erase(m_CynaraAdmin, bucketName.c_str(), static_cast<int>(recursive),
369             client.c_str(), user.c_str(), privilege.c_str()),
370         "Error while emptying bucket: " + bucketName + ", filter (C, U, P): " +
371             client + ", " + user + ", " + privilege);
372 }
373
374 Cynara::Cynara()
375 {
376     checkCynaraError(
377         cynara_initialize(&m_Cynara, nullptr),
378         "Cannot connect to Cynara policy interface.");
379 }
380
381 Cynara::~Cynara()
382 {
383     cynara_finish(m_Cynara);
384 }
385
386 Cynara &Cynara::getInstance()
387 {
388     static Cynara cynara;
389     return cynara;
390 }
391
392 bool Cynara::check(const std::string &label, const std::string &privilege,
393         const std::string &user, const std::string &session)
394 {
395     return checkCynaraError(
396         cynara_check(m_Cynara,
397             label.c_str(), session.c_str(), user.c_str(), privilege.c_str()),
398         "Cannot check permission with Cynara.");
399 }
400
401 } // namespace SecurityManager