Implement service side of privileges mapping
[platform/core/security/security-manager.git] / src / server / service / service.cpp
index 664235f..45cdcf3 100644 (file)
  * @brief       Implementation of security-manager service.
  */
 
+#include <sys/socket.h>
+
 #include <dpl/log/log.h>
 #include <dpl/serialization.h>
+#include <sys/smack.h>
 
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <pwd.h>
-#include <limits.h>
-#include <cstring>
-
-#include "service.h"
+#include "connection.h"
 #include "protocols.h"
-#include "security-manager.h"
-#include "smack-common.h"
-#include "smack-rules.h"
-#include "smack-labels.h"
-#include "privilege_db.h"
-#include "cynara.h"
+#include "service.h"
+#include "service_impl.h"
+#include "master-req.h"
 
 namespace SecurityManager {
 
 const InterfaceID IFACE = 1;
 
-
-Service::Service()
+Service::Service(const bool isSlave):
+        m_isSlave(isSlave)
 {
 }
 
 GenericSocketService::ServiceDescriptionVector Service::GetServiceDescription()
 {
-    return ServiceDescriptionVector {
-        {SERVICE_SOCKET, "security-manager", IFACE},
-    };
-}
-
-void Service::accept(const AcceptEvent &event)
-{
-    LogDebug("Accept event. ConnectionID.sock: " << event.connectionID.sock <<
-             " ConnectionID.counter: " << event.connectionID.counter <<
-             " ServiceID: " << event.interfaceID);
-
-    auto &info = m_connectionInfoMap[event.connectionID.counter];
-    info.interfaceID = event.interfaceID;
-}
-
-void Service::write(const WriteEvent &event)
-{
-    LogDebug("WriteEvent. ConnectionID: " << event.connectionID.sock <<
-             " Size: " << event.size <<
-             " Left: " << event.left);
-
-    if (event.left == 0)
-        m_serviceManager->Close(event.connectionID);
-}
-
-void Service::process(const ReadEvent &event)
-{
-    LogDebug("Read event for counter: " << event.connectionID.counter);
-    auto &info = m_connectionInfoMap[event.connectionID.counter];
-    info.buffer.Push(event.rawBuffer);
-
-    // We can get several requests in one package.
-    // Extract and process them all
-    while (processOne(event.connectionID, info.buffer, info.interfaceID));
+    if (m_isSlave)
+        return ServiceDescriptionVector {
+            {SLAVE_SERVICE_SOCKET,  /* path */
+             "*",   /* smackLabel label (not used, we rely on systemd) */
+             IFACE, /* InterfaceID */
+             false, /* useSendMsg */
+             true}, /* systemdOnly */
+        };
+    else
+        return ServiceDescriptionVector {
+            {SERVICE_SOCKET,  /* path */
+             "*",   /* smackLabel label (not used, we rely on systemd) */
+             IFACE, /* InterfaceID */
+             false, /* useSendMsg */
+             true}, /* systemdOnly */
+        };
 }
 
-void Service::close(const CloseEvent &event)
+static bool getPeerID(int sock, uid_t &uid, pid_t &pid, std::string &smackLabel)
 {
-    LogDebug("CloseEvent. ConnectionID: " << event.connectionID.sock);
-    m_connectionInfoMap.erase(event.connectionID.counter);
-}
-
-static bool getPeerUserID(int sock, uid_t *uid) {
     struct ucred cr;
-    socklen_t len = sizeof (cr);
-    if (!uid) {
-        return false;
-    }
+    socklen_t len = sizeof(cr);
+
     if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &len)) {
-        *uid = cr.uid;
+        char *smk;
+        ssize_t ret = smack_new_label_from_socket(sock, &smk);
+        if (ret < 0)
+            return false;
+        smackLabel = smk;
+        uid = cr.uid;
+        pid = cr.pid;
+        free(smk);
         return true;
     }
+
     return false;
 }
 
@@ -121,9 +98,11 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer,
     bool retval = false;
 
     uid_t uid;
+    pid_t pid;
+    std::string smackLabel;
 
