<commit_before>/*
 *  Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *  Contact: <NAME> <<EMAIL>>
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 */
/*
 * @file        service_impl.cpp
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 * @brief       Implementation of the service methods
 */

#include <fcntl.h>
#include <grp.h>
#include <linux/xattr.h>
#include <limits.h>
#include <pwd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <cstring>
#include <algorithm>

#include <dpl/log/log.h>
#include <dpl/errno_string.h>

#include <privilege_info.h>
#include <sys/smack.h>

#include <config.h>
#include "protocols.h"
#include "privilege_db.h"
#include "cynara.h"
#include "permissible-set.h"
#include "smack-rules.h"
#include "smack-labels.h"
#include "security-manager.h"
#include "tzplatform-config.h"
#include "utils.h"

#include "service_impl.h"

namespace SecurityManager {

namespace {

bool fileExists(const std::string &path) {
    struct stat buffer;
    return (stat(path.c_str(), &buffer) == 0) && S_ISREG(buffer.st_mode);
}

class ScopedTransaction {
public:
    ScopedTransaction(PrivilegeDb &priviligeDb) : m_isCommited(false), m_priviligeDb(priviligeDb) {
        m_priviligeDb.BeginTransaction();
    }
    ScopedTransaction(const ScopedTransaction &other) = delete;
    ScopedTransaction& operation(const ScopedTransaction &other) = delete;

