Dbus proxy call support 36/24636/3
authorPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Tue, 15 Jul 2014 13:24:26 +0000 (15:24 +0200)
committerPiotr Bartosiewicz <p.bartosiewi@partner.samsung.com>
Fri, 18 Jul 2014 10:59:52 +0000 (12:59 +0200)
[Bug/Feature]   Introduce the API for communication between services
                inside container and services in host. SCS works as a
                router/proxy enabling host <-> container and container
                <-> container dbus calls.
[Cause]         N/A
[Solution]      N/A
[Verification]  Build, install, run tests

Change-Id: Ia85a7c0234880069653d1a8596dbc240fa7b3f76

27 files changed:
packaging/security-containers.spec
server/configs/daemon.conf
server/container-connection.cpp
server/container-connection.hpp
server/container-dbus-definitions.hpp
server/container.cpp
server/container.hpp
server/containers-manager-config.hpp
server/containers-manager.cpp
server/containers-manager.hpp
server/host-connection.cpp
server/host-connection.hpp
server/host-dbus-definitions.hpp
server/proxy-call-config.hpp [new file with mode: 0644]
server/proxy-call-policy.cpp [new file with mode: 0644]
server/proxy-call-policy.hpp [new file with mode: 0644]
tests/unit_tests/server/configs/CMakeLists.txt
tests/unit_tests/server/configs/dbus-1/system.d/org.tizen.containers.tests.conf [new file with mode: 0644]
tests/unit_tests/server/configs/ut-containers-manager/buggy-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/buggy-default-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/buggy-foreground-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/test-daemon.conf
tests/unit_tests/server/configs/ut-containers-manager/test-dbus-daemon.conf
tests/unit_tests/server/configs/ut-server/buggy-daemon.conf
tests/unit_tests/server/configs/ut-server/test-daemon.conf
tests/unit_tests/server/test-dbus-definitions.hpp [new file with mode: 0644]
tests/unit_tests/server/ut-containers-manager.cpp

index 1258c1a..7d7b0a1 100644 (file)
@@ -188,3 +188,4 @@ Unit tests for both: server and client and integration tests.
 %{script_dir}/sc_test_parser.py
 %{_datadir}/security-containers
 %{python_sitelib}/sc_integration_tests
+/etc/dbus-1/system.d/org.tizen.containers.tests.conf
index 9c0eba6..67ef356 100644 (file)
@@ -8,5 +8,6 @@
                      "device" : "gpio-keys",
                      "code" : 139,
                      "numberOfEvents" : 3,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : []
 }
index adabace..f71a5a6 100644 (file)
@@ -146,6 +146,11 @@ void ContainerConnection::setFileMoveRequestCallback(
     mFileMoveRequestCallback = callback;
 }
 
+void ContainerConnection::setProxyCallCallback(const ProxyCallCallback& callback)
+{
+    mProxyCallCallback = callback;
+}
+
 void ContainerConnection::onMessageCall(const std::string& objectPath,
                                         const std::string& interface,
                                         const std::string& methodName,
@@ -174,6 +179,34 @@ void ContainerConnection::onMessageCall(const std::string& objectPath,
             mFileMoveRequestCallback(destination, path, result);
         }
     }
+
+    if (methodName == api::METHOD_PROXY_CALL) {
+        const gchar* target = NULL;
+        const gchar* targetBusName = NULL;
+        const gchar* targetObjectPath = NULL;
+        const gchar* targetInterface = NULL;
+        const gchar* targetMethod = NULL;
+        GVariant* rawArgs = NULL;
+        g_variant_get(parameters,
+                      "(&s&s&s&s&sv)",
+                      &target,
+                      &targetBusName,
+                      &targetObjectPath,
+                      &targetInterface,
+                      &targetMethod,
+                      &rawArgs);
+        dbus::GVariantPtr args(rawArgs, g_variant_unref);
+
+        if (mProxyCallCallback) {
+            mProxyCallCallback(target,
+                               targetBusName,
+                               targetObjectPath,
+                               targetInterface,
+                               targetMethod,
+                               args.get(),
+                               result);
+        }
+    }
 }
 
 void ContainerConnection::onSignalReceived(const std::string& senderBusName,
@@ -207,5 +240,21 @@ void ContainerConnection::sendNotification(const std::string& container,
                                 parameters);
 }
 
+void ContainerConnection::proxyCallAsync(const std::string& busName,
+                                         const std::string& objectPath,
+                                         const std::string& interface,
+                                         const std::string& method,
+                                         GVariant* parameters,
+                                         const dbus::DbusConnection::AsyncMethodCallCallback& callback)
+{
+    mDbusConnection->callMethodAsync(busName,
+                                     objectPath,
+                                     interface,
+                                     method,
+                                     parameters,
+                                     std::string(),
+                                     callback);
+}
+
 
 } // namespace security_containers
