Service backend implementation for getting policies levels
[platform/core/security/security-manager.git] / src / common / service_impl.cpp
index f4ec16f..074495f 100644 (file)
@@ -20,6 +20,7 @@
  * @author      Michal Witanowski <m.witanowski@samsung.com>
  * @author      Jacek Bukarewicz <j.bukarewicz@samsung.com>
  * @author      Rafal Krypa <r.krypa@samsung.com>
+ * @author      Krzysztof Sasiak <k.sasiak@samsung.com>
  * @brief       Implementation of the service methods
  */
 
 #include "cynara.h"
 #include "smack-rules.h"
 #include "smack-labels.h"
+#include "security-manager.h"
 
 #include "service_impl.h"
 
 namespace SecurityManager {
 namespace ServiceImpl {
 
+static const std::string ADMIN_PRIVILEGE = "http://tizen.org/privilege/systemsettings.admin";
+static const std::string SELF_PRIVILEGE = "http://tizen.org/privilege/systemsettings";
+
+namespace {
+
+static inline int validatePolicy(policy_entry &policyEntry, std::string uidStr, bool &forAdmin, CynaraAdminPolicy &cyap)
+{
+    LogDebug("Authenticating and validating policy update request for user with id: " << uidStr);
+    LogDebug("[policy_entry] app: " << policyEntry.appId
+            << " user: " << policyEntry.user
+            << " privilege: " << policyEntry.privilege
+            << " current: " << policyEntry.currentLevel
+            << " max: " << policyEntry.maxLevel);
+    //automagically fill missing fields:
+    if (policyEntry.user.empty()) {
+        policyEntry.user = uidStr;
+    };
+
+    std::string client;
+    int level;
+
+    if (policyEntry.currentLevel.empty()) { //for admin
+        if (policyEntry.appId.empty()
+            || policyEntry.privilege.empty()) {
+            LogError("Bad admin update request");
+            return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
+        };
+
+        if (!policyEntry.maxLevel.compare(SECURITY_MANAGER_DELETE)) {
+            level = CYNARA_ADMIN_DELETE;
+        } else {
+            try {
+                level = CynaraAdmin::getInstance().convertToPolicyType(policyEntry.maxLevel);
+            } catch (const std::out_of_range& e) {
+                LogError("policy max level cannot be: " << policyEntry.maxLevel);
+                return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
+            };
+        };
+        forAdmin = true;
+
+    } else if (policyEntry.maxLevel.empty()) { //for self
+        if (policyEntry.user.compare(uidStr)
+            || !policyEntry.appId.compare(SECURITY_MANAGER_ANY)
+            || !policyEntry.privilege.compare(SECURITY_MANAGER_ANY)
+            || policyEntry.appId.empty()
+            || policyEntry.privilege.empty()) {
+            LogError("Bad privacy manager update request");
+            return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
+        };
+
+        if (!policyEntry.currentLevel.compare(SECURITY_MANAGER_DELETE)) {
+            level = CYNARA_ADMIN_DELETE;
+        } else {
+            try {
+                level = CynaraAdmin::getInstance().convertToPolicyType(policyEntry.currentLevel);
+            } catch (const std::out_of_range& e) {
+                LogError("policy current level cannot be: " << policyEntry.currentLevel);
+                return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
+            };
+        };
+        forAdmin = false;
+
+    } else { //neither => bad request
+        return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
+    };
+
+    if (!policyEntry.user.compare(SECURITY_MANAGER_ANY))
+        policyEntry.user = CYNARA_ADMIN_WILDCARD;
+    if (!policyEntry.privilege.compare(SECURITY_MANAGER_ANY))
+        policyEntry.privilege = CYNARA_ADMIN_WILDCARD;
+    if (policyEntry.appId.compare(SECURITY_MANAGER_ANY))
+        generateAppLabel(policyEntry.appId, client);
+    else
+        client = CYNARA_ADMIN_WILDCARD;
+
+    cyap = std::move(CynaraAdminPolicy(
+        client,
+        policyEntry.user,
+        policyEntry.privilege,
+        level,
+        (forAdmin)?CynaraAdmin::Buckets.at(Bucket::ADMIN):CynaraAdmin::Buckets.at(Bucket::PRIVACY_MANAGER)));
+
+    LogDebug("Policy update request authenticated and validated successfully");
+    return SECURITY_MANAGER_API_SUCCESS;
+}
+} // end of anonymous namespace
+
 static uid_t getGlobalUserId(void)
 {
     static uid_t globaluid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
@@ -177,7 +266,7 @@ int appInstall(const app_inst_req &req, uid_t uid)
         PrivilegeDb::getInstance().UpdateAppPrivileges(req.appId, uid, req.privileges);
         /* Get all application ids in the package to generate rules withing the package */
         PrivilegeDb::getInstance().GetAppIdsForPkgId(req.pkgId, pkgContents);
-        CynaraAdmin::UpdateAppPolicy(smackLabel, uidstr, oldAppPrivileges,
+        CynaraAdmin::getInstance().UpdateAppPolicy(smackLabel, uidstr, oldAppPrivileges,
                                          req.privileges);
         PrivilegeDb::getInstance().CommitTransaction();
         LogDebug("Application installation commited to database");
@@ -256,7 +345,7 @@ int appUninstall(const std::string &appId, uid_t uid)
             PrivilegeDb::getInstance().GetAppPrivileges(appId, uid, oldAppPrivileges);
             PrivilegeDb::getInstance().UpdateAppPrivileges(appId, uid, std::vector<std::string>());
             PrivilegeDb::getInstance().RemoveApplication(appId, uid, removePkg);
-            CynaraAdmin::UpdateAppPolicy(smackLabel, uidstr, oldAppPrivileges,
+            CynaraAdmin::getInstance().UpdateAppPolicy(smackLabel, uidstr, oldAppPrivileges,
                                              std::vector<std::string>());
             PrivilegeDb::getInstance().CommitTransaction();
             LogDebug("Application uninstallation commited to database");
@@ -386,20 +475,11 @@ int userAdd(uid_t uidAdded, int userType, uid_t uid)
     if (uid != 0)
         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
 
-    switch (userType) {
-    case SM_USER_TYPE_SYSTEM:
-    case SM_USER_TYPE_ADMIN:
-    case SM_USER_TYPE_GUEST:
-    case SM_USER_TYPE_NORMAL:
-        break;
-    default:
+    try {
+        CynaraAdmin::getInstance().UserInit(uidAdded, static_cast<security_manager_user_type>(userType));
+    } catch (CynaraException::InvalidParam &e) {
         return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
     }
-
-    //TODO add policy information to cynara regarding user default privileges based on user_type
-    (void) uidAdded;
-    (void) userType;
-
     return SECURITY_MANAGER_API_SUCCESS;
 }
 
@@ -409,8 +489,6 @@ int userDelete(uid_t uidDeleted, uid_t uid)
     if (uid != 0)
         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
 
-    //TODO remove policy information from cynara
-
     /*Uninstall all user apps*/
     std::vector<std::string> userApps;
     try {
@@ -428,6 +506,318 @@ int userDelete(uid_t uidDeleted, uid_t uid)
         }
     }
 
+    CynaraAdmin::getInstance().UserRemove(uidDeleted);
+
+    return ret;
+}
+
+int policyUpdate(const std::vector<policy_entry> &policyEntries, uid_t uid, pid_t pid, const std::string &smackLabel)
+{
+    enum {
+        NOT_CHECKED,
+        IS_NOT_ADMIN,
+        IS_ADMIN
+    }  isAdmin = NOT_CHECKED;
+
+    try {
+        std::string uidStr = std::to_string(uid);
+        std::string pidStr = std::to_string(pid);
+
+        if (policyEntries.size() == 0) {
+            LogError("Validation failed: policy update request is empty");
+            return SECURITY_MANAGER_API_ERROR_BAD_REQUEST;
+        };
+
+        if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) {
+            LogError("Not enough permission to call: " << __FUNCTION__);
+            return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
+        };
+
+        std::vector<CynaraAdminPolicy> validatedPolicies;
+
+        for (auto &entry : const_cast<std::vector<policy_entry>&>(policyEntries)) {
+            bool forAdmin = false;
+            CynaraAdminPolicy cyap("", "", "", CYNARA_ADMIN_NONE, "");
+            int ret = validatePolicy(entry, uidStr, forAdmin, cyap);
+
+            if (forAdmin && (isAdmin == NOT_CHECKED)) {
+                isAdmin = Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)?IS_ADMIN:IS_NOT_ADMIN;
+            };
+
+            if (ret == SECURITY_MANAGER_API_SUCCESS) {
+                if (!forAdmin
+                    || (forAdmin && (isAdmin == IS_ADMIN))) {
+                    validatedPolicies.push_back(std::move(cyap));
+                } else {
+                    LogError("Not enough privilege to enforce admin policy");
+                    return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
+                };
+
+            } else
+                return ret;
+        };
+
+            // Apply updates
+        CynaraAdmin::getInstance().SetPolicies(validatedPolicies);
+
+    } catch (const CynaraException::Base &e) {
+        LogError("Error while updating Cynara rules: " << e.DumpToString());
+        return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
+    } catch (const std::bad_alloc &e) {
+        LogError("Memory allocation error while updating Cynara rules: " << e.what());
+        return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
+    }
+
+    return SECURITY_MANAGER_API_SUCCESS;
+}
+
+int getConfiguredPolicy(bool forAdmin, const policy_entry &filter, uid_t uid, pid_t pid,
+    const std::string &smackLabel, std::vector<policy_entry> &policyEntries)
+{
+    try {
+        std::string uidStr = std::to_string(uid);
+        std::string pidStr = std::to_string(pid);
+
+        if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) {
+            LogError("Not enough permission to call: " << __FUNCTION__);
+            return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
+        };
+
+        LogDebug("Filter is: C: " << filter.appId
+                    << ", U: " << filter.user
+                    << ", P: " << filter.privilege
+                    << ", current: " << filter.currentLevel
+                    << ", max: " << filter.maxLevel
+                    );
+
+        std::vector<CynaraAdminPolicy> listOfPolicies;
+
+        //convert appId to smack label
+        std::string appLabel;
+        if (!filter.appId.compare(SECURITY_MANAGER_ANY))
+            appLabel = CYNARA_ADMIN_ANY;
+        else
+            generateAppLabel(filter.appId, appLabel);
+
+        std::string user = (!filter.user.compare(SECURITY_MANAGER_ANY))? CYNARA_ADMIN_ANY: filter.user;
+        std::string privilege = (!filter.privilege.compare(SECURITY_MANAGER_ANY))? CYNARA_ADMIN_ANY: filter.privilege;
+
+        LogDebug("App: " << filter.appId << ", Label: " << appLabel);
+
+        if (forAdmin) {
+            if (!Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) {
+                LogError("Not enough privilege to access admin enforced policies: " << __FUNCTION__);
+                return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
+                };
+
+            //Fetch privileges from ADMIN bucket
+            CynaraAdmin::getInstance().ListPolicies(
+                CynaraAdmin::Buckets.at(Bucket::ADMIN),
+                appLabel,
+                user,
+                privilege,
+                listOfPolicies
+                );
+            LogDebug("ADMIN - number of policies matched: " << listOfPolicies.size());
+        } else {
+            if (uidStr.compare(user)) {
+                if (!Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) {
+                    LogWarning("Not enough privilege to access other user's personal policies. Limiting query to personal privileges.");
+                    user = uidStr;
+                };
+            };
+            //Fetch privileges from PRIVACY_MANAGER bucket
+            CynaraAdmin::getInstance().ListPolicies(
+                CynaraAdmin::Buckets.at(Bucket::PRIVACY_MANAGER),
+                appLabel,
+                user,
+                privilege,
+                listOfPolicies
+                );
+            LogDebug("PRIVACY MANAGER - number of policies matched: " << listOfPolicies.size());
+        };
+
+        for (const auto &policy : listOfPolicies) {
+            //ignore "jump to bucket" entries
+            if (policy.result ==  CYNARA_ADMIN_BUCKET)
+                continue;
+
+            policy_entry pe;
+
+            pe.appId = strcmp(policy.client, CYNARA_ADMIN_WILDCARD) ? generateAppNameFromLabel(policy.client) : SECURITY_MANAGER_ANY;
+            pe.user =  strcmp(policy.user, CYNARA_ADMIN_WILDCARD) ? policy.user : SECURITY_MANAGER_ANY;
+            pe.privilege = strcmp(policy.privilege, CYNARA_ADMIN_WILDCARD) ? policy.privilege : pe.privilege = SECURITY_MANAGER_ANY;
+            pe.currentLevel = CynaraAdmin::getInstance().convertToPolicyDescription(policy.result);
+
+            if (!forAdmin) {
+                // All policy entries in PRIVACY_MANAGER should be fully-qualified
+                pe.maxLevel = CynaraAdmin::getInstance().convertToPolicyDescription(
+                    CynaraAdmin::getInstance().GetPrivilegeManagerMaxLevel(
+                        policy.client, policy.user, policy.privilege));
+            } else {
+                // Cannot reliably calculate maxLavel for policies from ADMIN bucket
+                pe.maxLevel = CynaraAdmin::getInstance().convertToPolicyDescription(CYNARA_ADMIN_ALLOW);
+            }
+
+
+            LogDebug(
+                "[policy_entry] app: " << pe.appId
+                << " user: " << pe.user
+                << " privilege: " << pe.privilege
+                << " current: " << pe.currentLevel
+                << " max: " << pe.maxLevel
+                );
+
+            policyEntries.push_back(pe);
+        };
+
+    } catch (const CynaraException::Base &e) {
+        LogError("Error while listing Cynara rules: " << e.DumpToString());
+        return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
+    } catch (const std::bad_alloc &e) {
+        LogError("Memory allocation error while listing Cynara rules: " << e.what());
+        return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
+    }
+
+
+    return SECURITY_MANAGER_API_SUCCESS;
+}
+
+int getPolicy(const policy_entry &filter, uid_t uid, pid_t pid, const std::string &smackLabel, std::vector<policy_entry> &policyEntries)
+{
+    try {
+        std::string uidStr = std::to_string(uid);
+        std::string pidStr = std::to_string(pid);
+
+        if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) {
+            LogWarning("Not enough permission to call: " << __FUNCTION__);
+            return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
+        };
+
+        LogDebug("Filter is: C: " << filter.appId
+                    << ", U: " << filter.user
+                    << ", P: " << filter.privilege
+                    << ", current: " << filter.currentLevel
+                    << ", max: " << filter.maxLevel
+                    );
+
+        std::vector<uid_t> listOfUsers;
+
+        if (Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) {
+            LogDebug("User is privileged");
+            if (filter.user.compare(SECURITY_MANAGER_ANY)) {
+                LogDebug("Limitting Cynara query to user: " << filter.user);
+                try {
+                    listOfUsers.push_back(static_cast<uid_t>(std::stoul(filter.user)));
+                } catch (std::invalid_argument &e) {
+                    LogError("Invalid UID: " << e.what());
+                };
+            } else
+                CynaraAdmin::getInstance().ListUsers(listOfUsers);
+        } else {
+            LogWarning("Not enough privilege to fetch user policy for all users by user: " << uid);
+            LogDebug("Fetching personal policy for user: " << uid);
+            listOfUsers.push_back(uid);
+        };
+        LogDebug("Fetching policy for " << listOfUsers.size() << " users");
+
+        for (const uid_t &uid : listOfUsers) {
+            LogDebug("User: " << uid);
+            std::string userStr = std::to_string(uid);
+            std::vector<std::string> listOfApps;
+
+            if (filter.appId.compare(SECURITY_MANAGER_ANY)) {
+                LogDebug("Limitting Cynara query to app: " << filter.appId);
+                listOfApps.push_back(filter.appId);
+            } else {
+                PrivilegeDb::getInstance().GetUserApps(uid, listOfApps);
+                LogDebug("Found apps: " << listOfApps.size());
+            };
+
+            for (const std::string &appId : listOfApps) {
+                LogDebug("App: " << appId);
+                std::string smackLabelForApp;
+                std::vector<std::string> listOfPrivileges;
+
+                generateAppLabel(appId, smackLabelForApp);
+                // FIXME: also fetch privileges of global applications
+                PrivilegeDb::getInstance().GetAppPrivileges(appId, uid, listOfPrivileges);
+
+                if (filter.privilege.compare(SECURITY_MANAGER_ANY)) {
+                    LogDebug("Limitting Cynara query to privilege: " << filter.privilege);
+                    // FIXME: this filtering should be already performed by method fetching the privileges
+                    if (std::find(listOfPrivileges.begin(), listOfPrivileges.end(),
+                        filter.privilege) == listOfPrivileges.end()) {
+                        LogDebug("Application " << appId <<
+                            " doesn't have the filteres privilege " << filter.privilege);
+                        continue;
+                    }
+                    listOfPrivileges.clear();
+                    listOfPrivileges.push_back(filter.privilege);
+                }
+
+                LogDebug("Privileges matching filter - " << filter.privilege << ": " << listOfPrivileges.size());
+
+                for (const std::string &privilege : listOfPrivileges) {
+                    LogDebug("Privilege: " << privilege);
+                    policy_entry pe;
+
+                    pe.appId = appId;
+                    pe.user = userStr;
+                    pe.privilege = privilege;
+
+                    pe.currentLevel = CynaraAdmin::getInstance().convertToPolicyDescription(
+                        CynaraAdmin::getInstance().GetPrivilegeManagerCurrLevel(
+                            smackLabelForApp, userStr, privilege));
+
+                    pe.maxLevel = CynaraAdmin::getInstance().convertToPolicyDescription(
+                        CynaraAdmin::getInstance().GetPrivilegeManagerMaxLevel(
+                            smackLabelForApp, userStr, privilege));
+
+                    LogDebug(
+                        "[policy_entry] app: " << pe.appId
+                        << " user: " << pe.user
+                        << " privilege: " << pe.privilege
+                        << " current: " << pe.currentLevel
+                        << " max: " << pe.maxLevel
+                        );
+
+                    policyEntries.push_back(pe);
+                };
+            };
+        };
+
+    } catch (const CynaraException::Base &e) {
+        LogError("Error while listing Cynara rules: " << e.DumpToString());
+        return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
+    } catch (const std::bad_alloc &e) {
+        LogError("Memory allocation error while listing Cynara rules: " << e.what());
+        return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
+    }
+
+    return SECURITY_MANAGER_API_SUCCESS;
+}
+
+int policyGetDesc(std::vector<std::string> &levels)
+{
+    int ret = SECURITY_MANAGER_API_SUCCESS;
+
+    try {
+        CynaraAdmin::getInstance().ListPoliciesDescriptions(levels);
+    } catch (const CynaraException::OutOfMemory &e) {
+        LogError("Error - out of memory while querying Cynara for policy descriptions list: " << e.DumpToString());
+        return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
+    } catch (const CynaraException::InvalidParam &e) {
+        LogError("Error - invalid parameter while querying Cynara for policy descriptions list: " << e.DumpToString());
+        return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
+    } catch (const CynaraException::ServiceNotAvailable &e) {
+        LogError("Error - service not available while querying Cynara for policy descriptions list: " << e.DumpToString());
+        return SECURITY_MANAGER_API_ERROR_NO_SUCH_SERVICE;
+    } catch (const CynaraException::Base &e) {
+        LogError("Error while getting policy descriptions list from Cynara: " << e.DumpToString());
+        return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
+    }
+
     return ret;
 }