    void commit() {
        m_priviligeDb.CommitTransaction();
        m_isCommited = true;
    }
    ~ScopedTransaction() {
        if (!m_isCommited) {
            try {
                m_priviligeDb.RollbackTransaction();
            } catch (const SecurityManager::Exception &e) {
                LogError("Transaction rollback failed: " << e.GetMessage());
            } catch(...) {
                LogError("Transaction rollback failed with unknown exception");
            }
        }
    }
private:
    bool m_isCommited;
    PrivilegeDb &m_priviligeDb;
};

} // end of anonymous namespace

ServiceImpl::ServiceImpl()
{
}

ServiceImpl::~ServiceImpl()
{
}

int ServiceImpl::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.appName
            << " user: " << policyEntry.user
            << " privilege: " << policyEntry.privilege
            << " current: " << policyEntry.currentLevel
            << " max: " << policyEntry.maxLevel);
    //automagically fill missing fields:
    if (policyEntry.user.empty()) {
        policyEntry.user = uidStr;
    };

    int level;

    if (policyEntry.currentLevel.empty()) { //for admin
        if (policyEntry.appName.empty()
            || policyEntry.privilege.empty()) {
            LogError("Bad admin update request");
            return SECURITY_MANAGER_ERROR_BAD_REQUEST;
        };

        if (!policyEntry.maxLevel.compare(SECURITY_MANAGER_DELETE)) {
            level = CYNARA_ADMIN_DELETE;
        } else {
            try {
                level = m_cynaraAdmin.convertToPolicyType(policyEntry.maxLevel);
            } catch (const std::out_of_range& e) {
                LogError("policy max level cannot be: " << policyEntry.maxLevel);
                return SECURITY_MANAGER_ERROR_INPUT_PARAM;
            };
        };
        forAdmin = true;

    } else if (policyEntry.maxLevel.empty()) { //for self
        if (policyEntry.user.compare(uidStr)
            || !policyEntry.appName.compare(SECURITY_MANAGER_ANY)
            || !policyEntry.privilege.compare(SECURITY_MANAGER_ANY)
            || policyEntry.appName.empty()
            || policyEntry.privilege.empty()) {
            LogError("Bad privacy manager update request");
            return SECURITY_MANAGER_ERROR_BAD_REQUEST;
        };

        if (!policyEntry.currentLevel.compare(SECURITY_MANAGER_DELETE)) {
            level = CYNARA_ADMIN_DELETE;
        } else {
            try {
                level = m_cynaraAdmin.convertToPolicyType(policyEntry.currentLevel);
            } catch (const std::out_of_range& e) {
                LogError("policy current level cannot be: " << policyEntry.currentLevel);
                return SECURITY_MANAGER_ERROR_INPUT_PARAM;
            };
        };
        forAdmin = false;

    } else { //neither => bad request
        return SECURITY_MANAGER_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;

    std::string cynaraClient;
    if (policyEntry.appName.compare(SECURITY_MANAGER_ANY)) {
        cynaraClient = getAppProcessLabel(policyEntry.appName);
        if (cynaraClient.empty()) {
            LogWarning("Cannot set policy for unknown application " << policyEntry.appName);
            return SECURITY_MANAGER_ERROR_APP_UNKNOWN;
        }
    } else {
        cynaraClient = CYNARA_ADMIN_WILDCARD;
    }

    cyap = std::move(CynaraAdminPolicy(
        cynaraClient,
        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_SUCCESS;
}

std::string ServiceImpl::getAppProcessLabel(const std::string &appName, const std::string &pkgName)
{
    bool isPkgHybrid = m_priviligeDb.IsPackageHybrid(pkgName);
    return SmackLabels::generateProcessLabel(appName, pkgName, isPkgHybrid);
}

std::string ServiceImpl::getAppProcessLabel(const std::string &appName)
{
    std::string pkgName;
    m_priviligeDb.GetAppPkgName(appName, pkgName);
    if (pkgName.empty()) {
        LogWarning("Cannot create label for unknown application: " << appName);
        return "";
    }
    return getAppProcessLabel(appName, pkgName);
}

bool ServiceImpl::sharingExists(const std::string &targetAppName, const std::string &path)
{
    int targetPathCount;
    m_priviligeDb.GetTargetPathSharingCount(targetAppName, path, targetPathCount);
    return targetPathCount != 0;
}

void ServiceImpl::getPkgsProcessLabels(const std::vector<PkgInfo> &pkgsInfo, SmackRules::PkgsLabels &pkgsLabels)
{
    pkgsLabels.resize(pkgsInfo.size());
    for (size_t i = 0; i < pkgsInfo.size(); ++i) {
        pkgsLabels[i].first = pkgsInfo[i].name;
        m_priviligeDb.GetPkgApps(pkgsLabels[i].first, pkgsLabels[i].second);
        for (auto &appName : pkgsLabels[i].second) {
            std::string label = SmackLabels::generateProcessLabel(appName, pkgsLabels[i].first,
                                                                  pkgsInfo[i].hybrid);
            appName = label;
        }
    }
}

bool ServiceImpl::authenticate(const Credentials &creds, const std::string &privilege)
{
    if (creds.authenticated)
        return true;
    return m_cynara.check(creds.label, privilege,
        std::to_string(creds.uid), std::to_string(creds.pid));
}

uid_t ServiceImpl::getGlobalUserId(void)
{
    static uid_t globaluid = TizenPlatformConfig::getUid(TZ_SYS_GLOBALAPP_USER);
    return globaluid;
}

bool ServiceImpl::isSubDir(const std::string &parent, const std::string &subdir)
{
    const char *str1 = parent.c_str();
    const char *str2 = subdir.c_str();

    while (*str1 && *str2)
        if (*str1++ != *str2++)
            return false;

    return (*str2 == '/' || *str1 == *str2);
}

std::string ServiceImpl::realPath(const std::string &path)
{
    auto real_pathPtr = makeUnique(realpath(path.c_str(), nullptr), free);
    if (!real_pathPtr) {
        LogError("Error in realpath(): " << GetErrnoString(errno) << " for: " << path);
        return std::string();
    }

    return real_pathPtr.get();
}

bool ServiceImpl::getUserPkgDir(const uid_t &uid,
                                const std::string &pkgName,
                                app_install_type installType,
                                std::string &userPkgDir)
{
    TizenPlatformConfig tpc(uid);

    enum tzplatform_variable id;

    switch (installType) {
    case SM_APP_INSTALL_LOCAL:
        id = TZ_USER_APP;
        break;
    case SM_APP_INSTALL_GLOBAL:
        id = TZ_SYS_RW_APP;
        break;
    case SM_APP_INSTALL_PRELOADED:
        id = TZ_SYS_RO_APP;
        break;
    default:
        LogError("Unsupported installation type: " << installType);
        return false;
    }

    userPkgDir = std::move(realPath(tpc.ctxGetEnv(id)));
    if (userPkgDir.empty())
        return false;

    userPkgDir.append("/").append(pkgName);

    return true;
}

void ServiceImpl::getSkelPkgDir(const std::string &pkgName,
                                std::string &skelPkgDir)
{
    std::string app = TizenPlatformConfig::getEnv(TZ_USER_APP);
    std::string home = TizenPlatformConfig::getEnv(TZ_USER_HOME);

    skelPkgDir.assign(app);
    skelPkgDir.replace(0, home.length(), Config::SKEL_DIR);
    skelPkgDir.append("/").append(pkgName);
}

void ServiceImpl::setRequestDefaultValues(uid_t& uid, int& installationType)
{
    uid_t globalUid = getGlobalUserId();

    if (installationType == SM_APP_INSTALL_NONE)
        installationType = ((uid == 0) || (uid == globalUid)) ? SM_APP_INSTALL_GLOBAL :
                SM_APP_INSTALL_LOCAL;
    if ((installationType == SM_APP_INSTALL_GLOBAL)
        || (installationType == SM_APP_INSTALL_PRELOADED))
        uid = globalUid;
}

void ServiceImpl::installRequestMangle(app_inst_req &req, std::string &cynaraUserStr)
{
    setRequestDefaultValues(req.uid, req.installationType);

    if (req.installationType == SM_APP_INSTALL_GLOBAL
        || req.installationType == SM_APP_INSTALL_PRELOADED) {
        LogDebug("Installation type: global installation");
        cynaraUserStr = CYNARA_ADMIN_WILDCARD;
    } else if (req.installationType == SM_APP_INSTALL_LOCAL) {
        LogDebug("Installation type: local installation");
        cynaraUserStr = std::to_string(static_cast<unsigned int>(req.uid));
    } else
        LogError("Installation type: unknown");
}

bool ServiceImpl::authCheck(const Credentials &creds,
                            const uid_t& uid,
                            int installationType)
{
    if (installationType == SM_APP_INSTALL_LOCAL) {
        if (!authenticate(creds, Config::PRIVILEGE_APPINST_USER)) {
            LogError("Caller is not permitted to manage local applications");
            return false;
        }
        if (uid != creds.uid && !authenticate(creds, Config::PRIVILEGE_USER_ADMIN)) {
            LogError("Caller is not permitted to manage applications for other users");
            return false;
        }
        if (uid == getGlobalUserId()) {
            LogError("Request local installation for global uid=" << uid);
            return false;
        }
    } else {
        if (!authenticate(creds, Config::PRIVILEGE_APPINST_ADMIN)) {
            LogError("Caller is not permitted to manage global applications");
            return false;
        }
    }

    return true;
}

bool ServiceImpl::pathsCheck(const pkg_paths &requestedPaths,
    const std::vector<std::string> &allowedDirs)
{
    LogDebug("Validating installation paths. Allowed directories: ");
    for (const auto &dir : allowedDirs)
        LogDebug("- " << dir);

    for (const auto &path : requestedPaths) {
        LogDebug("Requested path is '" << path.first.c_str() << "'");
        bool allowed = std::any_of(allowedDirs.begin(), allowedDirs.end(),
            std::bind(isSubDir, std::placeholders::_1, realPath(path.first)));

        if (!allowed) {
            LogWarning("Installation path " << path.first << " is outside allowed directories");
            return false;
        }
    }
    return true;
}

int ServiceImpl::labelPaths(const pkg_paths &paths,
                            const std::string &pkgName,
                            app_install_type installationType,
                            const uid_t &uid)
{
    try {
        std::string pkgBasePath;
        int authorId;

        if (!m_priviligeDb.PkgNameExists(pkgName)) {
            LogError("No such package: " << pkgName);
            return SECURITY_MANAGER_ERROR_INPUT_PARAM;
        }

        m_priviligeDb.GetPkgAuthorId(pkgName, authorId);

        if (!getUserPkgDir(uid, pkgName, installationType, pkgBasePath))
            return SECURITY_MANAGER_ERROR_SERVER_ERROR;

        // check if paths are inside
        bool pathsOK;
        if (installationType == SM_APP_INSTALL_LOCAL)
            pathsOK = pathsCheck(paths, {pkgBasePath});
        else {
            std::string skelPkgBasePath;
            getSkelPkgDir(pkgName, skelPkgBasePath);
            pathsOK = pathsCheck(paths, {pkgBasePath, skelPkgBasePath});
        }

        if (!pathsOK)
            return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED;

        if (!paths.empty())
            SmackLabels::setupPkgBasePath(pkgBasePath);

        // register paths
        for (const auto &pkgPath : paths) {
            const std::string &path = pkgPath.first;
            app_install_path_type pathType = static_cast<app_install_path_type>(pkgPath.second);
            SmackLabels::setupPath(pkgName, path, pathType, authorId);
        }
        return SECURITY_MANAGER_SUCCESS;
    } catch (const PrivilegeDb::Exception::Base &e) {
        LogError("Database error: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_SERVER_ERROR;
    } catch (const SmackException::InvalidParam &e) {
        LogError("Invalid parameter during labeling: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_INPUT_PARAM;
    } catch (const SmackException::InvalidPathType &e) {
        LogError("Invalid path type: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_INPUT_PARAM;
    } catch (const SmackException::Base &e) {
        LogError("Error while generating Smack labels: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_SETTING_FILE_LABEL_FAILED;
    } catch (const std::bad_alloc &e) {
        LogError("Memory allocation failed: " << e.what());
        return SECURITY_MANAGER_ERROR_MEMORY;
    }
}

bool ServiceImpl::isPrivilegePrivacy(const std::string &privilege)
{
    if (Config::IS_ASKUSER_ENABLED) {
        int ret = privilege_info_is_privacy(privilege.c_str());
        if (ret == 1)
            return true;
        if (ret != 0)
            LogError("privilege_info_is_privacy called with " << privilege << " returned error: " << ret);
        // FIXME: we should probably disallow such installation where privilege is not known
        // However, currently privielge-checker seems to return -1 with so many real privileges
        // that it would make ask-user testing impossible.
    }
    return false;
}

bool ServiceImpl::isSharedRO(const pkg_paths& paths)
{
    for (const auto& pkgPath : paths) {
        auto pathType = static_cast<app_install_path_type>(pkgPath.second);
        if (pathType == SECURITY_MANAGER_PATH_OWNER_RW_OTHER_RO)
            return true;
    }

    return false;
}

void ServiceImpl::getPkgLabels(const std::string &pkgName, SmackRules::Labels &pkgsLabels)
{
    bool isPkgHybrid = m_priviligeDb.IsPackageHybrid(pkgName);
    if (isPkgHybrid) {
        std::vector<std::string> apps;
        m_priviligeDb.GetPkgApps(pkgName, apps);
        for (auto &app : apps) {
            auto appLabel = SmackLabels::generateProcessLabel(app, pkgName, isPkgHybrid);
            app = appLabel;
        }
        pkgsLabels = std::move(apps);
    } else {
        pkgsLabels.push_back(SmackLabels::generateProcessLabel("", pkgName, false));
    }
}

void ServiceImpl::updatePermissibleSet(uid_t uid, int type)
{
    std::vector<std::string> userPkgs;
    m_priviligeDb.GetUserPkgs(uid, userPkgs);
    std::vector<std::string> labelsForUser;
    for (const auto &pkg : userPkgs) {
        std::vector<std::string> pkgLabels;
        getPkgLabels(pkg, pkgLabels);
        labelsForUser.insert(labelsForUser.end(), pkgLabels.begin(), pkgLabels.end());
    }
    PermissibleSet::updatePermissibleFile(uid, type, labelsForUser);
}

int ServiceImpl::appInstall(const Credentials &creds, app_inst_req &&req)
{
    std::vector<std::string> addedPermissions;
    std::vector<std::string> removedPermissions;
    SmackRules::Labels pkgLabels;
    std::string cynaraUserStr;
    std::string pkgBasePath;
    std::string appLabel;
    std::string pkgLabel;
    SmackRules::PkgsLabels pkgsProcessLabels;
    int authorId;
    std::vector<PkgInfo> pkgsInfo;
    bool hasSharedRO = isSharedRO(req.pkgPaths);<commit_msg>Remove unused local variables in service_impl.cpp<commit_after>/*
 *  Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved
 *
 *  Contact: <NAME> <<EMAIL>>
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License
 */
/*
 * @file        service_impl.cpp
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 * @author      <NAME> <<EMAIL>>
 * @brief       Implementation of the service methods
 */

#include <fcntl.h>
#include <grp.h>
#include <linux/xattr.h>
#include <limits.h>
#include <pwd.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#include <cstring>
#include <algorithm>

#include <dpl/log/log.h>
#include <dpl/errno_string.h>

#include <privilege_info.h>
#include <sys/smack.h>

#include <config.h>
#include "protocols.h"
#include "privilege_db.h"
#include "cynara.h"
#include "permissible-set.h"
#include "smack-rules.h"
#include "smack-labels.h"
#include "security-manager.h"
#include "tzplatform-config.h"
#include "utils.h"

#include "service_impl.h"

namespace SecurityManager {

namespace {

bool fileExists(const std::string &path) {
    struct stat buffer;
    return (stat(path.c_str(), &buffer) == 0) && S_ISREG(buffer.st_mode);
}

class ScopedTransaction {
public:
    ScopedTransaction(PrivilegeDb &priviligeDb) : m_isCommited(false), m_priviligeDb(priviligeDb) {
        m_priviligeDb.BeginTransaction();
    }
    ScopedTransaction(const ScopedTransaction &other) = delete;
    ScopedTransaction& operation(const ScopedTransaction &other) = delete;

    void commit() {
        m_priviligeDb.CommitTransaction();
        m_isCommited = true;
    }
    ~ScopedTransaction() {
        if (!m_isCommited) {
            try {
                m_priviligeDb.RollbackTransaction();
            } catch (const SecurityManager::Exception &e) {
                LogError("Transaction rollback failed: " << e.GetMessage());
            } catch(...) {
                LogError("Transaction rollback failed with unknown exception");
            }
        }
    }
private:
    bool m_isCommited;
    PrivilegeDb &m_priviligeDb;
};

} // end of anonymous namespace

ServiceImpl::ServiceImpl()
{
}

ServiceImpl::~ServiceImpl()
{
}

int ServiceImpl::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.appName
            << " user: " << policyEntry.user
            << " privilege: " << policyEntry.privilege
            << " current: " << policyEntry.currentLevel
            << " max: " << policyEntry.maxLevel);
    //automagically fill missing fields:
    if (policyEntry.user.empty()) {
        policyEntry.user = uidStr;
    };

    int level;

    if (policyEntry.currentLevel.empty()) { //for admin
        if (policyEntry.appName.empty()
            || policyEntry.privilege.empty()) {
            LogError("Bad admin update request");
            return SECURITY_MANAGER_ERROR_BAD_REQUEST;
        };

        if (!policyEntry.maxLevel.compare(SECURITY_MANAGER_DELETE)) {
            level = CYNARA_ADMIN_DELETE;
        } else {
            try {
                level = m_cynaraAdmin.convertToPolicyType(policyEntry.maxLevel);
            } catch (const std::out_of_range& e) {
                LogError("policy max level cannot be: " << policyEntry.maxLevel);
                return SECURITY_MANAGER_ERROR_INPUT_PARAM;
            };
        };
        forAdmin = true;

    } else if (policyEntry.maxLevel.empty()) { //for self
        if (policyEntry.user.compare(uidStr)
            || !policyEntry.appName.compare(SECURITY_MANAGER_ANY)
            || !policyEntry.privilege.compare(SECURITY_MANAGER_ANY)
            || policyEntry.appName.empty()
            || policyEntry.privilege.empty()) {
            LogError("Bad privacy manager update request");
            return SECURITY_MANAGER_ERROR_BAD_REQUEST;
        };

        if (!policyEntry.currentLevel.compare(SECURITY_MANAGER_DELETE)) {
            level = CYNARA_ADMIN_DELETE;
        } else {
            try {
                level = m_cynaraAdmin.convertToPolicyType(policyEntry.currentLevel);
            } catch (const std::out_of_range& e) {
                LogError("policy current level cannot be: " << policyEntry.currentLevel);
                return SECURITY_MANAGER_ERROR_INPUT_PARAM;
            };
        };
        forAdmin = false;

    } else { //neither => bad request
        return SECURITY_MANAGER_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;

    std::string cynaraClient;
    if (policyEntry.appName.compare(SECURITY_MANAGER_ANY)) {
        cynaraClient = getAppProcessLabel(policyEntry.appName);
        if (cynaraClient.empty()) {
            LogWarning("Cannot set policy for unknown application " << policyEntry.appName);
            return SECURITY_MANAGER_ERROR_APP_UNKNOWN;
        }
    } else {
        cynaraClient = CYNARA_ADMIN_WILDCARD;
    }

    cyap = std::move(CynaraAdminPolicy(
        cynaraClient,
        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_SUCCESS;
}

std::string ServiceImpl::getAppProcessLabel(const std::string &appName, const std::string &pkgName)
{
    bool isPkgHybrid = m_priviligeDb.IsPackageHybrid(pkgName);
    return SmackLabels::generateProcessLabel(appName, pkgName, isPkgHybrid);
}

std::string ServiceImpl::getAppProcessLabel(const std::string &appName)
{
    std::string pkgName;
    m_priviligeDb.GetAppPkgName(appName, pkgName);
    if (pkgName.empty()) {
        LogWarning("Cannot create label for unknown application: " << appName);
        return "";
    }
    return getAppProcessLabel(appName, pkgName);
}

bool ServiceImpl::sharingExists(const std::string &targetAppName, const std::string &path)
{
    int targetPathCount;
    m_priviligeDb.GetTargetPathSharingCount(targetAppName, path, targetPathCount);
    return targetPathCount != 0;
}

void ServiceImpl::getPkgsProcessLabels(const std::vector<PkgInfo> &pkgsInfo, SmackRules::PkgsLabels &pkgsLabels)
{
    pkgsLabels.resize(pkgsInfo.size());
    for (size_t i = 0; i < pkgsInfo.size(); ++i) {
        pkgsLabels[i].first = pkgsInfo[i].name;
        m_priviligeDb.GetPkgApps(pkgsLabels[i].first, pkgsLabels[i].second);
        for (auto &appName : pkgsLabels[i].second) {
            std::string label = SmackLabels::generateProcessLabel(appName, pkgsLabels[i].first,
                                                                  pkgsInfo[i].hybrid);
            appName = label;
        }
    }
}

bool ServiceImpl::authenticate(const Credentials &creds, const std::string &privilege)
{
    if (creds.authenticated)
        return true;
    return m_cynara.check(creds.label, privilege,
        std::to_string(creds.uid), std::to_string(creds.pid));
}

uid_t ServiceImpl::getGlobalUserId(void)
{
    static uid_t globaluid = TizenPlatformConfig::getUid(TZ_SYS_GLOBALAPP_USER);
    return globaluid;
}

bool ServiceImpl::isSubDir(const std::string &parent, const std::string &subdir)
{
    const char *str1 = parent.c_str();
    const char *str2 = subdir.c_str();

    while (*str1 && *str2)
        if (*str1++ != *str2++)
            return false;

    return (*str2 == '/' || *str1 == *str2);
}

std::string ServiceImpl::realPath(const std::string &path)
{
    auto real_pathPtr = makeUnique(realpath(path.c_str(), nullptr), free);
    if (!real_pathPtr) {
        LogError("Error in realpath(): " << GetErrnoString(errno) << " for: " << path);
        return std::string();
    }

    return real_pathPtr.get();
}

bool ServiceImpl::getUserPkgDir(const uid_t &uid,
                                const std::string &pkgName,
                                app_install_type installType,
                                std::string &userPkgDir)
{
    TizenPlatformConfig tpc(uid);

    enum tzplatform_variable id;

    switch (installType) {
    case SM_APP_INSTALL_LOCAL:
        id = TZ_USER_APP;
        break;
    case SM_APP_INSTALL_GLOBAL:
        id = TZ_SYS_RW_APP;
        break;
    case SM_APP_INSTALL_PRELOADED:
        id = TZ_SYS_RO_APP;
        break;
    default:
        LogError("Unsupported installation type: " << installType);
        return false;
    }

    userPkgDir = std::move(realPath(tpc.ctxGetEnv(id)));
    if (userPkgDir.empty())
        return false;

    userPkgDir.append("/").append(pkgName);

    return true;
}

void ServiceImpl::getSkelPkgDir(const std::string &pkgName,
                                std::string &skelPkgDir)
{
    std::string app = TizenPlatformConfig::getEnv(TZ_USER_APP);
    std::string home = TizenPlatformConfig::getEnv(TZ_USER_HOME);

    skelPkgDir.assign(app);
    skelPkgDir.replace(0, home.length(), Config::SKEL_DIR);
    skelPkgDir.append("/").append(pkgName);
}

void ServiceImpl::setRequestDefaultValues(uid_t& uid, int& installationType)
{
    uid_t globalUid = getGlobalUserId();

    if (installationType == SM_APP_INSTALL_NONE)
        installationType = ((uid == 0) || (uid == globalUid)) ? SM_APP_INSTALL_GLOBAL :
                SM_APP_INSTALL_LOCAL;
    if ((installationType == SM_APP_INSTALL_GLOBAL)
        || (installationType == SM_APP_INSTALL_PRELOADED))
        uid = globalUid;
}

void ServiceImpl::installRequestMangle(app_inst_req &req, std::string &cynaraUserStr)
{
    setRequestDefaultValues(req.uid, req.installationType);

    if (req.installationType == SM_APP_INSTALL_GLOBAL
        || req.installationType == SM_APP_INSTALL_PRELOADED) {
        LogDebug("Installation type: global installation");
        cynaraUserStr = CYNARA_ADMIN_WILDCARD;
    } else if (req.installationType == SM_APP_INSTALL_LOCAL) {
        LogDebug("Installation type: local installation");
        cynaraUserStr = std::to_string(static_cast<unsigned int>(req.uid));
    } else
        LogError("Installation type: unknown");
}

bool ServiceImpl::authCheck(const Credentials &creds,
                            const uid_t& uid,
                            int installationType)
{
    if (installationType == SM_APP_INSTALL_LOCAL) {
        if (!authenticate(creds, Config::PRIVILEGE_APPINST_USER)) {
            LogError("Caller is not permitted to manage local applications");
            return false;
        }
        if (uid != creds.uid && !authenticate(creds, Config::PRIVILEGE_USER_ADMIN)) {
            LogError("Caller is not permitted to manage applications for other users");
            return false;
        }
        if (uid == getGlobalUserId()) {
            LogError("Request local installation for global uid=" << uid);
            return false;
        }
    } else {
        if (!authenticate(creds, Config::PRIVILEGE_APPINST_ADMIN)) {
            LogError("Caller is not permitted to manage global applications");
            return false;
        }
    }

    return true;
}

bool ServiceImpl::pathsCheck(const pkg_paths &requestedPaths,
    const std::vector<std::string> &allowedDirs)
{
    LogDebug("Validating installation paths. Allowed directories: ");
    for (const auto &dir : allowedDirs)
        LogDebug("- " << dir);

    for (const auto &path : requestedPaths) {
        LogDebug("Requested path is '" << path.first.c_str() << "'");
        bool allowed = std::any_of(allowedDirs.begin(), allowedDirs.end(),
            std::bind(isSubDir, std::placeholders::_1, realPath(path.first)));

        if (!allowed) {
            LogWarning("Installation path " << path.first << " is outside allowed directories");
            return false;
        }
    }
    return true;
}

int ServiceImpl::labelPaths(const pkg_paths &paths,
                            const std::string &pkgName,
                            app_install_type installationType,
                            const uid_t &uid)
{
    try {
        std::string pkgBasePath;
        int authorId;

        if (!m_priviligeDb.PkgNameExists(pkgName)) {
            LogError("No such package: " << pkgName);
            return SECURITY_MANAGER_ERROR_INPUT_PARAM;
        }

        m_priviligeDb.GetPkgAuthorId(pkgName, authorId);

        if (!getUserPkgDir(uid, pkgName, installationType, pkgBasePath))
            return SECURITY_MANAGER_ERROR_SERVER_ERROR;

        // check if paths are inside
        bool pathsOK;
        if (installationType == SM_APP_INSTALL_LOCAL)
            pathsOK = pathsCheck(paths, {pkgBasePath});
        else {
            std::string skelPkgBasePath;
            getSkelPkgDir(pkgName, skelPkgBasePath);
            pathsOK = pathsCheck(paths, {pkgBasePath, skelPkgBasePath});
        }

        if (!pathsOK)
            return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED;

        if (!paths.empty())
            SmackLabels::setupPkgBasePath(pkgBasePath);

        // register paths
        for (const auto &pkgPath : paths) {
            const std::string &path = pkgPath.first;
            app_install_path_type pathType = static_cast<app_install_path_type>(pkgPath.second);
            SmackLabels::setupPath(pkgName, path, pathType, authorId);
        }
        return SECURITY_MANAGER_SUCCESS;
    } catch (const PrivilegeDb::Exception::Base &e) {
        LogError("Database error: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_SERVER_ERROR;
    } catch (const SmackException::InvalidParam &e) {
        LogError("Invalid parameter during labeling: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_INPUT_PARAM;
    } catch (const SmackException::InvalidPathType &e) {
        LogError("Invalid path type: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_INPUT_PARAM;
    } catch (const SmackException::Base &e) {
        LogError("Error while generating Smack labels: " << e.DumpToString());
        return SECURITY_MANAGER_ERROR_SETTING_FILE_LABEL_FAILED;
    } catch (const std::bad_alloc &e) {
        LogError("Memory allocation failed: " << e.what());
        return SECURITY_MANAGER_ERROR_MEMORY;
    }
}

bool ServiceImpl::isPrivilegePrivacy(const std::string &privilege)
{
    if (Config::IS_ASKUSER_ENABLED) {
        int ret = privilege_info_is_privacy(privilege.c_str());
        if (ret == 1)
            return true;
        if (ret != 0)
            LogError("privilege_info_is_privacy called with " << privilege << " returned error: " << ret);
        // FIXME: we should probably disallow such installation where privilege is not known
        // However, currently privielge-checker seems to return -1 with so many real privileges
        // that it would make ask-user testing impossible.
    }
    return false;
}

bool ServiceImpl::isSharedRO(const pkg_paths& paths)
{
    for (const auto& pkgPath : paths) {
        auto pathType = static_cast<app_install_path_type>(pkgPath.second);
        if (pathType == SECURITY_MANAGER_PATH_OWNER_RW_OTHER_RO)
            return true;
    }

    return false;
}

void ServiceImpl::getPkgLabels(const std::string &pkgName, SmackRules::Labels &pkgsLabels)
{
    bool isPkgHybrid = m_priviligeDb.IsPackageHybrid(pkgName);
    if (isPkgHybrid) {
        std::vector<std::string> apps;
        m_priviligeDb.GetPkgApps(pkgName, apps);
        for (auto &app : apps) {
            auto appLabel = SmackLabels::generateProcessLabel(app, pkgName, isPkgHybrid);
            app = appLabel;
        }
        pkgsLabels = std::move(apps);
    } else {
        pkgsLabels.push_back(SmackLabels::generateProcessLabel("", pkgName, false));
    }
}

void ServiceImpl::updatePermissibleSet(uid_t uid, int type)
{
    std::vector<std::string> userPkgs;
    m_priviligeDb.GetUserPkgs(uid, userPkgs);
    std::vector<std::string> labelsForUser;
    for (const auto &pkg : userPkgs) {
        std::vector<std::string> pkgLabels;
        getPkgLabels(pkg, pkgLabels);
        labelsForUser.insert(labelsForUser.end(), pkgLabels.begin(), pkgLabels.end());
    }
    PermissibleSet::updatePermissibleFile(uid, type, labelsForUser);
}

int ServiceImpl::appInstall(const Credentials &creds, app_inst_req &&req)
{
    SmackRules::Labels pkgLabels;
    std::string cynaraUserStr;
    std::string pkgBasePath;
    std::string appLabel;
    std::string pkgLabel;
    SmackRules::PkgsLabels pkgsProcessLabels;
    int authorId;
    std::vector<PkgInfo> pkgsInfo;
    bool hasSharedRO = isSharedRO(req.pkgPaths);
{'fraction_non_alphanumeric': 0.11244226327944573, 'fraction_numerical': 0.0017321016166281756, 'mean_word_length': 2.936924650528469, 'pattern_counts': {'":': 0, '<': 225, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2105401', 'n_tokens_mistral': 10832, 'n_tokens_neox': 10599, 'n_words': 2182}