index 7759a09..a635bc8 100644 (file)
@@ -55,6 +55,15 @@ public:
                                dbus::MethodResultBuilder::Pointer result
                               )> FileMoveRequestCallback;
 
+    typedef std::function<void(const std::string& target,
+                               const std::string& targetBusName,
+                               const std::string& targetObjectPath,
+                               const std::string& targetInterface,
+                               const std::string& targetMethod,
+                               GVariant* parameters,
+                               dbus::MethodResultBuilder::Pointer result
+                               )> ProxyCallCallback;
+
     /**
      * Register notification request callback
      */
@@ -71,12 +80,27 @@ public:
     void setFileMoveRequestCallback(const FileMoveRequestCallback& callback);
 
     /**
+     * Register proxy call callback
+     */
+    void setProxyCallCallback(const ProxyCallCallback& callback);
+
+    /**
      * Send notification signal to this container
      */
     void sendNotification(const std::string& container,
                           const std::string& application,
                           const std::string& message);
 
+    /**
+     * Make a proxy call
+     */
+    void proxyCallAsync(const std::string& busName,
+                        const std::string& objectPath,
+                        const std::string& interface,
+                        const std::string& method,
+                        GVariant* parameters,
+                        const dbus::DbusConnection::AsyncMethodCallCallback& callback);
+
 private:
     dbus::DbusConnection::Pointer mDbusConnection;
     std::mutex mNameMutex;
@@ -87,6 +111,7 @@ private:
     NotifyActiveContainerCallback mNotifyActiveContainerCallback;
     DisplayOffCallback mDisplayOffCallback;
     FileMoveRequestCallback mFileMoveRequestCallback;
+    ProxyCallCallback mProxyCallCallback;
 
     void onNameAcquired();
     void onNameLost();
index febcbbc..fe72e4d 100644 (file)
@@ -38,6 +38,7 @@ const std::string INTERFACE                         = "org.tizen.containers.doma
 
 const std::string METHOD_NOTIFY_ACTIVE_CONTAINER    = "NotifyActiveContainer";
 const std::string METHOD_FILE_MOVE_REQUEST          = "FileMoveRequest";
+const std::string METHOD_PROXY_CALL                 = "ProxyCall";
 const std::string SIGNAL_NOTIFICATION               = "Notification";
 
 const std::string FILE_MOVE_DESTINATION_NOT_FOUND   = "FILE_MOVE_DESTINATION_NOT_FOUND";
@@ -60,6 +61,15 @@ const std::string DEFINITION =
     "      <arg type='s' name='path' direction='in'/>"
     "      <arg type='s' name='result' direction='out'/>"
     "    </method>"
+    "    <method name='" + METHOD_PROXY_CALL + "'>"
+    "      <arg type='s' name='target' direction='in'/>"
+    "      <arg type='s' name='busName' direction='in'/>"
+    "      <arg type='s' name='objectPath' direction='in'/>"
+    "      <arg type='s' name='interface' direction='in'/>"
+    "      <arg type='s' name='method' direction='in'/>"
+    "      <arg type='v' name='parameters' direction='in'/>"
+    "      <arg type='v' name='result' direction='out'/>"
+    "    </method>"
     "    <signal name='" + SIGNAL_NOTIFICATION + "'>"
     "      <arg type='s' name='container'/>"
     "      <arg type='s' name='application'/>"
index 96ca936..0b61d65 100644 (file)
@@ -128,6 +128,9 @@ void Container::start()
     if (mFileMoveCallback) {
         mConnection->setFileMoveRequestCallback(mFileMoveCallback);
     }
+    if (mProxyCallCallback) {
+        mConnection->setProxyCallCallback(mProxyCallCallback);
+    }
 
     // Send to the background only after we're connected,
     // otherwise it'd take ages.
@@ -270,5 +273,35 @@ void Container::setFileMoveRequestCallback(const FileMoveRequestCallback& callba
     }
 }
 
+void Container::setProxyCallCallback(const ProxyCallCallback& callback)
+{
+    Lock lock(mReconnectMutex);
+
+    mProxyCallCallback = callback;
+    if (mConnection) {
+        mConnection->setProxyCallCallback(callback);
+    }
+}
+
+void Container::proxyCallAsync(const std::string& busName,
+                               const std::string& objectPath,
+                               const std::string& interface,
+                               const std::string& method,
+                               GVariant* parameters,
+                               const dbus::DbusConnection::AsyncMethodCallCallback& callback)
+{
+    Lock lock(mReconnectMutex);
+    if (mConnection) {
+        mConnection->proxyCallAsync(busName,
+                                    objectPath,
+                                    interface,
+                                    method,
+                                    parameters,
+                                    callback);
+    } else {
+        LOGE(getId() << ": Can't do a proxy call, no connection to DBUS");
+    }
+}
+
 
 } // namespace security_containers
