2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Jan Olszak <j.olszak@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
21 * @author Jan Olszak (j.olszak@samsung.com)
22 * @brief Definition of the class for managing containers
27 #include "container-dbus-definitions.hpp"
28 #include "containers-manager.hpp"
29 #include "container-admin.hpp"
30 #include "exception.hpp"
32 #include "utils/paths.hpp"
33 #include "log/logger.hpp"
34 #include "config/manager.hpp"
35 #include "dbus/exception.hpp"
37 #include <boost/filesystem.hpp>
38 #include <boost/regex.hpp>
44 namespace security_containers {
49 bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v)
51 for (const boost::regex& toMatch: v) {
52 if (boost::regex_match(str, toMatch)) {
60 const std::string HOST_ID = "host";
61 const std::string DBUS_ERROR_NAME_FORBIDDEN = "org.tizen.containers.Error.Forbidden";
62 const std::string DBUS_ERROR_NAME_FORWARDED = "org.tizen.containers.Error.Forwarded";
63 const std::string DBUS_ERROR_NAME_UNKNOWN_TARGET = "org.tizen.containers.Error.UnknownTarget";
67 ContainersManager::ContainersManager(const std::string& managerConfigPath): mDetachOnExit(false)
69 LOGD("Instantiating ContainersManager object...");
70 config::loadFromFile(managerConfigPath, mConfig);
72 mProxyCallPolicy.reset(new ProxyCallPolicy(mConfig.proxyCallRules));
74 using namespace std::placeholders;
75 mHostConnection.setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
76 this, HOST_ID, _1, _2, _3, _4, _5, _6, _7));
78 mHostConnection.setGetContainerDbusesCallback(bind(
79 &ContainersManager::handleGetContainerDbuses, this, _1));
81 for (auto& containerConfig : mConfig.containerConfigs) {
82 std::string containerConfigPath;
84 if (containerConfig[0] == '/') {
85 containerConfigPath = containerConfig;
87 std::string baseConfigPath = utils::dirName(managerConfigPath);
88 containerConfigPath = utils::createFilePath(baseConfigPath, "/", containerConfig);
91 LOGD("Creating Container " << containerConfigPath);
92 std::unique_ptr<Container> c(new Container(containerConfigPath,
93 mConfig.runMountPointPrefix));
94 const std::string id = c->getId();
96 throw ContainerOperationException("Cannot use reserved container ID");
99 c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
102 c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
105 c->setFileMoveRequestCallback(std::bind(&ContainersManager::handleContainerMoveFileRequest,
106 this, id, _1, _2, _3));
108 c->setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
109 this, id, _1, _2, _3, _4, _5, _6, _7));
111 c->setDbusStateChangedCallback(bind(&ContainersManager::handleDbusStateChanged,
114 mContainers.insert(ContainerMap::value_type(id, std::move(c)));
117 // check if default container exists, throw ContainerOperationException if not found
118 if (mContainers.find(mConfig.defaultId) == mContainers.end()) {
119 LOGE("Provided default container ID " << mConfig.defaultId << " is invalid.");
120 throw ContainerOperationException("Provided default container ID " + mConfig.defaultId +
124 LOGD("ContainersManager object instantiated");
126 if (mConfig.inputConfig.enabled) {
127 LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]");
128 mSwitchingSequenceMonitor.reset(
129 new InputMonitor(mConfig.inputConfig,
130 std::bind(&ContainersManager::switchingSequenceMonitorNotify,
136 ContainersManager::~ContainersManager()
138 LOGD("Destroying ContainersManager object...");
140 if (!mDetachOnExit) {
143 } catch (ServerException&) {
144 LOGE("Failed to stop all of the containers");
148 LOGD("ContainersManager object destroyed");
152 void ContainersManager::focus(const std::string& containerId)
154 /* try to access the object first to throw immediately if it doesn't exist */
155 ContainerMap::mapped_type& foregroundContainer = mContainers.at(containerId);
157 for (auto& container : mContainers) {
158 LOGD(container.second->getId() << ": being sent to background");
159 container.second->goBackground();
161 mConfig.foregroundId = foregroundContainer->getId();
162 LOGD(mConfig.foregroundId << ": being sent to foreground");
163 foregroundContainer->goForeground();
167 void ContainersManager::startAll()
169 LOGI("Starting all containers");
171 bool isForegroundFound = false;
173 for (auto& container : mContainers) {
174 container.second->start();
176 if (container.first == mConfig.foregroundId) {
177 isForegroundFound = true;
178 LOGI(container.second->getId() << ": set as the foreground container");
179 container.second->goForeground();
183 if (!isForegroundFound) {
184 auto foregroundIterator = std::min_element(mContainers.begin(), mContainers.end(),
185 [](ContainerMap::value_type &c1, ContainerMap::value_type &c2) {
186 return c1.second->getPrivilege() < c2.second->getPrivilege();
189 mConfig.foregroundId = foregroundIterator->second->getId();
190 LOGI(mConfig.foregroundId << ": no foreground container configured, setting one with highest priority");
191 foregroundIterator->second->goForeground();
196 void ContainersManager::stopAll()
198 LOGI("Stopping all containers");
200 for (auto& container : mContainers) {
201 container.second->stop();
206 std::string ContainersManager::getRunningForegroundContainerId()
208 for (auto& container : mContainers) {
209 if (container.first == mConfig.foregroundId &&
210 container.second->isRunning()) {
211 return container.first;
214 return std::string();
217 void ContainersManager::switchingSequenceMonitorNotify()
219 LOGI("switchingSequenceMonitorNotify() called");
224 void ContainersManager::setContainersDetachOnExit()
226 mDetachOnExit = true;
228 for (auto& container : mContainers) {
229 container.second->setDetachOnExit();
233 void ContainersManager::notifyActiveContainerHandler(const std::string& caller,
234 const std::string& application,
235 const std::string& message)
237 LOGI("notifyActiveContainerHandler(" << caller << ", " << application << ", " << message
240 const std::string activeContainer = getRunningForegroundContainerId();
241 if (!activeContainer.empty() && caller != activeContainer) {
242 mContainers[activeContainer]->sendNotification(caller, application, message);
244 } catch(const SecurityContainersException&) {
245 LOGE("Notification from " << caller << " hasn't been sent");
249 void ContainersManager::displayOffHandler(const std::string& /*caller*/)
251 // get config of currently set container and switch if switchToDefaultAfterTimeout is true
252 const std::string activeContainerName = getRunningForegroundContainerId();
253 const auto& activeContainer = mContainers.find(activeContainerName);
255 if (activeContainer != mContainers.end() &&
256 activeContainer->second->isSwitchToDefaultAfterTimeoutAllowed()) {
257 LOGI("Switching to default container " << mConfig.defaultId);
258 focus(mConfig.defaultId);
262 void ContainersManager::handleContainerMoveFileRequest(const std::string& srcContainerId,
263 const std::string& dstContainerId,
264 const std::string& path,
265 dbus::MethodResultBuilder::Pointer result)
267 // TODO: this implementation is only a placeholder.
268 // There are too many unanswered questions and security concerns:
269 // 1. What about mount namespace, host might not see the source/destination
270 // file. The file might be a different file from a host perspective.
271 // 2. Copy vs move (speed and security concerns over already opened FDs)
272 // 3. Access to source and destination files - DAC, uid/gig
273 // 4. Access to source and destintation files - MAC, smack
274 // 5. Destination file uid/gid assignment
275 // 6. Destination file smack label assignment
276 // 7. Verifiability of the source path
278 // NOTE: other possible implementations include:
279 // 1. Sending file descriptors opened directly in each container through DBUS
280 // using something like g_dbus_message_set_unix_fd_list()
281 // 2. SCS forking and calling setns(MNT) in each container and opening files
282 // by itself, then passing FDs to the main process
283 // Now when the main process has obtained FDs (by either of those methods)
284 // it can do the copying by itself.
286 LOGI("File move requested\n"
287 << "src: " << srcContainerId << "\n"
288 << "dst: " << dstContainerId << "\n"
289 << "path: " << path);
291 ContainerMap::const_iterator srcIter = mContainers.find(srcContainerId);
292 if (srcIter == mContainers.end()) {
293 LOGE("Source container '" << srcContainerId << "' not found");
296 Container& srcContainer = *srcIter->second;
298 ContainerMap::const_iterator dstIter = mContainers.find(dstContainerId);
299 if (dstIter == mContainers.end()) {
300 LOGE("Destination container '" << dstContainerId << "' not found");
301 result->set(g_variant_new("(s)", api::FILE_MOVE_DESTINATION_NOT_FOUND.c_str()));
304 Container& dstContanier = *dstIter->second;
306 if (srcContainerId == dstContainerId) {
307 LOGE("Cannot send a file to yourself");
308 result->set(g_variant_new("(s)", api::FILE_MOVE_WRONG_DESTINATION.c_str()));
312 if (!regexMatchVector(path, srcContainer.getPermittedToSend())) {
313 LOGE("Source container has no permissions to send the file: " << path);
314 result->set(g_variant_new("(s)", api::FILE_MOVE_NO_PERMISSIONS_SEND.c_str()));
318 if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
319 LOGE("Destination container has no permissions to receive the file: " << path);
320 result->set(g_variant_new("(s)", api::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str()));
324 namespace fs = boost::filesystem;
325 std::string srcPath = fs::absolute(srcContainerId, mConfig.containersPath).string() + path;
326 std::string dstPath = fs::absolute(dstContainerId, mConfig.containersPath).string() + path;
328 if (!utils::moveFile(srcPath, dstPath)) {
329 LOGE("Failed to move the file: " << path);
330 result->set(g_variant_new("(s)", api::FILE_MOVE_FAILED.c_str()));
332 result->set(g_variant_new("(s)", api::FILE_MOVE_SUCCEEDED.c_str()));
334 dstContanier.sendNotification(srcContainerId, path, api::FILE_MOVE_SUCCEEDED);
335 } catch (ServerException&) {
336 LOGE("Notification to '" << dstContainerId << "' has not been sent");
341 void ContainersManager::handleProxyCall(const std::string& caller,
342 const std::string& target,
343 const std::string& targetBusName,
344 const std::string& targetObjectPath,
345 const std::string& targetInterface,
346 const std::string& targetMethod,
347 GVariant* parameters,
348 dbus::MethodResultBuilder::Pointer result)
350 if (!mProxyCallPolicy->isProxyCallAllowed(caller,
356 LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
357 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
358 result->setError(DBUS_ERROR_NAME_FORBIDDEN, "Proxy call forbidden");
362 LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
363 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
365 auto asyncResultCallback = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) {
367 GVariant* targetResult = asyncMethodCallResult.get();
368 result->set(g_variant_new("(v)", targetResult));
369 } catch (dbus::DbusException& e) {
370 result->setError(DBUS_ERROR_NAME_FORWARDED, e.what());
374 if (target == HOST_ID) {
375 mHostConnection.proxyCallAsync(targetBusName,
380 asyncResultCallback);
384 ContainerMap::const_iterator targetIter = mContainers.find(target);
385 if (targetIter == mContainers.end()) {
386 LOGE("Target container '" << target << "' not found");
387 result->setError(DBUS_ERROR_NAME_UNKNOWN_TARGET, "Unknown proxy call target");
391 Container& targetContainer = *targetIter->second;
392 targetContainer.proxyCallAsync(targetBusName,
397 asyncResultCallback);
400 void ContainersManager::handleGetContainerDbuses(dbus::MethodResultBuilder::Pointer result)
402 std::vector<GVariant*> entries;
403 for (auto& container : mContainers) {
404 GVariant* containerId = g_variant_new_string(container.first.c_str());
405 GVariant* dbusAddress = g_variant_new_string(container.second->getDbusAddress().c_str());
406 GVariant* entry = g_variant_new_dict_entry(containerId, dbusAddress);
407 entries.push_back(entry);
409 GVariant* dict = g_variant_new_array(G_VARIANT_TYPE("{ss}"), entries.data(), entries.size());
410 result->set(g_variant_new("(*)", dict));
413 void ContainersManager::handleDbusStateChanged(const std::string& containerId,
414 const std::string& dbusAddress)
416 mHostConnection.signalContainerDbusState(containerId, dbusAddress);
420 } // namespace security_containers