Refactoring Smack-related code for exception-based error handling
[platform/core/security/security-manager.git] / src / common / service_impl.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        service_impl.cpp
20  * @author      Michal Witanowski <m.witanowski@samsung.com>
21  * @author      Jacek Bukarewicz <j.bukarewicz@samsung.com>
22  * @author      Rafal Krypa <r.krypa@samsung.com>
23  * @author      Krzysztof Sasiak <k.sasiak@samsung.com>
24  * @brief       Implementation of the service methods
25  */
26
27 #include <grp.h>
28 #include <limits.h>
29 #include <pwd.h>
30
31 #include <cstring>
32 #include <algorithm>
33
34 #include <dpl/log/log.h>
35 #include <tzplatform_config.h>
36
37 #include "protocols.h"
38 #include "privilege_db.h"
39 #include "cynara.h"
40 #include "smack-rules.h"
41 #include "smack-labels.h"
42 #include "security-manager.h"
43
44 #include "service_impl.h"
45
46 namespace SecurityManager {
47 namespace ServiceImpl {
48
49 static const std::string ADMIN_PRIVILEGE = "http://tizen.org/privilege/systemsettings.admin";
50 static const std::string SELF_PRIVILEGE = "http://tizen.org/privilege/systemsettings";
51
52 namespace {
53
54 static inline int validatePolicy(policy_entry &policyEntry, std::string uidStr, bool &forAdmin, CynaraAdminPolicy &cyap)
55 {
56     LogDebug("Authenticating and validating policy update request for user with id: " << uidStr);
57     LogDebug("[policy_entry] app: " << policyEntry.appId
58             << " user: " << policyEntry.user
59             << " privilege: " << policyEntry.privilege
60             << " current: " << policyEntry.currentLevel
61             << " max: " << policyEntry.maxLevel);
62     //automagically fill missing fields:
63     if (policyEntry.user.empty()) {
64         policyEntry.user = uidStr;
65     };
66
67     int level;
68
69     if (policyEntry.currentLevel.empty()) { //for admin
70         if (policyEntry.appId.empty()
71             || policyEntry.privilege.empty()) {
72             LogError("Bad admin update request");
73             return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
74         };
75
76         if (!policyEntry.maxLevel.compare(SECURITY_MANAGER_DELETE)) {
77             level = CYNARA_ADMIN_DELETE;
78         } else {
79             try {
80                 level = CynaraAdmin::getInstance().convertToPolicyType(policyEntry.maxLevel);
81             } catch (const std::out_of_range& e) {
82                 LogError("policy max level cannot be: " << policyEntry.maxLevel);
83                 return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
84             };
85         };
86         forAdmin = true;
87
88     } else if (policyEntry.maxLevel.empty()) { //for self
89         if (policyEntry.user.compare(uidStr)
90             || !policyEntry.appId.compare(SECURITY_MANAGER_ANY)
91             || !policyEntry.privilege.compare(SECURITY_MANAGER_ANY)
92             || policyEntry.appId.empty()
93             || policyEntry.privilege.empty()) {
94             LogError("Bad privacy manager update request");
95             return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
96         };
97
98         if (!policyEntry.currentLevel.compare(SECURITY_MANAGER_DELETE)) {
99             level = CYNARA_ADMIN_DELETE;
100         } else {
101             try {
102                 level = CynaraAdmin::getInstance().convertToPolicyType(policyEntry.currentLevel);
103             } catch (const std::out_of_range& e) {
104                 LogError("policy current level cannot be: " << policyEntry.currentLevel);
105                 return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
106             };
107         };
108         forAdmin = false;
109
110     } else { //neither => bad request
111         return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
112     };
113
114     if (!policyEntry.user.compare(SECURITY_MANAGER_ANY))
115         policyEntry.user = CYNARA_ADMIN_WILDCARD;
116     if (!policyEntry.privilege.compare(SECURITY_MANAGER_ANY))
117         policyEntry.privilege = CYNARA_ADMIN_WILDCARD;
118
119     cyap = std::move(CynaraAdminPolicy(
120         policyEntry.appId.compare(SECURITY_MANAGER_ANY) ?
121             SmackLabels::generateAppLabel(policyEntry.appId) : CYNARA_ADMIN_WILDCARD,
122         policyEntry.user,
123         policyEntry.privilege,
124         level,
125         (forAdmin)?CynaraAdmin::Buckets.at(Bucket::ADMIN):CynaraAdmin::Buckets.at(Bucket::PRIVACY_MANAGER)));
126
127     LogDebug("Policy update request authenticated and validated successfully");
128     return SECURITY_MANAGER_API_SUCCESS;
129 }
130 } // end of anonymous namespace
131
132 static uid_t getGlobalUserId(void)
133 {
134     static uid_t globaluid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
135     return globaluid;
136 }
137
138 /**
139  * Unifies user data of apps installed for all users
140  * @param  uid            peer's uid - may be changed during process
141  * @param  cynaraUserStr  string to which cynara user parameter will be put
142  */
143 static void checkGlobalUser(uid_t &uid, std::string &cynaraUserStr)
144 {
145     static uid_t globaluid = getGlobalUserId();
146     if (uid == 0 || uid == globaluid) {
147         uid = globaluid;
148         cynaraUserStr = CYNARA_ADMIN_WILDCARD;
149     } else {
150         cynaraUserStr = std::to_string(static_cast<unsigned int>(uid));
151     }
152 }
153 static inline bool isSubDir(const char *parent, const char *subdir)
154 {
155     while (*parent && *subdir)
156         if (*parent++ != *subdir++)
157             return false;
158
159     return (*subdir == '/');
160 }
161
162 static bool getUserAppDir(const uid_t &uid, std::string &userAppDir)
163 {
164     struct tzplatform_context *tz_ctx = nullptr;
165
166     if (tzplatform_context_create(&tz_ctx))
167             return false;
168
169     if (tzplatform_context_set_user(tz_ctx, uid)) {
170         tzplatform_context_destroy(tz_ctx);
171         tz_ctx = nullptr;
172         return false;
173     }
174
175     enum tzplatform_variable id =
176             (uid == getGlobalUserId()) ? TZ_SYS_RW_APP : TZ_USER_APP;
177     const char *appDir = tzplatform_context_getenv(tz_ctx, id);
178     if (!appDir) {
179         tzplatform_context_destroy(tz_ctx);
180         tz_ctx = nullptr;
181         return false;
182     }
183
184     userAppDir = appDir;
185
186     tzplatform_context_destroy(tz_ctx);
187     tz_ctx = nullptr;
188
189     return true;
190 }
191
192 static inline bool installRequestAuthCheck(const app_inst_req &req, uid_t uid, bool &isCorrectPath, std::string &appPath)
193 {
194     std::string userHome;
195     std::string userAppDir;
196     std::stringstream correctPath;
197
198     if (uid != getGlobalUserId())
199         LogDebug("Installation type: single user");
200     else
201         LogDebug("Installation type: global installation");
202
203     if (!getUserAppDir(uid, userAppDir)) {
204         LogError("Failed getting app dir for user uid: " << uid);
205         return false;
206     }
207
208     appPath = userAppDir;
209     correctPath.clear();
210     correctPath << userAppDir << "/" << req.pkgId << "/" << req.appId;
211     LogDebug("correctPath: " << correctPath.str());
212
213     for (const auto &appPath : req.appPaths) {
214         std::unique_ptr<char, std::function<void(void*)>> real_path(
215             realpath(appPath.first.c_str(), NULL), free);
216         if (!real_path.get()) {
217             LogError("realpath failed with '" << appPath.first.c_str()
218                     << "' as parameter: " << strerror(errno));
219             return false;
220         }
221         LogDebug("Requested path is '" << appPath.first.c_str()
222                 << "'. User's APPS_DIR is '" << userAppDir << "'");
223         if (!isSubDir(userAppDir.c_str(), real_path.get())) {
224             LogWarning("User's apps may have registered folders only in user's APPS_DIR");
225             return false;
226         }
227
228         if (!isSubDir(correctPath.str().c_str(), real_path.get())) {
229             LogWarning("Installation is outside correct path: " << correctPath.str());
230             //return false;
231         } else
232             isCorrectPath = true;
233
234         app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
235         if (pathType == SECURITY_MANAGER_PATH_PUBLIC) {
236             LogWarning("Only root can register SECURITY_MANAGER_PATH_PUBLIC path");
237             return false;
238         }
239     }
240     return true;
241 }
242
243 int appInstall(const app_inst_req &req, uid_t uid)
244 {
245     std::vector<std::string> addedPermissions;
246     std::vector<std::string> removedPermissions;
247     std::vector<std::string> pkgContents;
248     std::string uidstr;
249     bool isCorrectPath = false;
250     std::string appPath;
251     std::string appLabel;
252     std::string pkgLabel;
253
254     if (uid) {
255         if (uid != req.uid) {
256             LogError("User " << uid <<
257                      " is denied to install application for user " << req.uid);
258             return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
259         }
260     } else {
261         if (req.uid)
262             uid = req.uid;
263     }
264     checkGlobalUser(uid, uidstr);
265
266     if (!installRequestAuthCheck(req, uid, isCorrectPath, appPath)) {
267         LogError("Request from uid " << uid << " for app installation denied");
268         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
269     }
270
271     // create null terminated array of strings for permissions
272     std::unique_ptr<const char *[]> pp_permissions(new const char* [req.privileges.size() + 1]);
273     for (size_t i = 0; i < req.privileges.size(); ++i) {
274         LogDebug("  Permission = " << req.privileges[i]);
275         pp_permissions[i] = req.privileges[i].c_str();
276     }
277     pp_permissions[req.privileges.size()] = nullptr;
278
279     try {
280         std::vector<std::string> oldAppPrivileges;
281
282         appLabel = SmackLabels::generateAppLabel(req.appId);
283         /* NOTE: we don't use pkgLabel here, but generate it for pkgId validation */
284         pkgLabel = SmackLabels::generatePkgLabel(req.pkgId);
285         LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
286                  << ", uidstr " << uidstr
287                  << ", app label: " << appLabel << ", pkg label: " << pkgLabel);
288
289         PrivilegeDb::getInstance().BeginTransaction();
290         std::string pkg;
291         bool ret = PrivilegeDb::getInstance().GetAppPkgId(req.appId, pkg);
292         if (ret == true && pkg != req.pkgId) {
293             LogError("Application already installed with different package id");
294             PrivilegeDb::getInstance().RollbackTransaction();
295             return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
296         }
297         PrivilegeDb::getInstance().GetAppPrivileges(req.appId, uid, oldAppPrivileges);
298         PrivilegeDb::getInstance().AddApplication(req.appId, req.pkgId, uid);
299         PrivilegeDb::getInstance().UpdateAppPrivileges(req.appId, uid, req.privileges);
300         /* Get all application ids in the package to generate rules withing the package */
301         PrivilegeDb::getInstance().GetAppIdsForPkgId(req.pkgId, pkgContents);
302         CynaraAdmin::getInstance().UpdateAppPolicy(appLabel, uidstr, oldAppPrivileges,
303                                          req.privileges);
304         PrivilegeDb::getInstance().CommitTransaction();
305         LogDebug("Application installation commited to database");
306     } catch (const PrivilegeDb::Exception::IOError &e) {
307         LogError("Cannot access application database: " << e.DumpToString());
308         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
309     } catch (const PrivilegeDb::Exception::InternalError &e) {
310         PrivilegeDb::getInstance().RollbackTransaction();
311         LogError("Error while saving application info to database: " << e.DumpToString());
312         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
313     } catch (const CynaraException::Base &e) {
314         PrivilegeDb::getInstance().RollbackTransaction();
315         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
316         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
317     } catch (const SmackException::InvalidLabel &e) {
318         PrivilegeDb::getInstance().RollbackTransaction();
319         LogError("Error while generating Smack labels: " << e.DumpToString());
320         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
321     } catch (const std::bad_alloc &e) {
322         PrivilegeDb::getInstance().RollbackTransaction();
323         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
324         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
325     }
326
327     try {
328         if (isCorrectPath)
329             SmackLabels::setupCorrectPath(req.pkgId, req.appId, appPath);
330
331         // register paths
332         for (const auto &appPath : req.appPaths) {
333             const std::string &path = appPath.first;
334             app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
335             SmackLabels::setupPath(req.appId, path, pathType);
336         }
337
338         LogDebug("Adding Smack rules for new appId: " << req.appId << " with pkgId: "
339                 << req.pkgId << ". Applications in package: " << pkgContents.size());
340         SmackRules::installApplicationRules(req.appId, req.pkgId, pkgContents);
341     } catch (const SmackException::Base &e) {
342         LogError("Error while applying Smack policy for application: " << e.DumpToString());
343         return SECURITY_MANAGER_API_ERROR_SETTING_FILE_LABEL_FAILED;
344     } catch (const std::bad_alloc &e) {
345         LogError("Memory allocation error: " << e.what());
346         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
347     }
348
349     return SECURITY_MANAGER_API_SUCCESS;
350 }
351
352 int appUninstall(const std::string &appId, uid_t uid)
353 {
354     std::string pkgId;
355     std::string smackLabel;
356     std::vector<std::string> pkgContents;
357     bool appExists = true;
358     bool removePkg = false;
359     std::string uidstr;
360     checkGlobalUser(uid, uidstr);
361
362     try {
363         std::vector<std::string> oldAppPrivileges;
364
365         PrivilegeDb::getInstance().BeginTransaction();
366         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
367             LogWarning("Application " << appId <<
368                 " not found in database while uninstalling");
369             PrivilegeDb::getInstance().RollbackTransaction();
370             appExists = false;
371         } else {
372             smackLabel = SmackLabels::generateAppLabel(appId);
373             LogDebug("Uninstall parameters: appId: " << appId << ", pkgId: " << pkgId
374                      << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
375
376             /* Before we remove the app from the database, let's fetch all apps in the package
377                 that this app belongs to, this will allow us to remove all rules withing the
378                 package that the app appears in */
379             PrivilegeDb::getInstance().GetAppIdsForPkgId(pkgId, pkgContents);
380             PrivilegeDb::getInstance().GetAppPrivileges(appId, uid, oldAppPrivileges);
381             PrivilegeDb::getInstance().UpdateAppPrivileges(appId, uid, std::vector<std::string>());
382             PrivilegeDb::getInstance().RemoveApplication(appId, uid, removePkg);
383             CynaraAdmin::getInstance().UpdateAppPolicy(smackLabel, uidstr, oldAppPrivileges,
384                                              std::vector<std::string>());
385             PrivilegeDb::getInstance().CommitTransaction();
386             LogDebug("Application uninstallation commited to database");
387         }
388     } catch (const PrivilegeDb::Exception::IOError &e) {
389         LogError("Cannot access application database: " << e.DumpToString());
390         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
391     } catch (const PrivilegeDb::Exception::InternalError &e) {
392         PrivilegeDb::getInstance().RollbackTransaction();
393         LogError("Error while removing application info from database: " << e.DumpToString());
394         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
395     } catch (const CynaraException::Base &e) {
396         PrivilegeDb::getInstance().RollbackTransaction();
397         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
398         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
399     } catch (const SmackException::InvalidLabel &e) {
400         PrivilegeDb::getInstance().RollbackTransaction();
401         LogError("Error while generating Smack labels: " << e.DumpToString());
402         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
403     } catch (const std::bad_alloc &e) {
404         PrivilegeDb::getInstance().RollbackTransaction();
405         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
406         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
407     }
408
409     try {
410         if (appExists) {
411             if (removePkg) {
412                 LogDebug("Removing Smack rules for deleted pkgId " << pkgId);
413                 SmackRules::uninstallPackageRules(pkgId);
414             }
415
416             LogDebug("Removing smack rules for deleted appId " << appId);
417             SmackRules::uninstallApplicationRules(appId, pkgId, pkgContents);
418         }
419     } catch (const SmackException::Base &e) {
420         LogError("Error while removing Smack rules for application: " << e.DumpToString());
421         return SECURITY_MANAGER_API_ERROR_SETTING_FILE_LABEL_FAILED;
422     } catch (const std::bad_alloc &e) {
423         LogError("Memory allocation error: " << e.what());
424         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
425     }
426
427     return SECURITY_MANAGER_API_SUCCESS;
428 }
429
430 int getPkgId(const std::string &appId, std::string &pkgId)
431 {
432     LogDebug("appId: " << appId);
433
434     try {
435         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
436             LogWarning("Application " << appId << " not found in database");
437             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
438         } else {
439             LogDebug("pkgId: " << pkgId);
440         }
441     } catch (const PrivilegeDb::Exception::Base &e) {
442         LogError("Error while getting pkgId from database: " << e.DumpToString());
443         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
444     }
445
446     return SECURITY_MANAGER_API_SUCCESS;
447 }
448
449 int getAppGroups(const std::string &appId, uid_t uid, pid_t pid, std::unordered_set<gid_t> &gids)
450 {
451     try {
452         std::string pkgId;
453         std::string smackLabel;
454         std::string uidStr = std::to_string(uid);
455         std::string pidStr = std::to_string(pid);
456
457         LogDebug("appId: " << appId);
458
459         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
460             LogWarning("Application " << appId << " not found in database");
461             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
462         }
463         LogDebug("pkgId: " << pkgId);
464
465         smackLabel = SmackLabels::generatePkgLabel(pkgId);
466         LogDebug("smack label: " << smackLabel);
467
468         std::vector<std::string> privileges;
469         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, privileges);
470         /*there is also a need of checking, if privilege is granted to all users*/
471         size_t tmp = privileges.size();
472         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, getGlobalUserId(), privileges);
473         /*privileges needs to be sorted and with no duplications - for cynara sake*/
474         std::inplace_merge(privileges.begin(), privileges.begin() + tmp, privileges.end());
475         privileges.erase( unique( privileges.begin(), privileges.end() ), privileges.end() );
476
477         for (const auto &privilege : privileges) {
478             std::vector<std::string> gidsTmp;
479             PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, gidsTmp);
480             if (!gidsTmp.empty()) {
481                 LogDebug("Considering privilege " << privilege << " with " <<
482                     gidsTmp.size() << " groups assigned");
483                 if (Cynara::getInstance().check(smackLabel, privilege, uidStr, pidStr)) {
484                     for_each(gidsTmp.begin(), gidsTmp.end(), [&] (std::string group)
485                     {
486                         struct group *grp = getgrnam(group.c_str());
487                         if (grp == NULL) {
488                                 LogError("No such group: " << group.c_str());
489                                 return;
490                         }
491                         gids.insert(grp->gr_gid);
492                     });
493                     LogDebug("Cynara allowed, adding groups");
494                 } else
495                     LogDebug("Cynara denied, not adding groups");
496             }
497         }
498     } catch (const PrivilegeDb::Exception::Base &e) {
499         LogError("Database error: " << e.DumpToString());
500         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
501     } catch (const CynaraException::Base &e) {
502         LogError("Error while querying Cynara for permissions: " << e.DumpToString());
503         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
504     } catch (const SmackException::InvalidLabel &e) {
505         LogError("Error while generating Smack labels: " << e.DumpToString());
506         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
507     } catch (const std::bad_alloc &e) {
508         LogError("Memory allocation failed: " << e.what());
509         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
510     }
511
512     return SECURITY_MANAGER_API_SUCCESS;
513 }
514
515 int userAdd(uid_t uidAdded, int userType, uid_t uid)
516 {
517     if (uid != 0)
518         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
519
520     try {
521         CynaraAdmin::getInstance().UserInit(uidAdded, static_cast<security_manager_user_type>(userType));
522     } catch (CynaraException::InvalidParam &e) {
523         return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
524     }
525     return SECURITY_MANAGER_API_SUCCESS;
526 }
527
528 int userDelete(uid_t uidDeleted, uid_t uid)
529 {
530     int ret = SECURITY_MANAGER_API_SUCCESS;
531     if (uid != 0)
532         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
533
534     /*Uninstall all user apps*/
535     std::vector<std::string> userApps;
536     try {
537         PrivilegeDb::getInstance().GetUserApps(uidDeleted, userApps);
538     } catch (const PrivilegeDb::Exception::Base &e) {
539         LogError("Error while getting user apps from database: " << e.DumpToString());
540         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
541     }
542
543     for (auto &app: userApps) {
544         if (appUninstall(app, uidDeleted) != SECURITY_MANAGER_API_SUCCESS) {
545         /*if uninstallation of this app fails, just go on trying to uninstall another ones.
546         we do not have anything special to do about that matter - user will be deleted anyway.*/
547             ret = SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
548         }
549     }
550
551     CynaraAdmin::getInstance().UserRemove(uidDeleted);
552
553     return ret;
554 }
555
556 int policyUpdate(const std::vector<policy_entry> &policyEntries, uid_t uid, pid_t pid, const std::string &smackLabel)
557 {
558     enum {
559         NOT_CHECKED,
560         IS_NOT_ADMIN,
561         IS_ADMIN
562     }  isAdmin = NOT_CHECKED;
563
564     try {
565         std::string uidStr = std::to_string(uid);
566         std::string pidStr = std::to_string(pid);
567
568         if (policyEntries.size() == 0) {
569             LogError("Validation failed: policy update request is empty");
570             return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
571         };
572
573         if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) {
574             LogError("Not enough permission to call: " << __FUNCTION__);
575             return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
576         };
577
578         std::vector<CynaraAdminPolicy> validatedPolicies;
579
580         for (auto &entry : const_cast<std::vector<policy_entry>&>(policyEntries)) {
581             bool forAdmin = false;
582             CynaraAdminPolicy cyap("", "", "", CYNARA_ADMIN_NONE, "");
583             int ret = validatePolicy(entry, uidStr, forAdmin, cyap);
584
585             if (forAdmin && (isAdmin == NOT_CHECKED)) {
586                 isAdmin = Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)?IS_ADMIN:IS_NOT_ADMIN;
587             };
588
589             if (ret == SECURITY_MANAGER_API_SUCCESS) {
590                 if (!forAdmin
591                     || (forAdmin && (isAdmin == IS_ADMIN))) {
592                     validatedPolicies.push_back(std::move(cyap));
593                 } else {
594                     LogError("Not enough privilege to enforce admin policy");
595                     return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
596                 };
597
598             } else
599                 return ret;
600         };
601
602             // Apply updates
603         CynaraAdmin::getInstance().SetPolicies(validatedPolicies);
604
605     } catch (const CynaraException::Base &e) {
606         LogError("Error while updating Cynara rules: " << e.DumpToString());
607         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
608     } catch (const std::bad_alloc &e) {
609         LogError("Memory allocation error while updating Cynara rules: " << e.what());
610         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
611     }
612
613     return SECURITY_MANAGER_API_SUCCESS;
614 }
615
616 int getConfiguredPolicy(bool forAdmin, const policy_entry &filter, uid_t uid, pid_t pid,
617     const std::string &smackLabel, std::vector<policy_entry> &policyEntries)
618 {
619     try {
620         std::string uidStr = std::to_string(uid);
621         std::string pidStr = std::to_string(pid);
622
623         if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) {
624             LogError("Not enough permission to call: " << __FUNCTION__);
625             return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
626         };
627
628         LogDebug("Filter is: C: " << filter.appId
629                     << ", U: " << filter.user
630                     << ", P: " << filter.privilege
631                     << ", current: " << filter.currentLevel
632                     << ", max: " << filter.maxLevel
633                     );
634
635         std::vector<CynaraAdminPolicy> listOfPolicies;
636
637         //convert appId to smack label
638         std::string appLabel = filter.appId.compare(SECURITY_MANAGER_ANY) ? SmackLabels::generateAppLabel(filter.appId) : CYNARA_ADMIN_ANY;
639         std::string user = filter.user.compare(SECURITY_MANAGER_ANY) ? filter.user : CYNARA_ADMIN_ANY;
640         std::string privilege = filter.privilege.compare(SECURITY_MANAGER_ANY) ? filter.privilege : CYNARA_ADMIN_ANY;
641
642         LogDebug("App: " << filter.appId << ", Label: " << appLabel);
643
644         if (forAdmin) {
645             if (!Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) {
646                 LogError("Not enough privilege to access admin enforced policies: " << __FUNCTION__);
647                 return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
648                 };
649
650             //Fetch privileges from ADMIN bucket
651             CynaraAdmin::getInstance().ListPolicies(
652                 CynaraAdmin::Buckets.at(Bucket::ADMIN),
653                 appLabel,
654                 user,
655                 privilege,
656                 listOfPolicies
657                 );
658             LogDebug("ADMIN - number of policies matched: " << listOfPolicies.size());
659         } else {
660             if (uidStr.compare(user)) {
661                 if (!Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) {
662                     LogWarning("Not enough privilege to access other user's personal policies. Limiting query to personal privileges.");
663                     user = uidStr;
664                 };
665             };
666             //Fetch privileges from PRIVACY_MANAGER bucket
667             CynaraAdmin::getInstance().ListPolicies(
668                 CynaraAdmin::Buckets.at(Bucket::PRIVACY_MANAGER),
669                 appLabel,
670                 user,
671                 privilege,
672                 listOfPolicies
673                 );
674             LogDebug("PRIVACY MANAGER - number of policies matched: " << listOfPolicies.size());
675         };
676
677         for (const auto &policy : listOfPolicies) {
678             //ignore "jump to bucket" entries
679             if (policy.result ==  CYNARA_ADMIN_BUCKET)
680                 continue;
681
682             policy_entry pe;
683
684             pe.appId = strcmp(policy.client, CYNARA_ADMIN_WILDCARD) ? SmackLabels::generateAppNameFromLabel(policy.client) : SECURITY_MANAGER_ANY;
685             pe.user =  strcmp(policy.user, CYNARA_ADMIN_WILDCARD) ? policy.user : SECURITY_MANAGER_ANY;
686             pe.privilege = strcmp(policy.privilege, CYNARA_ADMIN_WILDCARD) ? policy.privilege : pe.privilege = SECURITY_MANAGER_ANY;
687             pe.currentLevel = CynaraAdmin::getInstance().convertToPolicyDescription(policy.result);
688
689             if (!forAdmin) {
690                 // All policy entries in PRIVACY_MANAGER should be fully-qualified
691                 pe.maxLevel = CynaraAdmin::getInstance().convertToPolicyDescription(
692                     CynaraAdmin::getInstance().GetPrivilegeManagerMaxLevel(
693                         policy.client, policy.user, policy.privilege));
694             } else {
695                 // Cannot reliably calculate maxLavel for policies from ADMIN bucket
696                 pe.maxLevel = CynaraAdmin::getInstance().convertToPolicyDescription(CYNARA_ADMIN_ALLOW);
697             }
698
699
700             LogDebug(
701                 "[policy_entry] app: " << pe.appId
702                 << " user: " << pe.user
703                 << " privilege: " << pe.privilege
704                 << " current: " << pe.currentLevel
705                 << " max: " << pe.maxLevel
706                 );
707
708             policyEntries.push_back(pe);
709         };
710
711     } catch (const CynaraException::Base &e) {
712         LogError("Error while listing Cynara rules: " << e.DumpToString());
713         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
714     } catch (const SmackException::InvalidLabel &e) {
715         LogError("Error while generating Smack labels: " << e.DumpToString());
716         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
717     } catch (const std::bad_alloc &e) {
718         LogError("Memory allocation error while listing Cynara rules: " << e.what());
719         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
720     }
721
722
723     return SECURITY_MANAGER_API_SUCCESS;
724 }
725
726 int getPolicy(const policy_entry &filter, uid_t uid, pid_t pid, const std::string &smackLabel, std::vector<policy_entry> &policyEntries)
727 {
728     try {
729         std::string uidStr = std::to_string(uid);
730         std::string pidStr = std::to_string(pid);
731
732         if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) {
733             LogWarning("Not enough permission to call: " << __FUNCTION__);
734             return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
735         };
736
737         LogDebug("Filter is: C: " << filter.appId
738                     << ", U: " << filter.user
739                     << ", P: " << filter.privilege
740                     << ", current: " << filter.currentLevel
741                     << ", max: " << filter.maxLevel
742                     );
743
744         std::vector<uid_t> listOfUsers;
745
746         if (Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) {
747             LogDebug("User is privileged");
748             if (filter.user.compare(SECURITY_MANAGER_ANY)) {
749                 LogDebug("Limitting Cynara query to user: " << filter.user);
750                 try {
751                     listOfUsers.push_back(static_cast<uid_t>(std::stoul(filter.user)));
752                 } catch (std::invalid_argument &e) {
753                     LogError("Invalid UID: " << e.what());
754                 };
755             } else
756                 CynaraAdmin::getInstance().ListUsers(listOfUsers);
757         } else {
758             LogWarning("Not enough privilege to fetch user policy for all users by user: " << uid);
759             LogDebug("Fetching personal policy for user: " << uid);
760             listOfUsers.push_back(uid);
761         };
762         LogDebug("Fetching policy for " << listOfUsers.size() << " users");
763
764         for (const uid_t &uid : listOfUsers) {
765             LogDebug("User: " << uid);
766             std::string userStr = std::to_string(uid);
767             std::vector<std::string> listOfApps;
768
769             if (filter.appId.compare(SECURITY_MANAGER_ANY)) {
770                 LogDebug("Limitting Cynara query to app: " << filter.appId);
771                 listOfApps.push_back(filter.appId);
772             } else {
773                 PrivilegeDb::getInstance().GetUserApps(uid, listOfApps);
774                 LogDebug("Found apps: " << listOfApps.size());
775             };
776
777             for (const std::string &appId : listOfApps) {
778                 LogDebug("App: " << appId);
779                 std::string smackLabelForApp = SmackLabels::generateAppLabel(appId);
780                 std::vector<std::string> listOfPrivileges;
781
782                 // FIXME: also fetch privileges of global applications
783                 PrivilegeDb::getInstance().GetAppPrivileges(appId, uid, listOfPrivileges);
784
785                 if (filter.privilege.compare(SECURITY_MANAGER_ANY)) {
786                     LogDebug("Limitting Cynara query to privilege: " << filter.privilege);
787                     // FIXME: this filtering should be already performed by method fetching the privileges
788                     if (std::find(listOfPrivileges.begin(), listOfPrivileges.end(),
789                         filter.privilege) == listOfPrivileges.end()) {
790                         LogDebug("Application " << appId <<
791                             " doesn't have the filteres privilege " << filter.privilege);
792                         continue;
793                     }
794                     listOfPrivileges.clear();
795                     listOfPrivileges.push_back(filter.privilege);
796                 }
797
798                 LogDebug("Privileges matching filter - " << filter.privilege << ": " << listOfPrivileges.size());
799
800                 for (const std::string &privilege : listOfPrivileges) {
801                     LogDebug("Privilege: " << privilege);
802                     policy_entry pe;
803
804                     pe.appId = appId;
805                     pe.user = userStr;
806                     pe.privilege = privilege;
807
808                     pe.currentLevel = CynaraAdmin::getInstance().convertToPolicyDescription(
809                         CynaraAdmin::getInstance().GetPrivilegeManagerCurrLevel(
810                             smackLabelForApp, userStr, privilege));
811
812                     pe.maxLevel = CynaraAdmin::getInstance().convertToPolicyDescription(
813                         CynaraAdmin::getInstance().GetPrivilegeManagerMaxLevel(
814                             smackLabelForApp, userStr, privilege));
815
816                     LogDebug(
817                         "[policy_entry] app: " << pe.appId
818                         << " user: " << pe.user
819                         << " privilege: " << pe.privilege
820                         << " current: " << pe.currentLevel
821                         << " max: " << pe.maxLevel
822                         );
823
824                     policyEntries.push_back(pe);
825                 };
826             };
827         };
828
829     } catch (const CynaraException::Base &e) {
830         LogError("Error while listing Cynara rules: " << e.DumpToString());
831         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
832     } catch (const SmackException::InvalidLabel &e) {
833         LogError("Error while generating Smack labels: " << e.DumpToString());
834         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
835     } catch (const std::bad_alloc &e) {
836         LogError("Memory allocation error while listing Cynara rules: " << e.what());
837         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
838     }
839
840     return SECURITY_MANAGER_API_SUCCESS;
841 }
842
843 int policyGetDesc(std::vector<std::string> &levels)
844 {
845     int ret = SECURITY_MANAGER_API_SUCCESS;
846
847     try {
848         CynaraAdmin::getInstance().ListPoliciesDescriptions(levels);
849     } catch (const CynaraException::OutOfMemory &e) {
850         LogError("Error - out of memory while querying Cynara for policy descriptions list: " << e.DumpToString());
851         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
852     } catch (const CynaraException::InvalidParam &e) {
853         LogError("Error - invalid parameter while querying Cynara for policy descriptions list: " << e.DumpToString());
854         return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
855     } catch (const CynaraException::ServiceNotAvailable &e) {
856         LogError("Error - service not available while querying Cynara for policy descriptions list: " << e.DumpToString());
857         return SECURITY_MANAGER_API_ERROR_NO_SUCH_SERVICE;
858     } catch (const CynaraException::Base &e) {
859         LogError("Error while getting policy descriptions list from Cynara: " << e.DumpToString());
860         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
861     }
862
863     return ret;
864 }
865
866 } /* namespace ServiceImpl */
867 } /* namespace SecurityManager */