index 4c1c879..dff5d23 100644 (file)
@@ -52,6 +52,7 @@ public:
     typedef ContainerConnection::NotifyActiveContainerCallback NotifyActiveContainerCallback;
     typedef ContainerConnection::DisplayOffCallback DisplayOffCallback;
     typedef ContainerConnection::FileMoveRequestCallback FileMoveRequestCallback;
+    typedef ContainerConnection::ProxyCallCallback ProxyCallCallback;
 
     /**
      * Returns a vector of regexps defining files permitted to be
@@ -144,6 +145,11 @@ public:
     void setDisplayOffCallback(const DisplayOffCallback& callback);
 
     /**
+     * Register proxy call callback
+     */
+    void setProxyCallCallback(const ProxyCallCallback& callback);
+
+    /**
      * Send notification signal to this container
      *
      * @param container   name of container in which the notification occurred
@@ -159,6 +165,16 @@ public:
      */
     void setFileMoveRequestCallback(const FileMoveRequestCallback& callback);
 
+    /**
+     * Make a proxy call
+     */
+    void proxyCallAsync(const std::string& busName,
+                        const std::string& objectPath,
+                        const std::string& interface,
+                        const std::string& method,
+                        GVariant* parameters,
+                        const dbus::DbusConnection::AsyncMethodCallCallback& callback);
+
 private:
     ContainerConfig mConfig;
     std::vector<boost::regex> mPermittedToSend;
@@ -172,6 +188,7 @@ private:
     NotifyActiveContainerCallback mNotifyCallback;
     DisplayOffCallback mDisplayOffCallback;
     FileMoveRequestCallback mFileMoveCallback;
+    ProxyCallCallback mProxyCallCallback;
     std::string mRunMountPoint;
 
     void onNameLostCallback();
index 7f5cb9d..310dca3 100644 (file)
@@ -28,6 +28,7 @@
 
 #include "config/fields.hpp"
 #include "input-monitor-config.hpp"
+#include "proxy-call-config.hpp"
 
 #include <string>
 #include <vector>
@@ -71,6 +72,11 @@ struct ContainersManagerConfig {
      */
     std::string runMountPointPrefix;
 
+    /**
+     * Proxy call rules.
+     */
+    std::vector<ProxyCallRule> proxyCallRules;
+
     CONFIG_REGISTER
     (
         containerConfigs,
@@ -78,7 +84,8 @@ struct ContainersManagerConfig {
         defaultId,
         containersPath,
         inputConfig,
-        runMountPointPrefix
+        runMountPointPrefix,
+        proxyCallRules
     )
 };
 
index 0150e4a..7bd22b3 100644 (file)
@@ -32,6 +32,7 @@
 #include "utils/paths.hpp"
 #include "log/logger.hpp"
 #include "config/manager.hpp"
+#include "dbus/exception.hpp"
 
 #include <boost/filesystem.hpp>
 #include <boost/regex.hpp>
@@ -56,6 +57,11 @@ bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v
     return false;
 }
 
+const std::string HOST_ID = "host";
+const std::string DBUS_ERROR_NAME_FORBIDDEN = "org.tizen.containers.Error.Forbidden";
+const std::string DBUS_ERROR_NAME_FORWARDED = "org.tizen.containers.Error.Forwarded";
+const std::string DBUS_ERROR_NAME_UNKNOWN_TARGET = "org.tizen.containers.Error.UnknownTarget";
+
 } // namespace
 
 ContainersManager::ContainersManager(const std::string& managerConfigPath): mDetachOnExit(false)
@@ -63,6 +69,12 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
     LOGD("Instantiating ContainersManager object...");
     config::loadFromFile(managerConfigPath, mConfig);
 
+    mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
+
+    using namespace std::placeholders;
+    mHostConnection.setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
+                                              this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
+
     for (auto& containerConfig : mConfig.containerConfigs) {
         std::string containerConfigPath;
 
@@ -76,8 +88,11 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
         LOGD("Creating Container " << containerConfigPath);
         std::unique_ptr<Container> c(new Container(containerConfigPath,
                                                    mConfig.runMountPointPrefix));
-        std::string id = c->getId();
-        using namespace std::placeholders;
+        const std::string id = c->getId();
+        if (id == HOST_ID) {
+            throw ContainerOperationException("Cannot use reserved container ID");
+        }
+
         c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
                                                  this, id, _1, _2));
 
@@ -87,6 +102,9 @@ ContainersManager::ContainersManager(const std::string& managerConfigPath): mDet
         c->setFileMoveRequestCallback(std::bind(&ContainersManager::handleContainerMoveFileRequest,
                                                 this, id, _1, _2, _3));
 