-    if(!getPeerUserID(conn.sock, &uid)) {
-        LogError("Closing socket because of error: unable to get peer's uid");
+    if (!getPeerID(conn.sock, uid, pid, smackLabel)) {
+        LogError("Closing socket because of error: unable to get peer's uid, pid or smack label");
         m_serviceManager->Close(conn);
         return false;
     }
@@ -136,6 +115,10 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer,
             SecurityModuleCall call_type = static_cast<SecurityModuleCall>(call_type_int);
 
             switch (call_type) {
+                case SecurityModuleCall::NOOP:
+                    LogDebug("call_type: SecurityModuleCall::NOOP");
+                    Serialization::Serialize(send, SECURITY_MANAGER_API_SUCCESS);
+                    break;
                 case SecurityModuleCall::APP_INSTALL:
                     LogDebug("call_type: SecurityModuleCall::APP_INSTALL");
                     processAppInstall(buffer, send, uid);
@@ -147,6 +130,33 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer,
                 case SecurityModuleCall::APP_GET_PKGID:
                     processGetPkgId(buffer, send);
                     break;
+                case SecurityModuleCall::APP_GET_GROUPS:
+                    processGetAppGroups(buffer, send, uid, pid);
+                    break;
+                case SecurityModuleCall::USER_ADD:
+                    processUserAdd(buffer, send, uid);
+                    break;
+                case SecurityModuleCall::USER_DELETE:
+                    processUserDelete(buffer, send, uid);
+                    break;
+                case SecurityModuleCall::POLICY_UPDATE:
+                    processPolicyUpdate(buffer, send, uid, pid, smackLabel);
+                    break;
+                case SecurityModuleCall::GET_CONF_POLICY_ADMIN:
+                    processGetConfiguredPolicy(buffer, send, uid, pid, smackLabel, true);
+                    break;
+                case SecurityModuleCall::GET_CONF_POLICY_SELF:
+                    processGetConfiguredPolicy(buffer, send, uid, pid, smackLabel, false);
+                    break;
+                case SecurityModuleCall::GET_POLICY:
+                    processGetPolicy(buffer, send, uid, pid, smackLabel);
+                    break;
+                case SecurityModuleCall::POLICY_GET_DESCRIPTIONS:
+                    processPolicyGetDesc(send);
+                    break;
+                case SecurityModuleCall::GET_PRIVILEGES_MAPPING:
+                    processPrivilegesMappings(buffer, send);
+                    break;
                 default:
                     LogError("Invalid call: " << call_type_int);
                     Throw(ServiceException::InvalidAction);
@@ -157,7 +167,7 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer,
             LogError("Broken protocol.");
         } Catch (ServiceException::Base) {
             LogError("Broken protocol.");
-        } catch (std::exception &e) {
+        } catch (const std::exception &e) {
             LogError("STD exception " << e.what());
         } catch (...) {
             LogError("Unknown exception");
@@ -178,263 +188,169 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer,
     return retval;
 }
 
-static inline bool isSubDir(const char *parent, const char *subdir)
+void Service::processAppInstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid)
 {
-    while (*parent && *subdir)
-        if (*parent++ != *subdir++)
-            return false;
+    app_inst_req req;
 
-    return (*subdir == '/');
+    Deserialization::Deserialize(buffer, req.appId);
+    Deserialization::Deserialize(buffer, req.pkgId);
+    Deserialization::Deserialize(buffer, req.privileges);
+    Deserialization::Deserialize(buffer, req.appPaths);
+    Deserialization::Deserialize(buffer, req.uid);
+    Serialization::Serialize(send, ServiceImpl::appInstall(req, uid, m_isSlave));
 }
 
-static inline bool installRequestAuthCheck(const app_inst_req &req, uid_t uid)
+void Service::processAppUninstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid)
 {
-    if (uid == 0)
-        return true;
+    std::string appId;
 
-    struct passwd *pwd;
-    do {
-        errno = 0;
-        pwd = getpwuid(uid);
-        if (!pwd && errno != EINTR) {
-            LogError("getpwuid failed with '" << uid
-                    << "' as paramter: " << strerror(errno));
-            return false;
-        }
-    } while (!pwd);
+    Deserialization::Deserialize(buffer, appId);
+    Serialization::Serialize(send, ServiceImpl::appUninstall(appId, uid, m_isSlave));
+}
 
