Modify app launched in mount namespace 81/139781/30
authorDariusz Michaluk <d.michaluk@samsung.com>
Mon, 2 Oct 2017 13:14:48 +0000 (15:14 +0200)
committerDariusz Michaluk <d.michaluk@samsung.com>
Fri, 13 Oct 2017 07:01:46 +0000 (09:01 +0200)
This commit adds worker that will be able to manage with mount namespace.
If mount namespace is not supported, security-manager will run without worker,
otherwise worker will be communicated with security-manager through IPC channel.

If app privilege status changes, worker will allow/deny access to filesystem directory
associated with this privilege.

Change-Id: I056cd752c228335c7b67a607bddc0934c7a79ddd

src/common/CMakeLists.txt
src/common/include/filesystem-exception.h
src/common/include/nsmount-logic.h [new file with mode: 0644]
src/common/include/service_impl.h
src/common/include/worker.h [new file with mode: 0644]
src/common/nsmount-logic.cpp [new file with mode: 0644]
src/common/service_impl.cpp
src/common/worker.cpp [new file with mode: 0644]
src/server/main/server-main.cpp
src/server/main/socket-manager.cpp
src/server/service/include/service.h

index 089d53e..1821c5a 100644 (file)
@@ -57,12 +57,14 @@ SET(COMMON_SOURCES
     ${COMMON_PATH}/permissible-set.cpp
     ${COMMON_PATH}/protocols.cpp
     ${COMMON_PATH}/message-buffer.cpp
+    ${COMMON_PATH}/nsmount-logic.cpp
     ${COMMON_PATH}/privilege_db.cpp
     ${COMMON_PATH}/smack-labels.cpp
     ${COMMON_PATH}/smack-rules.cpp
     ${COMMON_PATH}/smack-check.cpp
     ${COMMON_PATH}/service_impl.cpp
     ${COMMON_PATH}/tzplatform-config.cpp
+    ${COMMON_PATH}/worker.cpp
     ${COMMON_PATH}/privilege-info.cpp
     ${COMMON_PATH}/privilege-gids.cpp
     ${COMMON_PATH}/group2gid.cpp
index fd4bd73..292d76b 100644 (file)
@@ -33,6 +33,7 @@ class Exception {
 public:
     DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base)
     DECLARE_EXCEPTION_TYPE(Base, FileError)
+    DECLARE_EXCEPTION_TYPE(Base, InvalidArgument)
 };
 
 } // namespace FS
diff --git a/src/common/include/nsmount-logic.h b/src/common/include/nsmount-logic.h
new file mode 100644 (file)
index 0000000..5f25c3b
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ *  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
+ *
+ *  Security Manager NSS library
+ */
+/*
+ * @file        nsmount-logic.h
+ * @author      Bartlomiej Grzelewski <b.grzelewski@samsung.com>
+ * @version     1.0
+ * @brief
+ */
+
+
+#pragma once
+
+#include <cynara.h>
+#include <channel.h>
+
+namespace SecurityManager {
+
+class NSMountLogic {
+public:
+    NSMountLogic(Cynara &cynara, Channel &channel)
+      : m_cynara(cynara)
+      , m_channel(channel)
+    {}
+
+    struct Entry : public ISerializable {
+        typedef std::string Privilege;
+        typedef std::pair<Privilege,bool> PrivilegeStatus;
+        typedef std::vector<std::pair<Privilege, bool>> PrivilegeStatusVector;
+
+        Entry() {}
+
+        Entry(uid_t uid, std::string appName)
+          : uid(uid)
+          , appName(std::move(appName))
+        {}
+
+        explicit Entry(IStream &stream) {
+            Deserialization::Deserialize(stream, uid, appName, smackLabel, privilegeStatusVector);
+        }
+
+        virtual void Serialize(IStream &stream) const {
+            Serialization::Serialize(stream, uid, appName, smackLabel, privilegeStatusVector);
+        }
+
+        uid_t uid = 0;
+        std::string appName;
+        std::string smackLabel;
+        PrivilegeStatusVector privilegeStatusVector;
+    };
+
+    typedef std::vector<Entry> EntryVector;
+
+    bool check(std::function<void(Entry&)> cynaraFilter);
+
+    virtual ~NSMountLogic();
+protected:
+    void readFiles(void);
+    void cynaraCheck(void);
+    bool sendJobs();
+
+    EntryVector m_entryVector;
+    Cynara &m_cynara;
+    Channel &m_channel;
+};
+
+} // namespace SecurityManager
index 2424a3d..22a65e7 100644 (file)
@@ -28,6 +28,7 @@
 
 #include <vector>
 