+        c->setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
+                                     this, id, _1, _2, _3, _4, _5, _6, _7));
+
         mContainers.insert(ContainerMap::value_type(id, std::move(c)));
     }
 
@@ -314,5 +332,64 @@ void ContainersManager::handleContainerMoveFileRequest(const std::string& srcCon
     }
 }
 
+void ContainersManager::handleProxyCall(const std::string& caller,
+                                        const std::string& target,
+                                        const std::string& targetBusName,
+                                        const std::string& targetObjectPath,
+                                        const std::string& targetInterface,
+                                        const std::string& targetMethod,
+                                        GVariant* parameters,
+                                        dbus::MethodResultBuilder::Pointer result)
+{
+    if (!mProxyCallPolicy->isProxyCallAllowed(caller,
+                                              target,
+                                              targetBusName,
+                                              targetObjectPath,
+                                              targetInterface,
+                                              targetMethod)) {
+        LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
+                << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
+        result->setError(DBUS_ERROR_NAME_FORBIDDEN, "Proxy call forbidden");
+        return;
+    }
+
+    LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
+            << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
+
+    auto asyncResultCallback = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) {
+        try {
+            GVariant* targetResult = asyncMethodCallResult.get();
+            result->set(g_variant_new("(v)", targetResult));
+        } catch (dbus::DbusException& e) {
+            result->setError(DBUS_ERROR_NAME_FORWARDED, e.what());
+        }
+    };
+
+    if (target == HOST_ID) {
+        mHostConnection.proxyCallAsync(targetBusName,
+                                       targetObjectPath,
+                                       targetInterface,
+                                       targetMethod,
+                                       parameters,
+                                       asyncResultCallback);
+        return;
+    }
+
+    ContainerMap::const_iterator targetIter = mContainers.find(target);
+    if (targetIter == mContainers.end()) {
+        LOGE("Target container '" << target << "' not found");
+        result->setError(DBUS_ERROR_NAME_UNKNOWN_TARGET, "Unknown proxy call target");
+        return;
+    }
+
+    Container& targetContainer = *targetIter->second;
+    targetContainer.proxyCallAsync(targetBusName,
+                                   targetObjectPath,
+                                   targetInterface,
+                                   targetMethod,
+                                   parameters,
+                                   asyncResultCallback);
+}
+
 
 } // namespace security_containers
index 1e29ed7..4354fdd 100644 (file)
@@ -30,6 +30,7 @@
 #include "containers-manager-config.hpp"
 #include "host-connection.hpp"
 #include "input-monitor.hpp"
+#include "proxy-call-policy.hpp"
 
 #include <string>
 #include <unordered_map>
@@ -79,6 +80,7 @@ private:
     HostConnection mHostConnection;
     // to hold InputMonitor pointer to monitor if container switching sequence is recognized
     std::unique_ptr<InputMonitor> mSwitchingSequenceMonitor;
+    std::unique_ptr<ProxyCallPolicy> mProxyCallPolicy;
     typedef std::unordered_map<std::string, std::unique_ptr<Container>> ContainerMap;
     ContainerMap mContainers; // map of containers, id is the key
     bool mDetachOnExit;
@@ -92,10 +94,19 @@ private:
                                         const std::string& dstContainerId,
                                         const std::string& path,
                                         dbus::MethodResultBuilder::Pointer result);
+    void handleProxyCall(const std::string& caller,
+                         const std::string& target,
+                         const std::string& targetBusName,
+                         const std::string& targetObjectPath,
+                         const std::string& targetInterface,
+                         const std::string& targetMethod,
+                         GVariant* parameters,
+                         dbus::MethodResultBuilder::Pointer result);
+
 };
 
 
-}
+} // namespace security_containers
 
 
 #endif // SERVER_CONTAINERS_MANAGER_HPP
index 4f9a2f2..2821202 100644 (file)
@@ -110,28 +110,65 @@ void HostConnection::onNameLost()
     }
 }
 