-    std::unique_ptr<char, std::function<void(void*)>> home(
-        realpath(pwd->pw_dir, NULL), free);
-    if (!home.get()) {
-            LogError("realpath failed with '" << pwd->pw_dir
-                    << "' as paramter: " << strerror(errno));
-            return false;
-    }
+void Service::processGetPkgId(MessageBuffer &buffer, MessageBuffer &send)
+{
+    std::string appId;
+    std::string pkgId;
+    int ret;
 
-    for (const auto &appPath : req.appPaths) {
-        std::unique_ptr<char, std::function<void(void*)>> real_path(
-            realpath(appPath.first.c_str(), NULL), free);
-        if (!real_path.get()) {
-            LogError("realpath failed with '" << appPath.first.c_str()
-                    << "' as paramter: " << strerror(errno));
-            return false;
-        }
-        LogDebug("Requested path is '" << appPath.first.c_str()
-                << "'. User's HOME is '" << pwd->pw_dir << "'");
-        if (!isSubDir(home.get(), real_path.get())) {
-            LogWarning("User's apps may have registered folders only in user's home dir");
-            return false;
-        }
+    Deserialization::Deserialize(buffer, appId);
+    ret = ServiceImpl::getPkgId(appId, pkgId);
+    Serialization::Serialize(send, ret);
+    if (ret == SECURITY_MANAGER_API_SUCCESS)
+        Serialization::Serialize(send, pkgId);
+}
 
-        app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
-        if (pathType == SECURITY_MANAGER_PATH_PUBLIC) {
-            LogWarning("Only root can register SECURITY_MANAGER_PATH_PUBLIC path");
-            return false;
+void Service::processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid)
+{
+    std::string appId;
+    std::unordered_set<gid_t> gids;
+    int ret;
+
+    Deserialization::Deserialize(buffer, appId);
+    ret = ServiceImpl::getAppGroups(appId, uid, pid, m_isSlave, gids);
+    Serialization::Serialize(send, ret);
+    if (ret == SECURITY_MANAGER_API_SUCCESS) {
+        Serialization::Serialize(send, static_cast<int>(gids.size()));
+        for (const auto &gid : gids) {
+            Serialization::Serialize(send, gid);
         }
     }
-    return true;
 }
 
-bool Service::processAppInstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid)
+void Service::processUserAdd(MessageBuffer &buffer, MessageBuffer &send, uid_t uid)
 {
-    bool pkgIdIsNew = false;
-    std::vector<std::string> addedPermissions;
-    std::vector<std::string> removedPermissions;
+    int ret;
+    uid_t uidAdded;
+    int userType;
 
-    // deserialize request data
-    app_inst_req req;
-    Deserialization::Deserialize(buffer, req.appId);
-    Deserialization::Deserialize(buffer, req.pkgId);
-    Deserialization::Deserialize(buffer, req.privileges);
-    Deserialization::Deserialize(buffer, req.appPaths);
+    Deserialization::Deserialize(buffer, uidAdded);
+    Deserialization::Deserialize(buffer, userType);
 
-    if(!installRequestAuthCheck(req, uid)) {
-        LogError("Request from uid " << uid << " for app installation denied");
-        Serialization::Serialize(send, SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED);
-        return false;
-    }
+    ret = ServiceImpl::userAdd(uidAdded, userType, uid, m_isSlave);
+    Serialization::Serialize(send, ret);
+}
 
-    std::string smackLabel;
-    if (!generateAppLabel(req.pkgId, smackLabel)) {
-        LogError("Cannot generate Smack label for package: " << req.pkgId);
-        Serialization::Serialize(send, SECURITY_MANAGER_API_ERROR_SERVER_ERROR);
-        return false;
-    }
+void Service::processUserDelete(MessageBuffer &buffer, MessageBuffer &send, uid_t uid)
+{
+    int ret;
+    uid_t uidRemoved;
 
-    LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
-            << ", generated smack label: " << smackLabel);
+    Deserialization::Deserialize(buffer, uidRemoved);
 
-    // create null terminated array of strings for permissions
-    std::unique_ptr<const char *[]> pp_permissions(new const char* [req.privileges.size() + 1]);
-    for (size_t i = 0; i < req.privileges.size(); ++i) {
-        LogDebug("  Permission = " << req.privileges[i]);
-        pp_permissions[i] = req.privileges[i].c_str();
-    }
-    pp_permissions[req.privileges.size()] = nullptr;
-
-    try {
-        std::vector<std::string> oldPkgPrivileges, newPkgPrivileges;
-        std::string uidstr = uid ? std::to_string(static_cast<unsigned int>(uid))
-                             : CYNARA_ADMIN_WILDCARD;
-
-        LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
-                 << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
-
-        m_privilegeDb.BeginTransaction();
-        m_privilegeDb.GetPkgPrivileges(req.pkgId, uid, oldPkgPrivileges);
-        m_privilegeDb.AddApplication(req.appId, req.pkgId, uid, pkgIdIsNew);
-        m_privilegeDb.UpdateAppPrivileges(req.appId, uid, req.privileges);
-        m_privilegeDb.GetPkgPrivileges(req.pkgId, uid, newPkgPrivileges);
-        CynaraAdmin::UpdatePackagePolicy(req.pkgId, uidstr, oldPkgPrivileges,
-                                         newPkgPrivileges);
-        m_privilegeDb.CommitTransaction();
-        LogDebug("Application installation commited to database");
-    } catch (const PrivilegeDb::Exception::InternalError &e) {
-        m_privilegeDb.RollbackTransaction();
-        LogError("Error while saving application info to database: " << e.DumpToString());
-        goto error_label;
-    } catch (const CynaraException::Base &e) {
-        m_privilegeDb.RollbackTransaction();
-        LogError("Error while setting Cynara rules for application: " << e.DumpToString());
-        goto error_label;
-    } catch (const std::bad_alloc &e) {
-        m_privilegeDb.RollbackTransaction();
-        LogError("Memory allocation while setting Cynara rules for application: " << e.what());
-        goto error_label;
-    }
+    ret = ServiceImpl::userDelete(uidRemoved, uid, m_isSlave);
+    Serialization::Serialize(send, ret);
+}
 