+#include "channel.h"
 #include "credentials.h"
 #include "cynara.h"
 #include "security-manager.h"
@@ -300,6 +301,14 @@ public:
      */
     int appBindNamespace(const Credentials &creds, const std::string &appName);
 
+    /**
+     * Register channel for communication with worker process.
+     *
+     * @param[in] channel
+     *
+     */
+    void RegisterChannel(Channel channel) { m_channel = std::move(channel); }
+
 private:
     bool authenticate(const Credentials &creds, const std::string &privilege);
 
@@ -364,6 +373,7 @@ private:
     PrivilegeDb m_privilegeDb;
     CynaraAdmin m_cynaraAdmin;
     PrivilegeGids m_privilegeGids;
+    Channel m_channel;
 };
 
 } /* namespace SecurityManager */
diff --git a/src/common/include/worker.h b/src/common/include/worker.h
new file mode 100644 (file)
index 0000000..79abdb0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ *  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        worker.h
+ * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version     1.0
+ * @brief
+ */
+#pragma once
+
+#include <string>
+
+#include <channel.h>
+#include <nsmount-logic.h>
+
+namespace SecurityManager {
+
+class Worker {
+public:
+    typedef NSMountLogic::Entry Entry;
+
+    Worker(Channel channel);
+    int doWork(const NSMountLogic::EntryVector &entryVector);
+
+    void mainLoop();
+
+protected:
+    Channel m_channel;
+};
+
+} // namespace SecurityManager
diff --git a/src/common/nsmount-logic.cpp b/src/common/nsmount-logic.cpp
new file mode 100644 (file)
index 0000000..aef596b
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ *  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        nsmount-logic.cpp
+ * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version     1.0
+ * @brief       Logic for modifying up existing mount namespace.
+ */
+#include <string>
+
+#include <filesystem.h>
+#include <protocols.h>
+#include <message-buffer.h>
+#include <mount-namespace.h>
+#include <dpl/serialization.h>
+
+#include <nsmount-logic.h>
+
+namespace {
+
+auto notNumber = [](const std::string &input)
+{
+    try {
+        stoi(input);
+    } catch (const std::invalid_argument&) {
+        return true;
+    }
+    return false;
+};
+
+auto toNumber = [](const std::string &input)
+{
+    uid_t uid;
+    try {
+        uid = static_cast<uid_t>(stoi(input));
+    } catch (const std::invalid_argument &) {
+        ThrowMsg(FS::Exception::InvalidArgument, "Cound not translate " << input << " to uid_t");
+    } catch (const std::out_of_range &e) {
+        ThrowMsg(FS::Exception::InvalidArgument, "Cound not translate " << input << " to uid_t");
+    }
+    return uid;
+};
+
+} // namespace anonymous
+
+namespace SecurityManager {
+
+void NSMountLogic::readFiles(void)
+{
+    auto users = FS::getSubDirectoriesFromDirectory(MountNS::getUsersAppsMountPointsPath(), true);
+    auto last = std::remove_if(users.begin(), users.end(), notNumber);
+    users.erase(last, users.end());
+
+    for (auto &user : users) {
+        auto apps = FS::getFilesFromDirectory(MountNS::getUserAppsMountPointsPath(toNumber(user)));
+        for (auto &app : apps) {
+            m_entryVector.emplace_back(toNumber(user), app);
+        }
+    }
+}
+
+void NSMountLogic::cynaraCheck(void)
+{
+    for (auto &entry : m_entryVector) {
+        if (entry.smackLabel.empty())
+            continue;
+
+        auto storagePrivilegePathMap = MountNS::getPrivilegePathMap(entry.uid);
+
+        for (auto &p : storagePrivilegePathMap) {
+            entry.privilegeStatusVector.emplace_back(
+                std::make_pair(p.first, m_cynara.check(entry.smackLabel, p.first, std::to_string(entry.uid), std::string())));
+        }
+    }
+}
+
+bool NSMountLogic::sendJobs(void)
+{
+    int status;
+    MessageBuffer send, recv;
+    Serialization::Serialize(send, m_entryVector);
+    if (!m_channel.write(send)) {
+        LogError("Could not send data to worker!");
+        return false;
+    }
+    if (!m_channel.read(recv)) {
+        LogError("Could not recv data from worker!");
+        return false;
+    }
+    Deserialization::Deserialize(recv, status);
+    return status == 0;
+}
+
+bool NSMountLogic::check(std::function<void(Entry&)> cynaraFilter)
+{
+    try {
+        readFiles();
+
+        for (auto &entry : m_entryVector)
+            cynaraFilter(entry);
+
+        cynaraCheck();
+        return sendJobs();
+    } catch (const MessageBuffer::Exception::Base &e) {
+        LogError("Worker connection failed: " << e.DumpToString());
+    } catch (const FS::Exception::Base &e) {
+        LogError("Filesystem exception: " << e.DumpToString());
+    }
+    return false;
+}
+
+NSMountLogic::~NSMountLogic()
+{}
+
+} // namespace SecurityManager
index 2c33722..1fb8267 100644 (file)
@@ -43,6 +43,7 @@
 #include <sys/smack.h>
 
 #include <config.h>