-void HostConnection::setTestCallback(const TestCallback& callback)
+void HostConnection::setProxyCallCallback(const ProxyCallCallback& callback)
 {
-    mTestCallback = callback;
+    mProxyCallCallback = callback;
 }
 
 void HostConnection::onMessageCall(const std::string& objectPath,
                                         const std::string& interface,
                                         const std::string& methodName,
-                                        GVariant* /*parameters*/,
+                                        GVariant* parameters,
                                         dbus::MethodResultBuilder::Pointer result)
 {
     if (objectPath != hostapi::OBJECT_PATH || interface != hostapi::INTERFACE) {
         return;
     }
 
-    if (methodName == hostapi::METHOD_TEST) {
-        if (mTestCallback) {
-            mTestCallback();
-            result->setVoid();
+    if (methodName == hostapi::METHOD_PROXY_CALL) {
+        const gchar* target = NULL;
+        const gchar* targetBusName = NULL;
+        const gchar* targetObjectPath = NULL;
+        const gchar* targetInterface = NULL;
+        const gchar* targetMethod = NULL;
+        GVariant* rawArgs = NULL;
+        g_variant_get(parameters,
+                      "(&s&s&s&s&sv)",
+                      &target,
+                      &targetBusName,
+                      &targetObjectPath,
+                      &targetInterface,
+                      &targetMethod,
+                      &rawArgs);
+        dbus::GVariantPtr args(rawArgs, g_variant_unref);
+
+        if (mProxyCallCallback) {
+            mProxyCallCallback(target,
+                               targetBusName,
+                               targetObjectPath,
+                               targetInterface,
+                               targetMethod,
+                               args.get(),
+                               result);
         }
     }
 }
 
+void HostConnection::proxyCallAsync(const std::string& busName,
+                                    const std::string& objectPath,
+                                    const std::string& interface,
+                                    const std::string& method,
+                                    GVariant* parameters,
+                                    const dbus::DbusConnection::AsyncMethodCallCallback& callback)
+{
+    mDbusConnection->callMethodAsync(busName,
+                                     objectPath,
+                                     interface,
+                                     method,
+                                     parameters,
+                                     std::string(),
+                                     callback);
+}
+
 
 } // namespace security_containers
index d78949f..88cb480 100644 (file)
@@ -43,12 +43,28 @@ public:
 
     // ------------- API --------------
 
-    typedef std::function<void()> TestCallback;
+    typedef std::function<void(const std::string& target,
+                               const std::string& targetBusName,
+                               const std::string& targetObjectPath,
+                               const std::string& targetInterface,
+                               const std::string& targetMethod,
+                               GVariant* parameters,
+                               dbus::MethodResultBuilder::Pointer result)> ProxyCallCallback;
 
     /**
-     * Register test callback
+     * Register proxy call callback
      */
-    void setTestCallback(const TestCallback& callback);
+    void setProxyCallCallback(const ProxyCallCallback& callback);
+
+    /**
+     * Make a proxy call
+     */
+    void proxyCallAsync(const std::string& busName,
+                        const std::string& objectPath,
+                        const std::string& interface,
+                        const std::string& method,
+                        GVariant* parameters,
+                        const dbus::DbusConnection::AsyncMethodCallCallback& callback);
 
 private:
     dbus::DbusConnection::Pointer mDbusConnection;
@@ -56,7 +72,7 @@ private:
     std::condition_variable mNameCondition;
     bool mNameAcquired;
     bool mNameLost;
-    TestCallback mTestCallback;
+    ProxyCallCallback mProxyCallCallback;
 
     void onNameAcquired();
     void onNameLost();
index 2d82fd3..f41da38 100644 (file)
@@ -34,14 +34,21 @@ namespace hostapi {
 
 const std::string BUS_NAME                          = "org.tizen.containers.host";
 const std::string OBJECT_PATH                       = "/org/tizen/containers/host";
-const std::string INTERFACE                         = "org.tizen.containers.host.test";
+const std::string INTERFACE                         = "org.tizen.containers.host.manager";
 
-const std::string METHOD_TEST                       = "Test";
+const std::string METHOD_PROXY_CALL                 = "ProxyCall";
 
 const std::string DEFINITION =
     "<node>"
     "  <interface name='" + INTERFACE + "'>"
-    "    <method name='" + METHOD_TEST + "'>"
+    "    <method name='" + METHOD_PROXY_CALL + "'>"
+    "      <arg type='s' name='target' direction='in'/>"
+    "      <arg type='s' name='busName' direction='in'/>"
+    "      <arg type='s' name='objectPath' direction='in'/>"
+    "      <arg type='s' name='interface' direction='in'/>"
+    "      <arg type='s' name='method' direction='in'/>"
+    "      <arg type='v' name='parameters' direction='in'/>"
+    "      <arg type='v' name='result' direction='out'/>"
     "    </method>"
     "  </interface>"
     "</node>";
diff --git a/server/proxy-call-config.hpp b/server/proxy-call-config.hpp
new file mode 100644 (file)
index 0000000..f20d119
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.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
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Declaration of the struct for storing proxy call configuration
+ */
+
+
+#ifndef SERVER_PROXY_CALL_CONFIG_HPP
+#define SERVER_PROXY_CALL_CONFIG_HPP
+
+
+#include "config/fields.hpp"
+
+#include <string>
+
+
+namespace security_containers {
+
+/**
+ * A single allow rule for proxy call dispatching.
+ */
+struct ProxyCallRule {
+
+    std::string caller; ///< caller id (container id or host)
+    std::string target; ///< target id (container id or host)
+    std::string targetBusName; ///< target dbus bus name
+    std::string targetObjectPath; ///< target dbus object path
+    std::string targetInterface; ///< target dbus interface
+    std::string targetMethod; ///< target dbus method
+
+    CONFIG_REGISTER
+    (
+        caller,
+        target,
+        targetBusName,
+        targetObjectPath,
+        targetInterface,
+        targetMethod
+    )
+
+};
+
+} // namespace security_containers
+
+#endif /* SERVER_PROXY_CALL_CONFIG_HPP */
diff --git a/server/proxy-call-policy.cpp b/server/proxy-call-policy.cpp
new file mode 100644 (file)
index 0000000..c452d53
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.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
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Implementation of a class for checking permissions of proxy calls
+ */
+
+
+#include "config.hpp"
+
+#include "proxy-call-policy.hpp"
+
+
+namespace security_containers {
+
+namespace {
+const std::string ANY = "*";
+
+inline bool match(const std::string& rule, const std::string& value) {
+    // simple matching, change to regex if it turns out to be insufficient
+    return rule == ANY || rule == value;
+}
+
+} // namespace
+
+
+ProxyCallPolicy::ProxyCallPolicy(const std::vector<ProxyCallRule>& proxyCallRules)
+    : mProxyCallRules(proxyCallRules)
+{
+}
+
+bool ProxyCallPolicy::isProxyCallAllowed(const std::string& caller,
+                                         const std::string& target,
+                                         const std::string& targetBusName,
+                                         const std::string& targetObjectPath,
+                                         const std::string& targetInterface,
+                                         const std::string& targetMethod)
+{
+    for (const ProxyCallRule& rule : mProxyCallRules) {
+        if (match(rule.caller, caller)
+                && match(rule.target, target)
+                && match(rule.targetBusName, targetBusName)
+                && match(rule.targetObjectPath, targetObjectPath)
+                && match(rule.targetInterface, targetInterface)
+                && match(rule.targetMethod, targetMethod)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+
+} // namespace security_containers
diff --git a/server/proxy-call-policy.hpp b/server/proxy-call-policy.hpp
new file mode 100644 (file)
index 0000000..cbab0e0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.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
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Declaration of a class for checking permissions of proxy calls
+ */
+
+
+#ifndef SERVER_PROXY_CALL_POLICY_HPP
+#define SERVER_PROXY_CALL_POLICY_HPP
+
+#include "proxy-call-config.hpp"
+
+#include <vector>
+
+
+namespace security_containers {
+
+
+class ProxyCallPolicy {
+
+public:
+    ProxyCallPolicy(const std::vector<ProxyCallRule>& proxyCallRules);
+
+    bool isProxyCallAllowed(const std::string& caller,
+                            const std::string& target,
+                            const std::string& targetBusName,
+                            const std::string& targetObjectPath,
+                            const std::string& targetInterface,
+                            const std::string& targetMethod);
+
+private:
+    std::vector<ProxyCallRule> mProxyCallRules;
+};
+
+
+} // namespace security_containers
+
+
+#endif // SERVER_PROXY_CALL_POLICY_HPP
index fac2f9c..816a5bc 100644 (file)
@@ -110,3 +110,6 @@ INSTALL(FILES        ${network_admin_CONF}
 
 INSTALL(FILES        ${connection_CONF}
         DESTINATION  ${SC_TEST_CONFIG_INSTALL_DIR}/server/ut-container-connection)
+
+INSTALL(FILES        dbus-1/system.d/org.tizen.containers.tests.conf
+        DESTINATION  ${SYSCONF_INSTALL_DIR}/dbus-1/system.d/)
diff --git a/tests/unit_tests/server/configs/dbus-1/system.d/org.tizen.containers.tests.conf b/tests/unit_tests/server/configs/dbus-1/system.d/org.tizen.containers.tests.conf
new file mode 100644 (file)
index 0000000..be6c6d6
--- /dev/null
@@ -0,0 +1,14 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+        "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+
+<busconfig>
+        <policy user="root">
+                <allow own="org.tizen.containers.tests"/>
+                <allow send_destination="org.tizen.containers.tests"/>
+                <allow receive_sender="org.tizen.containers.tests"/>
+        </policy>
+        <policy context="default">
+                <allow send_destination="org.tizen.containers.tests"/>
+                <allow receive_sender="org.tizen.containers.tests"/>
+        </policy>
+</busconfig>
index c2d0ca3..92abbe2 100644 (file)
@@ -8,5 +8,6 @@
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
                      "numberOfEvents" : 2,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : []
 }
index 7b380cb..a19268c 100644 (file)
@@ -8,5 +8,6 @@
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
                      "numberOfEvents" : 2,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : []
 }
index 8f177ae..bcaba00 100644 (file)
@@ -8,5 +8,6 @@
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
                      "numberOfEvents" : 2,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : []
 }
index c5a42c0..6147c03 100644 (file)
@@ -8,5 +8,6 @@
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
                      "numberOfEvents" : 2,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : []
 }
index f7aedc6..2a6ad35 100644 (file)
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
                      "numberOfEvents" : 2,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : [{"caller" : "*",
+                         "target" : "*",
+                         "targetBusName" : "org.tizen.containers.tests",
+                         "targetObjectPath" : "*",
+                         "targetInterface" : "*",
+                         "targetMethod" : "*"}]
 }
index 9e2298f..cad519c 100644 (file)
@@ -8,5 +8,6 @@
                      "device" : "/dev/doesnotexist",
                      "code" : 139,
                      "numberOfEvents" : 2,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : []
 }