-    // register paths
-    for (const auto &appPath : req.appPaths) {
-        const std::string &path = appPath.first;
-        app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
-        int result = setupPath(req.pkgId, path, pathType);
+void Service::processPolicyUpdate(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel)
+{
+    int ret;
+    std::vector<policy_entry> policyEntries;
 
-        if (!result) {
-            LogError("setupPath() failed");
-            goto error_label;
-        }
-    }
+    Deserialization::Deserialize(buffer, policyEntries);
 
-    if (pkgIdIsNew) {
-        LogDebug("Adding Smack rules for new pkgId " << req.pkgId);
-        if (!SmackRules::installPackageRules(req.pkgId)) {
-            LogError("Failed to apply package-specific smack rules");
-            goto error_label;
-        }
+    if (m_isSlave) {
+        ret = MasterReq::PolicyUpdate(policyEntries, uid, pid, smackLabel);
+    } else {
+        ret = ServiceImpl::policyUpdate(policyEntries, uid, pid, smackLabel);
     }
-
-    // success
-    Serialization::Serialize(send, SECURITY_MANAGER_API_SUCCESS);
-    return true;
-
-error_label:
-    Serialization::Serialize(send, SECURITY_MANAGER_API_ERROR_SERVER_ERROR);
-    return false;
+    Serialization::Serialize(send, ret);
 }
 
-bool Service::processAppUninstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid)
+void Service::processGetConfiguredPolicy(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel, bool forAdmin)
 {
-    // deserialize request data
-    std::string appId;
-    std::string pkgId;
-    std::string smackLabel;
-    bool appExists = true;
-    bool removePkg = false;
+    int ret;
+    policy_entry filter;
+    Deserialization::Deserialize(buffer, filter);
+    std::vector<policy_entry> policyEntries;
 
-    Deserialization::Deserialize(buffer, appId);
+    if (m_isSlave) {
+        ret = MasterReq::GetConfiguredPolicy(forAdmin, filter, uid, pid, smackLabel, policyEntries);
+    } else {
+        ret = ServiceImpl::getConfiguredPolicy(forAdmin, filter, uid, pid, smackLabel,
+                                               policyEntries);
+    }
 
-    try {
-        std::vector<std::string> oldPkgPrivileges, newPkgPrivileges;
+    Serialization::Serialize(send, ret);
+    Serialization::Serialize(send, static_cast<int>(policyEntries.size()));
+    for (const auto &policyEntry : policyEntries) {
+        Serialization::Serialize(send, policyEntry);
+    };
+}
 
-        m_privilegeDb.BeginTransaction();
-        if (!m_privilegeDb.GetAppPkgId(appId, pkgId)) {
-            LogWarning("Application " << appId <<
-                " not found in database while uninstalling");
-            m_privilegeDb.RollbackTransaction();
-            appExists = false;
-        } else {
-            if (!generateAppLabel(pkgId, smackLabel)) {
-                LogError("Cannot generate Smack label for package: " << pkgId);
-                goto error_label;
+void Service::processGetPolicy(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel)
+{
+    int ret;
+    policy_entry filter;
+    Deserialization::Deserialize(buffer, filter);
+    std::vector<policy_entry> policyEntries;
 
-            }
+    if (m_isSlave) {
+        ret = MasterReq::GetPolicy(filter, uid, pid, smackLabel, policyEntries);
+    } else {
+        ret = ServiceImpl::getPolicy(filter, uid, pid, smackLabel, policyEntries);
+    }
 
-            std::string uidstr = uid ? std::to_string(static_cast<unsigned int>(uid))
-                                 : CYNARA_ADMIN_WILDCARD;
+    Serialization::Serialize(send, ret);
+    Serialization::Serialize(send, static_cast<int>(policyEntries.size()));
+    for (const auto &policyEntry : policyEntries) {
+        Serialization::Serialize(send, policyEntry);
+    };
+}
 
-            LogDebug("Uninstall parameters: appId: " << appId << ", pkgId: " << pkgId
-                     << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
+void Service::processPolicyGetDesc(MessageBuffer &send)
+{
+    int ret;
+    std::vector<std::string> descriptions;
 
-            m_privilegeDb.GetPkgPrivileges(pkgId, uid, oldPkgPrivileges);
-            m_privilegeDb.UpdateAppPrivileges(appId, uid, std::vector<std::string>());
-            m_privilegeDb.RemoveApplication(appId, uid, removePkg);
-            m_privilegeDb.GetPkgPrivileges(pkgId, uid, newPkgPrivileges);
-            CynaraAdmin::UpdatePackagePolicy(pkgId, uidstr, oldPkgPrivileges,
-                                             newPkgPrivileges);
-            m_privilegeDb.CommitTransaction();
-            LogDebug("Application uninstallation commited to database");
-        }
-    } catch (const PrivilegeDb::Exception::InternalError &e) {
-        m_privilegeDb.RollbackTransaction();
-        LogError("Error while removing application info from database: " << e.DumpToString());
-        goto error_label;
-    } catch (const CynaraException::Base &e) {
-        m_privilegeDb.RollbackTransaction();
-        LogError("Error while setting Cynara rules for application: " << e.DumpToString());
-        goto error_label;
-    } catch (const std::bad_alloc &e) {
-        m_privilegeDb.RollbackTransaction();
-        LogError("Memory allocation while setting Cynara rules for application: " << e.what());
-        goto error_label;
+    if (m_isSlave) {
+        ret = MasterReq::PolicyGetDesc(descriptions);
+    } else {
+        ret = ServiceImpl::policyGetDesc(descriptions);
     }
+    Serialization::Serialize(send, ret);
+    if (ret == SECURITY_MANAGER_API_SUCCESS) {
+        Serialization::Serialize(send, static_cast<int>(descriptions.size()));
 
-    if (appExists) {
-
-        if (removePkg) {
-            LogDebug("Removing Smack rules for deleted pkgId " << pkgId);
-            if (!SmackRules::uninstallPackageRules(pkgId)) {
-                LogError("Error on uninstallation of package-specific smack rules");
-                goto error_label;
-            }
+        for(std::vector<std::string>::size_type i = 0; i != descriptions.size(); i++) {
+            Serialization::Serialize(send, descriptions[i]);
         }
     }
-
-    // success
-    Serialization::Serialize(send, SECURITY_MANAGER_API_SUCCESS);
-    return true;
-
-error_label:
-    Serialization::Serialize(send, SECURITY_MANAGER_API_ERROR_SERVER_ERROR);
-    return false;
 }
 
-bool Service::processGetPkgId(MessageBuffer &buffer, MessageBuffer &send)
+void Service::processPrivilegesMappings(MessageBuffer &recv, MessageBuffer &send)
 {
-    // deserialize request data
-    std::string appId;
-    std::string pkgId;
-
-    Deserialization::Deserialize(buffer, appId);
-    LogDebug("appId: " << appId);
+    std::vector<std::string> privileges;
+    std::string version_from, version_to;
+    Deserialization::Deserialize(recv, version_from);
+    Deserialization::Deserialize(recv, version_to);
+    Deserialization::Deserialize(recv, privileges);
 
-    try {
-        if (!m_privilegeDb.GetAppPkgId(appId, pkgId)) {
-            LogWarning("Application " << appId << " not found in database");
-            Serialization::Serialize(send, SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT);
-            return false;
-        } else {
-            LogDebug("pkgId: " << pkgId);
-        }
-    } catch (const PrivilegeDb::Exception::InternalError &e) {
-        LogError("Error while getting pkgId from database: " << e.DumpToString());
-        Serialization::Serialize(send, SECURITY_MANAGER_API_ERROR_SERVER_ERROR);
-        return false;
-    }
+    std::vector<std::string> mappings;
+    int ret = ServiceImpl::getPrivilegesMappings(version_from, version_to, privileges, mappings);
 
-     // success
-    Serialization::Serialize(send, SECURITY_MANAGER_API_SUCCESS);
-    Serialization::Serialize(send, pkgId);
-    return true;
+    Serialization::Serialize(send, ret);
+    Serialization::Serialize(send, mappings);
 }
 
 } // namespace SecurityManager