[Bug/Feature] Add an ability for a container to request a file move to another container.
Some minor fixes here and there.
Added missing tests for utils/fs.
A little revamp of DbusAccessory in ut-cm tests.
[Cause] N/A
[Solution] Implement a DBUS API and a simple move implementation.
[Verification] Build, install, run tests, run server.
Change-Id: I881f7b6079e38e3dd43d6fe34360457172047c2c
Signed-off-by: Lukasz Pawelczyk <l.pawelczyk@partner.samsung.com>
#define BOOST_PP_VARIADICS 1
#endif
+// This has to be defined always when the boost has not been compiled
+// using C++11. Headers detect that you are compiling using C++11 and
+// blindly and wrongly assume that boost has been as well.
+#ifndef BOOST_NO_CXX11_SCOPED_ENUMS
+#define BOOST_NO_CXX11_SCOPED_ENUMS 1
+#endif
+
#endif // COMMON_CONFIG_HPP
#include "utils/paths.hpp"
#include "utils/exception.hpp"
+#include <boost/filesystem.hpp>
#include <dirent.h>
#include <fstream>
#include <streambuf>
bool isMountPoint(const std::string& path, bool& result)
{
- struct stat stat, parentStat;
std::string parentPath = dirName(path);
+ bool newResult;
+ bool ret = hasSameMountPoint(path, parentPath, newResult);
- if (::stat(path.c_str(), &stat)) {
- LOGD("Failed to get stat of " << path << ": " << strerror(errno));
+ result = !newResult;
+ return ret;
+}
+
+bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool& result)
+{
+ struct stat s1, s2;
+
+ if (::stat(path1.c_str(), &s1)) {
+ LOGD("Failed to get stat of " << path1 << ": " << strerror(errno));
return false;
}
- if (::stat(parentPath.c_str(), &parentStat)) {
- LOGD("Failed to get stat of " << parentPath << ": " << strerror(errno));
+ if (::stat(path2.c_str(), &s2)) {
+ LOGD("Failed to get stat of " << path2 << ": " << strerror(errno));
return false;
}
- result = (stat.st_dev != parentStat.st_dev);
+ result = (s1.st_dev == s2.st_dev);
+ return true;
+}
+
+bool moveFile(const std::string& src, const std::string& dst)
+{
+ bool bResult;
+
+ namespace fs = boost::filesystem;
+ boost::system::error_code error;
+
+ // The destination has to be a full path (including a file name)
+ // so it doesn't exist yet, we need to check upper level dir instead.
+ if (!hasSameMountPoint(src, dirName(dst), bResult)) {
+ LOGE("Failed to check the files' mount points");
+ return false;
+ }
+
+ if (bResult) {
+ fs::rename(src, dst, error);
+ if (error) {
+ LOGE("Failed to rename the file: " << error);
+ return false;
+ }
+ } else {
+ fs::copy_file(src, dst, error);
+ if (error) {
+ LOGE("Failed to copy the file: " << error);
+ return false;
+ }
+ fs::remove(src, error);
+ if (error) {
+ LOGE("Failed to remove the file: " << error);
+ fs::remove(dst, error);
+ return false;
+ }
+ }
+
return true;
}
*/
bool isMountPoint(const std::string& path, bool& result);
+/**
+ * Checks whether the given paths are under the same mount point
+ */
+bool hasSameMountPoint(const std::string& path1, const std::string& path2, bool& result);
+
+/**
+ * Moves the file either by rename if under the same mount point
+ * or by copy&delete if under a different one.
+ * The destination has to be a full path including file name.
+ */
+bool moveFile(const std::string& src, const std::string& dst);
+
} // namespace utils
} // namespace security_containers
## Link libraries ##############################################################
-FIND_PACKAGE (Boost COMPONENTS program_options)
+FIND_PACKAGE (Boost COMPONENTS program_options system filesystem)
PKG_CHECK_MODULES(CONTAINER_DAEMON_DEPS REQUIRED gio-2.0 libsystemd-journal)
INCLUDE_DIRECTORIES(${COMMON_FOLDER})
Group: Security/Other
Summary: Daemon for managing containers
BuildRequires: cmake
+BuildRequires: boost-devel
BuildRequires: libvirt-devel
BuildRequires: libjson-devel
BuildRequires: pkgconfig(glib-2.0)
Summary: Security Containers Containers Daemon
Group: Security/Other
Requires: security-containers = %{version}-%{release}
-BuildRequires: pkgconfig(glib-2.0)
-BuildRequires: pkgconfig(libsystemd-journal)
%description container-daemon
Daemon running inside every container.
Requires: security-containers-client = %{version}-%{release}
Requires: python
Requires: boost-test
-BuildRequires: boost-devel
%description tests
Unit tests for both: server and client and integration tests.
## Link libraries ##############################################################
FIND_PACKAGE (Boost COMPONENTS program_options system filesystem regex)
+
PKG_CHECK_MODULES(SERVER_DEPS REQUIRED libvirt libvirt-glib-1.0 json gio-2.0 libsystemd-journal)
INCLUDE_DIRECTORIES(${COMMON_FOLDER})
INCLUDE_DIRECTORIES(SYSTEM ${SERVER_DEPS_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS})
"privilege" : 1,
"config" : "../libvirt-config/business.xml",
"networkConfig" : "../libvirt-config/business-network.xml",
- "runMountPoint" : "/var/run/containers/business/run"
+ "runMountPoint" : "/var/run/containers/business/run",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
}
"privilege" : 10,
"config" : "../libvirt-config/private.xml",
"networkConfig" : "../libvirt-config/private-network.xml",
- "runMountPoint" : "/var/run/containers/private/run"
+ "runMountPoint" : "/var/run/containers/private/run",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
}
{
"containerConfigs" : ["containers/private.conf", "containers/business.conf" ],
+ "containersPath" : "/opt/usr/containers",
"foregroundId" : "private",
"defaultId" : "private",
"inputConfig" : {"enabled" : true,
<source network='business'/>
</interface>
<filesystem type='mount'>
+ <!-- TODO: this directory could be generated from .conf files-->
<source dir='/opt/usr/containers/business'/>
<target dir='/'/>
</filesystem>
<!-- Enable access to the containers dbus -->
<filesystem type='mount'>
+ <!-- TODO: this directory could be generated from .conf files-->
<source dir='/var/run/containers/business/run'/>
<target dir='/var/run'/>
</filesystem>
<source network='private'/>
</interface>
<filesystem type='mount'>
+ <!-- TODO: this directory could be generated from .conf files-->
<source dir='/opt/usr/containers/private'/>
<target dir='/'/>
</filesystem>
<!-- Enable access to the containers dbus -->
<filesystem type='mount'>
+ <!-- TODO: this directory could be generated from .conf files-->
<source dir='/var/run/containers/private/run'/>
<target dir='/var/run'/>
</filesystem>
#include "config/fields.hpp"
#include <string>
+#include <vector>
namespace security_containers {
*/
std::string runMountPoint;
+ /**
+ * When you move a file out of the container (by move request)
+ * its path must match at least one of the regexps in this vector.
+ */
+ std::vector<std::string> permittedToSend;
+
+ /**
+ * When you move a file to the container (by move request)
+ * its path must match at least one of the regexps in this vector.
+ */
+ std::vector<std::string> permittedToRecv;
+
CONFIG_REGISTER
(
privilege,
networkConfig,
cpuQuotaForeground,
cpuQuotaBackground,
- runMountPoint
+ runMountPoint,
+ permittedToSend,
+ permittedToRecv
)
};
mDisplayOffCallback = callback;
}
+void ContainerConnection::setFileMoveRequestCallback(
+ const FileMoveRequestCallback& callback)
+{
+ mFileMoveRequestCallback = callback;
+}
+
void ContainerConnection::onMessageCall(const std::string& objectPath,
const std::string& interface,
const std::string& methodName,
result.setVoid();
}
}
+
+ if (methodName == api::METHOD_FILE_MOVE_REQUEST) {
+ const gchar* destination = NULL;
+ const gchar* path = NULL;
+ g_variant_get(parameters, "(&s&s)", &destination, &path);
+ if (mFileMoveRequestCallback) {
+ mFileMoveRequestCallback(destination, path, result);
+ }
+ }
}
void ContainerConnection::onSignalReceived(const std::string& senderBusName,
const std::string& message
)> NotifyActiveContainerCallback;
+ typedef std::function<void(const std::string& destination,
+ const std::string& path,
+ dbus::MethodResultBuilder& result
+ )> FileMoveRequestCallback;
+
/**
* Register notification request callback
*/
*/
void setDisplayOffCallback(const DisplayOffCallback& callback);
+ /*
+ * Register file move request callback
+ */
+ void setFileMoveRequestCallback(const FileMoveRequestCallback& callback);
+
/**
* Send notification signal to this container
*/
OnNameLostCallback mOnNameLostCallback;
NotifyActiveContainerCallback mNotifyActiveContainerCallback;
DisplayOffCallback mDisplayOffCallback;
+ FileMoveRequestCallback mFileMoveRequestCallback;
void onNameAcquired();
void onNameLost();
const std::string INTERFACE = "org.tizen.containers.manager";
const std::string METHOD_NOTIFY_ACTIVE_CONTAINER = "NotifyActiveContainer";
+const std::string METHOD_FILE_MOVE_REQUEST = "FileMoveRequest";
const std::string SIGNAL_NOTIFICATION = "Notification";
+const std::string FILE_MOVE_DESTINATION_NOT_FOUND = "FILE_MOVE_DESTINATION_NOT_FOUND";
+const std::string FILE_MOVE_WRONG_DESTINATION = "FILE_MOVE_WRONG_DESTINATION";
+const std::string FILE_MOVE_NO_PERMISSIONS_SEND = "FILE_MOVE_NO_PERMISSIONS_SEND";
+const std::string FILE_MOVE_NO_PERMISSIONS_RECEIVE = "FILE_MOVE_NO_PERMISSIONS_RECEIVE";
+const std::string FILE_MOVE_FAILED = "FILE_MOVE_FAILED";
+const std::string FILE_MOVE_SUCCEEDED = "FILE_MOVE_SUCCEEDED";
+
+
const std::string DEFINITION =
"<node>"
" <interface name='" + INTERFACE + "'>"
" <arg type='s' name='application' direction='in'/>"
" <arg type='s' name='message' direction='in'/>"
" </method>"
+ " <method name='" + METHOD_FILE_MOVE_REQUEST + "'>"
+ " <arg type='s' name='destination' direction='in'/>"
+ " <arg type='s' name='path' direction='in'/>"
+ " <arg type='s' name='result' direction='out'/>"
+ " </method>"
" <signal name='" + SIGNAL_NOTIFICATION + "'>"
" <arg type='s' name='container'/>"
" <arg type='s' name='application'/>"
{
config::loadFromFile(containerConfigPath, mConfig);
+ for (std::string r: mConfig.permittedToSend) {
+ mPermittedToSend.push_back(boost::regex(r));
+ }
+ for (std::string r: mConfig.permittedToRecv) {
+ mPermittedToRecv.push_back(boost::regex(r));
+ }
+
const std::string baseConfigPath = utils::dirName(containerConfigPath);
mConfig.config = fs::absolute(mConfig.config, baseConfigPath).string();
mConfig.networkConfig = fs::absolute(mConfig.networkConfig, baseConfigPath).string();
}
}
+const std::vector<boost::regex>& Container::getPermittedToSend() const
+{
+ return mPermittedToSend;
+}
+
+const std::vector<boost::regex>& Container::getPermittedToRecv() const
+{
+ return mPermittedToRecv;
+}
+
const std::string& Container::getId() const
{
Lock lock(mReconnectMutex);
if (mDisplayOffCallback) {
mConnection->setDisplayOffCallback(mDisplayOffCallback);
}
+ if (mFileMoveCallback) {
+ mConnection->setFileMoveRequestCallback(mFileMoveCallback);
+ }
// Send to the background only after we're connected,
// otherwise it'd take ages.
if (mConnection) {
mConnection->setNotifyActiveContainerCallback(mNotifyCallback);
}
-
}
void Container::sendNotification(const std::string& container,
}
}
+void Container::setFileMoveRequestCallback(const FileMoveRequestCallback& callback)
+{
+ Lock lock(mReconnectMutex);
+
+ mFileMoveCallback = callback;
+ if (mConnection) {
+ mConnection->setFileMoveRequestCallback(callback);
+ }
+}
+
} // namespace security_containers
#include <string>
#include <memory>
#include <thread>
+#include <boost/regex.hpp>
namespace security_containers {
typedef ContainerConnection::NotifyActiveContainerCallback NotifyActiveContainerCallback;
typedef ContainerConnection::DisplayOffCallback DisplayOffCallback;
+ typedef ContainerConnection::FileMoveRequestCallback FileMoveRequestCallback;
+
+ /**
+ * Returns a vector of regexps defining files permitted to be
+ * send to other containers using file move functionality
+ */
+ const std::vector<boost::regex>& getPermittedToSend() const;
+
+ /**
+ * Returns a vector of regexps defining files permitted to be
+ * send to other containers using file move functionality
+ */
+ const std::vector<boost::regex>& getPermittedToRecv() const;
+
+ // ContainerAdmin API
/**
* Get the container id
*/
bool isPaused();
+ // ContainerConnection API
+
/**
* Register notification request callback
*/
const std::string& application,
const std::string& message);
+ /**
+ * Register file move request callback
+ */
+ void setFileMoveRequestCallback(const FileMoveRequestCallback& callback);
+
private:
ContainerConfig mConfig;
+ std::vector<boost::regex> mPermittedToSend;
+ std::vector<boost::regex> mPermittedToRecv;
std::unique_ptr<ContainerConnectionTransport> mConnectionTransport;
std::unique_ptr<NetworkAdmin> mNetworkAdmin;
std::unique_ptr<ContainerAdmin> mAdmin;
mutable std::recursive_mutex mReconnectMutex;
NotifyActiveContainerCallback mNotifyCallback;
DisplayOffCallback mDisplayOffCallback;
+ FileMoveRequestCallback mFileMoveCallback;
void onNameLostCallback();
void reconnectHandler();
struct ContainersManagerConfig {
/**
- * Parameters describing input device used to switch between containers
- */
- InputConfig inputConfig;
-
- /**
* List of containers' configs that we manage.
* File paths can be relative to the ContainerManager config file.
*/
*/
std::string defaultId;
+ /**
+ * A path where the containers mount points reside.
+ */
+ std::string containersPath;
+
+ /*
+ * Parameters describing input device used to switch between containers
+ */
+ InputConfig inputConfig;
+
CONFIG_REGISTER
(
containerConfigs,
foregroundId,
defaultId,
+ containersPath,
inputConfig
)
};
#include "config.hpp"
+#include "container-dbus-definitions.hpp"
#include "containers-manager.hpp"
#include "container-admin.hpp"
#include "exception.hpp"
#include "log/logger.hpp"
#include "config/manager.hpp"
+#include <boost/filesystem.hpp>
+#include <boost/regex.hpp>
#include <cassert>
#include <string>
#include <climits>
namespace security_containers {
+namespace {
+
+bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v)
+{
+ for (const boost::regex& toMatch: v) {
+ if (boost::regex_match(str, toMatch)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace
+
ContainersManager::ContainersManager(const std::string& managerConfigPath): mDetachOnExit(false)
{
LOGD("Instantiating ContainersManager object...");
std::string id = c->getId();
using namespace std::placeholders;
c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
- this,
- id,
- _1,
- _2));
+ this, id, _1, _2));
c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
- this,
- id));
+ this, id));
+
+ c->setFileMoveRequestCallback(std::bind(&ContainersManager::handleContainerMoveFileRequest,
+ this, id, _1, _2, _3));
mContainers.insert(ContainerMap::value_type(id, std::move(c)));
}
focus(mConfig.defaultId);
}
+void ContainersManager::handleContainerMoveFileRequest(const std::string& srcContainerId,
+ const std::string& dstContainerId,
+ const std::string& path,
+ dbus::MethodResultBuilder& result)
+{
+ // TODO: this implementation is only a placeholder.
+ // There are too many unanswered questions and security concerns:
+ // 1. What about mount namespace, host might not see the source/destination
+ // file. The file might be a different file from a host perspective.
+ // 2. Copy vs move (speed and security concerns over already opened FDs)
+ // 3. Access to source and destination files - DAC, uid/gig
+ // 4. Access to source and destintation files - MAC, smack
+ // 5. Destination file uid/gid assignment
+ // 6. Destination file smack label assignment
+ // 7. Verifiability of the source path
+
+ // NOTE: other possible implementations include:
+ // 1. Sending file descriptors opened directly in each container through DBUS
+ // using something like g_dbus_message_set_unix_fd_list()
+ // 2. SCS forking and calling setns(MNT) in each container and opening files
+ // by itself, then passing FDs to the main process
+ // Now when the main process has obtained FDs (by either of those methods)
+ // it can do the copying by itself.
+
+ LOGI("File move requested\n"
+ << "src: " << srcContainerId << "\n"
+ << "dst: " << dstContainerId << "\n"
+ << "path: " << path);
+
+ ContainerMap::const_iterator srcIter = mContainers.find(srcContainerId);
+ if (srcIter == mContainers.end()) {
+ LOGE("Source container '" << srcContainerId << "' not found");
+ return;
+ }
+ Container& srcContainer = *srcIter->second;
+
+ ContainerMap::const_iterator dstIter = mContainers.find(dstContainerId);
+ if (dstIter == mContainers.end()) {
+ LOGE("Destination container '" << dstContainerId << "' not found");
+ result.set(g_variant_new("(s)", api::FILE_MOVE_DESTINATION_NOT_FOUND.c_str()));
+ return;
+ }
+ Container& dstContanier = *dstIter->second;
+
+ if (srcContainerId == dstContainerId) {
+ LOGE("Cannot send a file to yourself");
+ result.set(g_variant_new("(s)", api::FILE_MOVE_WRONG_DESTINATION.c_str()));
+ return;
+ }
+
+ if (!regexMatchVector(path, srcContainer.getPermittedToSend())) {
+ LOGE("Source container has no permissions to send the file: " << path);
+ result.set(g_variant_new("(s)", api::FILE_MOVE_NO_PERMISSIONS_SEND.c_str()));
+ return;
+ }
+
+ if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
+ LOGE("Destination container has no permissions to receive the file: " << path);
+ result.set(g_variant_new("(s)", api::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str()));
+ return;
+ }
+
+ namespace fs = boost::filesystem;
+ std::string srcPath = fs::absolute(srcContainerId, mConfig.containersPath).string() + path;
+ std::string dstPath = fs::absolute(dstContainerId, mConfig.containersPath).string() + path;
+
+ if (!utils::moveFile(srcPath, dstPath)) {
+ LOGE("Failed to move the file: " << path);
+ result.set(g_variant_new("(s)", api::FILE_MOVE_FAILED.c_str()));
+ } else {
+ result.set(g_variant_new("(s)", api::FILE_MOVE_SUCCEEDED.c_str()));
+ try {
+ dstContanier.sendNotification(srcContainerId, path, api::FILE_MOVE_SUCCEEDED);
+ } catch (ServerException&) {
+ LOGE("Notification to '" << dstContainerId << "' has not been sent");
+ }
+ }
+}
+
+
} // namespace security_containers
const std::string& appliaction,
const std::string& message);
void displayOffHandler(const std::string& caller);
+ void handleContainerMoveFileRequest(const std::string& srcContainerId,
+ const std::string& dstContainerId,
+ const std::string& path,
+ dbus::MethodResultBuilder& result);
};
"networkConfig" : "",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "../libvirt-config/network.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "../libvirt-config/network.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : "/tmp/ut-container"
+ "runMountPoint" : "/tmp/ut-container",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "../libvirt-config/network.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"containerConfigs" : ["containers/console1.conf", "missing/file/path/missing.conf", "containers/console3.conf"],
"foregroundId" : "ut-containers-manager-console1",
"defaultId" : "ut-containers-manager-console1",
+ "containersPath" : "/tmp",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
{
- "containerConfigs" : ["containers/console1-dbus.conf", "containers/console2-dbus.conf", "containers/console3-dbus.conf"],
+ "containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
"foregroundId" : "ut-containers-manager-console1",
"defaultId" : "in_no_way_there_is_a_valid_id_here",
+ "containersPath" : "/tmp",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
"containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
"foregroundId" : "this_id_does_not_exist",
"defaultId" : "ut-containers-manager-console1",
+ "containersPath" : "/tmp",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
"networkConfig" : "../libvirt-config/network1.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : "/tmp/ut-containers-manager/console1-dbus"
+ "runMountPoint" : "/tmp/ut-containers-manager/console1-dbus",
+ "permittedToSend" : [ "/tmp/.*", "/etc/secret2" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
}
"networkConfig" : "../libvirt-config/network1.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
}
"networkConfig" : "../libvirt-config/network2.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : "/tmp/ut-containers-manager/console2-dbus"
+ "runMountPoint" : "/tmp/ut-containers-manager/console2-dbus",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*", "/etc/secret1" ]
}
"networkConfig" : "../libvirt-config/network2.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
}
"networkConfig" : "../libvirt-config/network3.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : "/tmp/ut-containers-manager/console3-dbus"
+ "runMountPoint" : "/tmp/ut-containers-manager/console3-dbus",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
}
"networkConfig" : "../libvirt-config/network3.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [ "/tmp/.*" ],
+ "permittedToRecv" : [ "/tmp/.*" ]
}
<domain type="lxc">
- <name>ut-containers-manager-console1</name>
+ <name>ut-containers-manager-console1-dbus</name>
<uuid>58184009-b278-4d01-975d-708393690084</uuid>
<memory>102400</memory>
<os>
<domain type="lxc">
- <name>ut-containers-manager-console2</name>
+ <name>ut-containers-manager-console2-dbus</name>
<uuid>3d18323e-4ada-4a1b-a907-836701891306</uuid>
<memory>102400</memory>
<os>
<domain type="lxc">
- <name>ut-containers-manager-console3</name>
+ <name>ut-containers-manager-console3-dbus</name>
<uuid>71cb8511-7474-4e90-865a-3360b7f77254</uuid>
<memory>102400</memory>
<os>
"containerConfigs" : ["containers/console1.conf", "containers/console2.conf", "containers/console3.conf"],
"foregroundId" : "ut-containers-manager-console1",
"defaultId" : "ut-containers-manager-console1",
+ "containersPath" : "/tmp",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
"containerConfigs" : ["containers/console1-dbus.conf",
"containers/console2-dbus.conf",
"containers/console3-dbus.conf"],
- "foregroundId" : "ut-containers-manager-console1",
- "defaultId" : "ut-containers-manager-console1",
+ "foregroundId" : "ut-containers-manager-console1-dbus",
+ "defaultId" : "ut-containers-manager-console1-dbus",
+ "containersPath" : "/tmp",
"inputConfig" : {"enabled" : false,
"device" : "/dev/doesnotexist",
"code" : 139,
"networkConfig" : "@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-network-admin/libvirt-config/buggy-network.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "@SC_TEST_CONFIG_INSTALL_DIR@/server/ut-network-admin/libvirt-config/network.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
{
"containerConfigs" : ["containers/container1.conf", "missing/file/path/missing.conf", "containers/container3.conf"],
+ "containersPath" : "/tmp",
"foregroundId" : "ut-server-container1",
"defaultId" : "ut-server-container1",
"inputConfig" : {"enabled" : false,
"networkConfig" : "../libvirt-config/network1.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "../libvirt-config/network2.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
"networkConfig" : "../libvirt-config/network3.xml",
"cpuQuotaForeground" : -1,
"cpuQuotaBackground" : 1000,
- "runMountPoint" : ""
+ "runMountPoint" : "",
+ "permittedToSend" : [],
+ "permittedToRecv" : []
}
{
"containerConfigs" : ["containers/container1.conf", "containers/container2.conf", "containers/container3.conf"],
+ "containersPath" : "/tmp",
"foregroundId" : "ut-server-container1",
"defaultId" : "ut-server-container1",
"inputConfig" : {"enabled" : false,
#include "utils/glib-loop.hpp"
#include "config/exception.hpp"
#include "utils/latch.hpp"
+#include "utils/fs.hpp"
+#include <vector>
+#include <map>
#include <memory>
#include <string>
#include <algorithm>
#include <functional>
#include <mutex>
#include <condition_variable>
-
+#include <boost/filesystem.hpp>
using namespace security_containers;
using namespace security_containers::config;
const int TEST_DBUS_CONNECTION_CONTAINERS_COUNT = 3;
const std::string PREFIX_CONSOLE_NAME = "ut-containers-manager-console";
const std::string TEST_APP_NAME = "testapp";
-const std::string TEST_MESSGAE = "testmessage";
+const std::string TEST_MESSAGE = "testmessage";
+const std::string FILE_CONTENT = "File content\n"
+ "Line 1\n"
+ "Line 2\n";
class DbusAccessory {
public:
- std::vector<std::string> mReceivedSignalsSource;
-
DbusAccessory(int id)
: mId(id),
mClient(DbusConnection::create(acquireAddress())),
mNameCondition.notify_one();
}
- void signalSubscribe(Latch& referenceLatch)
+ void signalSubscribe(const DbusConnection::SignalCallback& callback)
{
- using namespace std::placeholders;
- mClient->signalSubscribe(std::bind(&DbusAccessory::handler,
- this, std::ref(referenceLatch), _1, _2, _3, _4, _5),
- api::BUS_NAME);
+ mClient->signalSubscribe(callback, api::BUS_NAME);
}
void emitSignal(const std::string& objectPath,
mClient->emitSignal(objectPath, interface, name, parameters);
}
- void callMethod()
+ void callMethodNotify()
{
- GVariant* parameters = g_variant_new("(ss)", TEST_APP_NAME.c_str(), TEST_MESSGAE.c_str());
+ GVariant* parameters = g_variant_new("(ss)", TEST_APP_NAME.c_str(), TEST_MESSAGE.c_str());
mClient->callMethod(api::BUS_NAME,
api::OBJECT_PATH,
api::INTERFACE,
"()");
}
- std::string getContainerName() const
+ std::string callMethodMove(const std::string& dest, const std::string& path)
{
- return PREFIX_CONSOLE_NAME + std::to_string(mId);
+ GVariant* parameters = g_variant_new("(ss)", dest.c_str(), path.c_str());
+ GVariantPtr result = mClient->callMethod(api::BUS_NAME,
+ api::OBJECT_PATH,
+ api::INTERFACE,
+ api::METHOD_FILE_MOVE_REQUEST,
+ parameters,
+ "(s)");
+
+ const gchar* retcode = NULL;
+ g_variant_get(result.get(), "(&s)", &retcode);
+ return std::string(retcode);
}
private:
return "unix:path=/tmp/ut-containers-manager/console" + std::to_string(mId) +
"-dbus/dbus/system_bus_socket";
}
-
- void handler(Latch& referenceLatch,
- const std::string& /*senderBusName*/,
- const std::string& objectPath,
- const std::string& interface,
- const std::string& signalName,
- GVariant* parameters)
- {
- if (objectPath == api::OBJECT_PATH &&
- interface == api::INTERFACE &&
- signalName == api::SIGNAL_NOTIFICATION &&
- g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
-
- const gchar* container = NULL;
- const gchar* application = NULL;
- const gchar* message = NULL;
- g_variant_get(parameters, "(&s&s&s)", &container, &application, &message);
- mReceivedSignalsSource.push_back(container);
- if (application == TEST_APP_NAME && message == TEST_MESSGAE) {
- referenceLatch.set();
- }
- }
- }
};
BOOST_AUTO_TEST_CASE(NotifyActiveContainerTest)
{
- Latch signalReceivedLatch;
-
ContainersManager cm(TEST_DBUS_CONFIG_PATH);
cm.startAll();
- std::vector< std::unique_ptr<DbusAccessory> > dbuses;
+ std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
- dbuses.push_back(std::unique_ptr<DbusAccessory>(new DbusAccessory(i)));
+ dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
}
- for (auto& dbus : dbuses) {
- dbus->signalSubscribe(signalReceivedLatch);
+
+ Latch signalReceivedLatch;
+ std::map<int, std::vector<std::string>> signalReceivedSourcesMap;
+ auto handler = [](Latch& latch,
+ std::vector<std::string>& receivedSignalSources,
+ const std::string& /*senderBusName*/,
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& signalName,
+ GVariant* parameters)
+ {
+ if (objectPath == api::OBJECT_PATH &&
+ interface == api::INTERFACE &&
+ signalName == api::SIGNAL_NOTIFICATION &&
+ g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
+
+ const gchar* container = NULL;
+ const gchar* application = NULL;
+ const gchar* message = NULL;
+ g_variant_get(parameters, "(&s&s&s)", &container, &application, &message);
+ receivedSignalSources.push_back(container);
+ if (application == TEST_APP_NAME && message == TEST_MESSAGE) {
+ latch.set();
+ }
+ }
+ };
+
+ using namespace std::placeholders;
+ for (int i = 1; i <= TEST_DBUS_CONNECTION_CONTAINERS_COUNT; ++i) {
+ dbuses[i]->signalSubscribe(std::bind(handler,
+ std::ref(signalReceivedLatch),
+ std::ref(signalReceivedSourcesMap[i]),
+ _1, _2, _3, _4, _5));
}
for (auto& dbus : dbuses) {
- dbus->callMethod();
+ dbus.second->callMethodNotify();
}
BOOST_CHECK(signalReceivedLatch.waitForN(dbuses.size() - 1, EVENT_TIMEOUT));
BOOST_CHECK(signalReceivedLatch.empty());
//check if there are no signals that was received more than once
- for (const auto& source : dbuses[0]->mReceivedSignalsSource) {
- BOOST_CHECK_EQUAL(std::count(dbuses[0]->mReceivedSignalsSource.begin(),
- dbuses[0]->mReceivedSignalsSource.end(),
- source), 1);
+ for (const auto& source : signalReceivedSourcesMap[1]) {
+ BOOST_CHECK_EQUAL(std::count(signalReceivedSourcesMap[1].begin(),
+ signalReceivedSourcesMap[1].end(),
+ source), 1);
}
//check if all signals was received by active container
- BOOST_CHECK_EQUAL(dbuses[0]->mReceivedSignalsSource.size(), dbuses.size() - 1);
+ BOOST_CHECK_EQUAL(signalReceivedSourcesMap[1].size(), dbuses.size() - 1);
//check if no signals was received by inactive container
- for (size_t i = 1; i < dbuses.size(); ++i) {
- BOOST_CHECK(dbuses[i]->mReceivedSignalsSource.empty());
+ for (size_t i = 2; i <= dbuses.size(); ++i) {
+ BOOST_CHECK(signalReceivedSourcesMap[i].empty());
}
dbuses.clear();
std::unique_lock<std::mutex> Lock(Mutex);
std::condition_variable Condition;
auto cond = [&cm]() -> bool {
- return cm.getRunningForegroundContainerId() == "ut-containers-manager-console1";
+ return cm.getRunningForegroundContainerId() == "ut-containers-manager-console1-dbus";
};
for (auto& client : clients) {
// TEST SWITCHING TO DEFAULT CONTAINER
// focus non-default container
- BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3"));
+ BOOST_REQUIRE_NO_THROW(cm.focus("ut-containers-manager-console3-dbus"));
// emit signal from dbus connection
BOOST_REQUIRE_NO_THROW(client->emitSignal(fake_power_manager_api::OBJECT_PATH,
}
}
+BOOST_AUTO_TEST_CASE(MoveFileTest)
+{
+ ContainersManager cm(TEST_DBUS_CONFIG_PATH);
+ cm.startAll();
+
+ std::map<int, std::unique_ptr<DbusAccessory>> dbuses;
+ for (int i = 1; i <= 2; ++i) {
+ dbuses[i] = std::unique_ptr<DbusAccessory>(new DbusAccessory(i));
+ }
+
+ Latch notificationLatch;
+ std::string notificationSource;
+ std::string notificationPath;
+ std::string notificationRetcode;
+ auto handler = [&](const std::string& /*senderBusName*/,
+ const std::string& objectPath,
+ const std::string& interface,
+ const std::string& signalName,
+ GVariant* parameters)
+ {
+ if (objectPath == api::OBJECT_PATH &&
+ interface == api::INTERFACE &&
+ signalName == api::SIGNAL_NOTIFICATION &&
+ g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) {
+
+ const gchar* source = NULL;
+ const gchar* path = NULL;
+ const gchar* retcode = NULL;
+ g_variant_get(parameters, "(&s&s&s)", &source, &path, &retcode);
+
+ notificationSource = source;
+ notificationPath = path;
+ notificationRetcode = retcode;
+ notificationLatch.set();
+ }
+ };
+
+ // subscribe the second (destination) container for notifications
+ dbuses.at(2)->signalSubscribe(handler);
+
+ const std::string TMP = "/tmp";
+ const std::string NO_PATH = "path_doesnt_matter_here";
+ const std::string BUGGY_PATH = TMP + "/this_file_does_not_exist";
+ const std::string BUGGY_CONTAINER = "this-container-does-not-exist";
+ const std::string CONTAINER1 = "ut-containers-manager-console1-dbus";
+ const std::string CONTAINER2 = "ut-containers-manager-console2-dbus";
+ const std::string CONTAINER1PATH = TMP + "/" + CONTAINER1 + TMP;
+ const std::string CONTAINER2PATH = TMP + "/" + CONTAINER2 + TMP;
+
+ // sending to a non existing container
+ BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(BUGGY_CONTAINER, NO_PATH),
+ api::FILE_MOVE_DESTINATION_NOT_FOUND);
+ BOOST_CHECK(notificationLatch.empty());
+
+ // sending to self
+ BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER1, NO_PATH),
+ api::FILE_MOVE_WRONG_DESTINATION);
+ BOOST_CHECK(notificationLatch.empty());
+
+ // no permission to send
+ BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, "/etc/secret1"),
+ api::FILE_MOVE_NO_PERMISSIONS_SEND);
+ BOOST_CHECK(notificationLatch.empty());
+
+ // no permission to receive
+ BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, "/etc/secret2"),
+ api::FILE_MOVE_NO_PERMISSIONS_RECEIVE);
+ BOOST_CHECK(notificationLatch.empty());
+
+ // non existing file
+ BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, BUGGY_PATH),
+ api::FILE_MOVE_FAILED);
+ BOOST_CHECK(notificationLatch.empty());
+
+ // a working scenario
+ namespace fs = boost::filesystem;
+ boost::system::error_code ec;
+ fs::remove_all(CONTAINER1PATH, ec);
+ fs::remove_all(CONTAINER2PATH, ec);
+ BOOST_REQUIRE(fs::create_directories(CONTAINER1PATH, ec));
+ BOOST_REQUIRE(fs::create_directories(CONTAINER2PATH, ec));
+ BOOST_REQUIRE(utils::saveFileContent(CONTAINER1PATH + "/file", FILE_CONTENT));
+
+ BOOST_CHECK_EQUAL(dbuses.at(1)->callMethodMove(CONTAINER2, TMP + "/file"),
+ api::FILE_MOVE_SUCCEEDED);
+ BOOST_CHECK(notificationLatch.wait(EVENT_TIMEOUT));
+ BOOST_CHECK(notificationLatch.empty());
+ BOOST_CHECK_EQUAL(notificationSource, CONTAINER1);
+ BOOST_CHECK_EQUAL(notificationPath, TMP + "/file");
+ BOOST_CHECK_EQUAL(notificationRetcode, api::FILE_MOVE_SUCCEEDED);
+ BOOST_CHECK(!fs::exists(CONTAINER1PATH + "/file"));
+ BOOST_CHECK_EQUAL(utils::readFileContent(CONTAINER2PATH + "/file"), FILE_CONTENT);
+
+ fs::remove_all(CONTAINER1PATH, ec);
+ fs::remove_all(CONTAINER2PATH, ec);
+}
+
+
BOOST_AUTO_TEST_SUITE_END()
#include "utils/exception.hpp"
#include <memory>
+#include <sys/mount.h>
+#include <boost/filesystem.hpp>
BOOST_AUTO_TEST_SUITE(UtilsFSSuite)
using namespace security_containers;
using namespace security_containers::utils;
+namespace {
+
const std::string FILE_PATH = SC_TEST_CONFIG_INSTALL_DIR "/utils/ut-fs/file.txt";
const std::string FILE_CONTENT = "File content\n"
"Line 1\n"
"Line 2\n";
const std::string BUGGY_FILE_PATH = "/some/missing/file/path/file.txt";
+const std::string TMP_PATH = "/tmp";
+const std::string FILE_PATH_RANDOM =
+ boost::filesystem::unique_path("/tmp/testFile-%%%%").string();
+const std::string MOUNT_POINT_RANDOM_1 =
+ boost::filesystem::unique_path("/tmp/mountPoint-%%%%").string();
+const std::string MOUNT_POINT_RANDOM_2 =
+ boost::filesystem::unique_path("/tmp/mountPoint-%%%%").string();
+const std::string FILE_NAME_RANDOM_1 =
+ boost::filesystem::unique_path("testFile-%%%%").string();
+const std::string FILE_NAME_RANDOM_2 =
+ boost::filesystem::unique_path("testFile-%%%%").string();
+
+} // namespace
BOOST_AUTO_TEST_CASE(ReadFileContentTest)
{
BOOST_CHECK_THROW(readFileContent(BUGGY_FILE_PATH), UtilsException);
}
+BOOST_AUTO_TEST_CASE(SaveFileContentTest)
+{
+ BOOST_REQUIRE(saveFileContent(FILE_PATH_RANDOM, FILE_CONTENT));
+ BOOST_CHECK_EQUAL(FILE_CONTENT, readFileContent(FILE_PATH));
+
+ boost::system::error_code ec;
+ boost::filesystem::remove(FILE_PATH_RANDOM, ec);
+}
+
+BOOST_AUTO_TEST_CASE(MountPointTest)
+{
+ bool result;
+ namespace fs = boost::filesystem;
+ boost::system::error_code ec;
+
+ BOOST_REQUIRE(fs::create_directory(MOUNT_POINT_RANDOM_1, ec));
+ BOOST_REQUIRE(isMountPoint(MOUNT_POINT_RANDOM_1, result));
+ BOOST_CHECK_EQUAL(result, false);
+ BOOST_REQUIRE(hasSameMountPoint(TMP_PATH, MOUNT_POINT_RANDOM_1, result));
+ BOOST_CHECK_EQUAL(result, true);
+
+ BOOST_REQUIRE(mountRun(MOUNT_POINT_RANDOM_1));
+ BOOST_REQUIRE(isMountPoint(MOUNT_POINT_RANDOM_1, result));
+ BOOST_CHECK_EQUAL(result, true);
+ BOOST_REQUIRE(hasSameMountPoint(TMP_PATH, MOUNT_POINT_RANDOM_1, result));
+ BOOST_CHECK_EQUAL(result, false);
+
+ BOOST_REQUIRE(umount(MOUNT_POINT_RANDOM_1));
+ BOOST_REQUIRE(fs::remove(MOUNT_POINT_RANDOM_1, ec));
+}
+
+BOOST_AUTO_TEST_CASE(MoveFileTest)
+{
+ namespace fs = boost::filesystem;
+ boost::system::error_code ec;
+ std::string src, dst;
+
+ // same mount point
+ src = TMP_PATH + "/" + FILE_NAME_RANDOM_1;
+ dst = TMP_PATH + "/" + FILE_NAME_RANDOM_2;
+
+ BOOST_REQUIRE(saveFileContent(src, FILE_CONTENT));
+
+ BOOST_CHECK(moveFile(src, dst));
+ BOOST_CHECK(!fs::exists(src));
+ BOOST_CHECK_EQUAL(readFileContent(dst), FILE_CONTENT);
+
+ BOOST_REQUIRE(fs::remove(dst));
+
+ // different mount point
+ src = TMP_PATH + "/" + FILE_NAME_RANDOM_1;
+ dst = MOUNT_POINT_RANDOM_2 + "/" + FILE_NAME_RANDOM_2;
+
+ BOOST_REQUIRE(fs::create_directory(MOUNT_POINT_RANDOM_2, ec));
+ BOOST_REQUIRE(mountRun(MOUNT_POINT_RANDOM_2));
+ BOOST_REQUIRE(saveFileContent(src, FILE_CONTENT));
+
+ BOOST_CHECK(moveFile(src, dst));
+ BOOST_CHECK(!fs::exists(src));
+ BOOST_CHECK_EQUAL(readFileContent(dst), FILE_CONTENT);
+
+ BOOST_REQUIRE(fs::remove(dst));
+ BOOST_REQUIRE(umount(MOUNT_POINT_RANDOM_2));
+ BOOST_REQUIRE(fs::remove(MOUNT_POINT_RANDOM_2, ec));
+}
+
BOOST_AUTO_TEST_SUITE_END()