index 5c3b373..868f12b 100644 (file)
@@ -8,5 +8,6 @@
                      "device" : "gpio-keys.4",
                      "code" : 139,
                      "numberOfEvents" : 2,
-                     "timeWindowMs" : 500}
+                     "timeWindowMs" : 500},
+    "proxyCallRules" : []
 }
diff --git a/tests/unit_tests/server/test-dbus-definitions.hpp b/tests/unit_tests/server/test-dbus-definitions.hpp
new file mode 100644 (file)
index 0000000..ddad4ac
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Contact: Piotr Bartosiewicz <p.bartosiewi@partner.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
+ * @author  Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
+ * @brief   Common definitions for dbus tests
+ */
+
+#ifndef UNIT_TESTS_SERVER_TEST_DBUS_DEFINITIONS_HPP
+#define UNIT_TESTS_SERVER_TEST_DBUS_DEFINITIONS_HPP
+
+#include <string>
+
+
+namespace security_containers {
+namespace testapi {
+
+
+const std::string BUS_NAME       = "org.tizen.containers.tests";
+const std::string OBJECT_PATH    = "/org/tizen/containers/tests";
+const std::string INTERFACE      = "tests.api";
+const std::string METHOD         = "Method";
+
+const std::string DEFINITION =
+    "<node>"
+    "  <interface name='" + INTERFACE + "'>"
+    "    <method name='" + METHOD + "'>"
+    "      <arg type='s' name='argument' direction='in'/>"
+    "      <arg type='s' name='response' direction='out'/>"
+    "    </method>"
+    "  </interface>"
+    "</node>";
+
+
+} // namespace testapi
+} // namespace security_containers
+
+
+#endif // UNIT_TESTS_SERVER_TEST_DBUS_DEFINITIONS_HPP
index bf91d9f..64659ef 100644 (file)
@@ -28,6 +28,8 @@
 
 #include "containers-manager.hpp"
 #include "container-dbus-definitions.hpp"