+#include <nsmount-logic.h>
 #include "protocols.h"
 #include "privilege_db.h"
 #include "cynara.h"
@@ -1069,6 +1070,12 @@ int ServiceImpl::policyUpdate(const Credentials &creds, const std::vector<policy
         // Apply updates
         m_cynaraAdmin.setPolicies(validatedPolicies);
 
+        if (MountNS::isMountNamespaceEnabled()) {
+            NSMountLogic mntLogic(m_cynara, m_channel);
+            mntLogic.check(
+                [&](NSMountLogic::Entry &e){ e.smackLabel = getAppProcessLabel(e.appName); });
+        }
+
     } catch (const CynaraException::Base &e) {
         LogError("Error while updating Cynara rules: " << e.DumpToString());
         return SECURITY_MANAGER_ERROR_SERVER_ERROR;
diff --git a/src/common/worker.cpp b/src/common/worker.cpp
new file mode 100644 (file)
index 0000000..414947a
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ *  Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ *  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        worker.cpp
+ * @author      Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version     1.0
+ * @brief
+ */
+
+#include <string>
+
+#include <dpl/serialization.h>
+#include <channel.h>
+#include <filesystem.h>
+#include <mount-namespace.h>
+#include <protocols.h>
+#include <worker.h>
+
+namespace SecurityManager {
+
+Worker::Worker(Channel channel)
+  : m_channel(std::move(channel))
+{}
+
+int Worker::doWork(const NSMountLogic::EntryVector &entries)
+{
+    int status = 0;
+    bool inGlobalNamespace = false;
+
+    for (auto &entry : entries) {
+        try {
+            // in most cases entry.uid will not change between iterations
+            auto storagePrivilegePathMap = MountNS::getPrivilegePathMap(entry.uid);
+
+            if (!inGlobalNamespace && MountNS::enterMountNamespace(MountNS::MAIN_MOUNT_NAMESPACE)) {
+                inGlobalNamespace = true;
+            }
+
+            if (!inGlobalNamespace) {
+                LogError("Error entering global mount namespace. Environment of application: "
+                         << entry.appName << " will not be setup correctly.");
+                continue;
+            }
+
+            if (MountNS::enterMountNamespace(MountNS::getUserAppMountPointPath(entry.uid, entry.appName))) {
+                inGlobalNamespace = false;
+            } else {
+                continue;
+            }
+
+            for (auto &privStatus : entry.privilegeStatusVector) {
+                auto &privName = privStatus.first;
+                auto &result   = privStatus.second;
+                auto mapIter   = storagePrivilegePathMap.find(privName);
+
+                if (mapIter == storagePrivilegePathMap.end())
+                    continue;
+
+                MountNS::PathVector pathVector = mapIter->second;
+                for (auto &path : pathVector) {
+                    bool allowed = !MountNS::isPathBound(MountNS::ACCESS_DENIED_DIR_PATH, path);
+
+                    if (allowed == result)
+                        continue;
+
+                    if (result)
+                        MountNS::uMount(path); // allow access to path
+                    else
+                        MountNS::bindMount(MountNS::ACCESS_DENIED_DIR_PATH, path); // forbid access
+                }
+            }
+        } catch (...) {
+            status = -1;
+            LogError("Could not set up access to path for application: " << entry.appName);
+        }
+    }
+    return status;
+}
+
+void Worker::mainLoop()
+{
+    NSMountLogic::EntryVector entryVector;
+
+    do {
+        int status;
+        NSMountLogic::EntryVector entryVector;
+        MessageBuffer recv;
+        MessageBuffer send;
+
+        if (!m_channel.read(recv)) {
+            LogError("Error reading command socket. The Security-manager worker will exit");
+            break;
+        }
+
+        Deserialization::Deserialize(recv, entryVector);
+        status = doWork(entryVector);
+        Serialization::Serialize(send, status);
+        m_channel.write(send);
+    } while (true);
+}
+
+} // namespace SecurityManager
index 2a9a00f..7b71f7d 100644 (file)
 
 #include <iostream>
 
+#include <channel.h>
+#include <mount-namespace.h>
 #include <socket-manager.h>
 #include <file-lock.h>
 
 #include <service.h>
+#include <worker.h>
 
-#define REGISTER_SOCKET_SERVICE(manager, service) \
-    registerSocketService<service>(manager, #service)
+#define REGISTER_SOCKET_SERVICE(manager, service, channel) \
+    registerSocketService<service>(manager, #service, channel)
 
 template<typename T>
-bool registerSocketService(SecurityManager::SocketManager &manager,
-                           const std::string& serviceName)
+T* registerSocketService(SecurityManager::SocketManager &manager,
+                           const std::string& serviceName,
+                           Channel channel)
 {
     T *service = NULL;
     try {
         service = new T();
+        service->RegisterChannel(std::move(channel));
         service->Start();
         manager.RegisterSocketService(service);
-        return true;
+        return service;
     } catch (const SecurityManager::Exception &exception) {
         LogError("Error in creating service " << serviceName <<
                  ", details:\n" << exception.DumpToString());
@@ -61,37 +66,83 @@ bool registerSocketService(SecurityManager::SocketManager &manager,
         service->Stop();
         delete service;
     }
-    return false;
+    return nullptr;
+}
+
+int worker(Channel channel)
+{
+    SecurityManager::Singleton<SecurityManager::Log::LogSystem>::Instance().SetTag("SECURITY_MANAGER_WORKER");
+    SecurityManager::Worker worker(std::move(channel));
+    worker.mainLoop();
+    return 0;
+}
+
+int security_manager(Channel channel)
+{
+    SecurityManager::FileLocker serviceLock(SecurityManager::SERVICE_LOCK_FILE, true);
+    LogInfo("Start!");
+    SecurityManager::SocketManager manager;
+
+    if (!REGISTER_SOCKET_SERVICE(manager, SecurityManager::Service, std::move(channel))) {
+        LogError("Unable to create socket service. Exiting.");
+        return EXIT_FAILURE;
+    }
+
+    manager.MainLoop();
+    return 0;
 }
 
 int main()
 {
     UNHANDLED_EXCEPTION_HANDLER_BEGIN
     {
-        // initialize logging
         SecurityManager::Singleton<SecurityManager::Log::LogSystem>::Instance().SetTag("SECURITY_MANAGER");
 
-        SecurityManager::FileLocker serviceLock(SecurityManager::SERVICE_LOCK_FILE, true);
-
         sigset_t mask;
         sigemptyset(&mask);
         sigaddset(&mask, SIGTERM);
         sigaddset(&mask, SIGPIPE);
+        sigaddset(&mask, SIGCHLD);
+
         if (-1 == pthread_sigmask(SIG_BLOCK, &mask, NULL)) {
             LogError("Error in pthread_sigmask");
             return EXIT_FAILURE;
         }
 
-        LogInfo("Start!");
-        SecurityManager::SocketManager manager;
+        if (!MountNS::isMountNamespaceEnabled()) {
+            // Lagacy systems without mount namespace.
+            // In this case security-manager will run without WORKER
+            return security_manager(Channel());
+        }
 
-        if (!REGISTER_SOCKET_SERVICE(manager, SecurityManager::Service))
-        {
-            LogError("Unable to create socket service. Exiting.");
+        // New systems with mount namespace.
+        // In this case security-manager requires WORKER that will
+        // be able to manage with mount namespaces.
+        ChannelCreator creator;
+
+        if (!creator.init()) {
+            LogError("Error creating channel");
             return EXIT_FAILURE;
         }
+        Channel child(creator.child());
+        Channel parent(creator.parent());
+
+        int ret = fork();
 
-        manager.MainLoop();
+        if (-1 == ret) {
+            LogError("Error in fork");
+            return EXIT_FAILURE;
+        }
+
+        if (ret == 0) {
+            // child
+            parent.closeAll();
+            return worker(std::move(child));
+        } else {
+            // parent
+            child.closeAll();
+            return security_manager(std::move(parent));
+        }
     } catch (const SecurityManager::FileLocker::Exception::LockFailed &e) {
         LogError("Unable to get a file lock. Exiting.");
         return EXIT_FAILURE;
index ef69a15..885b7e3 100644 (file)
@@ -30,6 +30,7 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/smack.h>
+#include <sys/wait.h>
 #include <linux/xattr.h>
 #include <sys/un.h>
 #include <sys/stat.h>
@@ -73,6 +74,7 @@ struct SignalService : public GenericSocketService {
         sigset_t mask;
         sigemptyset(&mask);
         sigaddset(&mask, SIGTERM);
+        sigaddset(&mask, SIGCHLD);
         if (-1 == pthread_sigmask(SIG_BLOCK, &mask, NULL))
             return -1;
         return signalfd(-1, &mask, 0);
@@ -104,6 +106,10 @@ struct SignalService : public GenericSocketService {
             if (manager)
                 manager->MainLoopStop();
             return;
+        } else if (siginfo->ssi_signo == SIGCHLD) {
+            int status;
+            waitpid(-1, &status, WNOHANG);
+            return;
         }
 
         LogInfo("This should not happend. Got signal: " << siginfo->ssi_signo);
index 8c9e682..5181ff3 100644 (file)
@@ -27,6 +27,7 @@
 #include "base-service.h"
 #include "credentials.h"
 #include "service_impl.h"
+#include <channel.h>
 
 namespace SecurityManager {
 
@@ -43,7 +44,9 @@ class Service :
 public:
     Service();
     ServiceDescriptionVector GetServiceDescription();
-
+    void RegisterChannel(Channel channel) {
+        serviceImpl.RegisterChannel(std::move(channel));
+    }
 private:
 
     /**