+#include "host-dbus-definitions.hpp"
+#include "test-dbus-definitions.hpp"
 // TODO: Switch to real power-manager dbus defs when they will be implemented in power-manager
 #include "fake-power-manager-dbus-definitions.hpp"
 #include "exception.hpp"
@@ -73,6 +75,10 @@ const std::string FILE_CONTENT = "File content\n"
 
 class DbusAccessory {
 public:
+    typedef std::function<void(const std::string& argument,
+                               MethodResultBuilder::Pointer result
+                              )> TestApiMethodCallback;
+
     DbusAccessory(int id)
         : mId(id),
           mClient(DbusConnection::create(acquireAddress())),
@@ -153,6 +159,65 @@ public:
         return std::string(retcode);
     }
 
+    void registerTestApiObject(const TestApiMethodCallback& callback)
+    {
+        auto handler = [callback](const std::string& objectPath,
+                          const std::string& interface,
+                          const std::string& methodName,
+                          GVariant* parameters,
+                          MethodResultBuilder::Pointer result) {
+            if (objectPath == testapi::OBJECT_PATH &&
+                interface == testapi::INTERFACE &&
+                methodName == testapi::METHOD) {
+                const gchar* argument = NULL;
+                g_variant_get(parameters, "(&s)", &argument);
+                if (callback) {
+                    callback(argument, result);
+                }
+            }
+        };
+        mClient->registerObject(testapi::OBJECT_PATH, testapi::DEFINITION, handler);
+    }
+
+    std::string testApiProxyCall(const std::string& target, const std::string& argument)
+    {
+        GVariant* parameters = g_variant_new("(s)", argument.c_str());
+        GVariantPtr result = proxyCall(target,
+                                       testapi::BUS_NAME,
+                                       testapi::OBJECT_PATH,
+                                       testapi::INTERFACE,
+                                       testapi::METHOD,
+                                       parameters);
+        const gchar* ret = NULL;
+        g_variant_get(result.get(), "(&s)", &ret);
+        return ret;
+    }
+
+
+    GVariantPtr proxyCall(const std::string& target,
+                          const std::string& busName,
+                          const std::string& objectPath,
+                          const std::string& interface,
+                          const std::string& method,
+                          GVariant* parameters)
+    {
+        GVariant* packedParameters = g_variant_new("(sssssv)",
+                                                   target.c_str(),
+                                                   busName.c_str(),
+                                                   objectPath.c_str(),
+                                                   interface.c_str(),
+                                                   method.c_str(),
+                                                   parameters);
+        GVariantPtr result = mClient->callMethod(isHost() ? hostapi::BUS_NAME : api::BUS_NAME,
+                                                 isHost() ? hostapi::OBJECT_PATH : api::OBJECT_PATH,
+                                                 isHost() ? hostapi::INTERFACE : api::INTERFACE,
+                                                 isHost() ? hostapi::METHOD_PROXY_CALL : api::METHOD_PROXY_CALL,
+                                                 packedParameters,
+                                                 "(v)");
+        GVariant* unpackedResult = NULL;
+        g_variant_get(result.get(), "(v)", &unpackedResult);
+        return GVariantPtr(unpackedResult, g_variant_unref);
+    }
 private:
     const int mId;
     DbusConnection::Pointer mClient;
@@ -161,13 +226,25 @@ private:
     std::mutex mMutex;
     std::condition_variable mNameCondition;
 
+    bool isHost() const {
+        return mId == 0;
+    }
+
     std::string acquireAddress() const
     {
+        if (isHost()) {
+            return "unix:path=/var/run/dbus/system_bus_socket";
+        }
         return "unix:path=/tmp/ut-containers-manager/console" + std::to_string(mId) +
                "-dbus/dbus/system_bus_socket";
     }
 };
 
+std::function<bool(const std::exception&)> expectedMessage(const std::string& message) {
+    return [=](const std::exception& e) {
+        return e.what() == message;
+    };
+}
 
 struct Fixture {
     utils::ScopedGlibLoop mLoop;
@@ -496,5 +573,76 @@ BOOST_AUTO_TEST_CASE(AllowSwitchToDefaultTest)
     }
 }
 
+BOOST_AUTO_TEST_CASE(ProxyCallTest)
+{
+    ContainersManager cm(TEST_DBUS_CONFIG_PATH);
+    cm.startAll();
+
+    std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
+    for (int i = 0; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
+        dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
+    }
+
+    for (auto& dbus : dbuses) {
+        dbus.second->setName(testapi::BUS_NAME);
+
+        const int id = dbus.first;
+        auto handler = [id](const std::string& argument, MethodResultBuilder::Pointer result) {
+            if (argument.empty()) {
+                result->setError("org.tizen.containers.Error.Test", "Test error");
+            } else {
+                std::string ret = "reply from " + std::to_string(id) + ": " + argument;
+                result->set(g_variant_new("(s)", ret.c_str()));
+            }
+        };
+        dbus.second->registerTestApiObject(handler);
+    }
+
+    // host -> container2
+    BOOST_CHECK_EQUAL("reply from 2: param1",
+                      dbuses.at(0)->testApiProxyCall("ut-containers-manager-console2-dbus",
+                                                     "param1"));
+
+    // host -> host
+    BOOST_CHECK_EQUAL("reply from 0: param2",
+                      dbuses.at(0)->testApiProxyCall("host",
+                                                     "param2"));
+
+    // container1 -> host
+    BOOST_CHECK_EQUAL("reply from 0: param3",
+                      dbuses.at(1)->testApiProxyCall("host",
+                                                     "param3"));
+
+    // container1 -> container2
+    BOOST_CHECK_EQUAL("reply from 2: param4",
+                      dbuses.at(1)->testApiProxyCall("ut-containers-manager-console2-dbus",
+                                                     "param4"));
+
+    // container2 -> container2
+    BOOST_CHECK_EQUAL("reply from 2: param5",
+                      dbuses.at(2)->testApiProxyCall("ut-containers-manager-console2-dbus",
+                                                     "param5"));
+
+    // host -> unknown
+    BOOST_CHECK_EXCEPTION(dbuses.at(0)->testApiProxyCall("unknown", "param"),
+                          DbusCustomException,
+                          expectedMessage("Unknown proxy call target"));
+
+    // forwarding error
+    BOOST_CHECK_EXCEPTION(dbuses.at(0)->testApiProxyCall("host", ""),
+                          DbusCustomException,
+                          expectedMessage("Test error"));
+
+    // forbidden call
+    BOOST_CHECK_EXCEPTION(dbuses.at(0)->proxyCall("host",
+                                              "org.fake",
+                                              "/a/b",
+                                              "c.d",
+                                              "foo",
+                                              g_variant_new("(s)", "arg")),
+                          DbusCustomException,
+                          expectedMessage("Proxy call forbidden"));
+}
+
 
 BOOST_AUTO_TEST_SUITE_END()