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 "host-dbus-definitions.hpp"
28 #include "common-dbus-definitions.hpp"
29 #include "container-dbus-definitions.hpp"
30 #include "containers-manager.hpp"
31 #include "container-admin.hpp"
32 #include "exception.hpp"
34 #include "utils/paths.hpp"
35 #include "logger/logger.hpp"
36 #include "config/manager.hpp"
37 #include "dbus/exception.hpp"
38 #include "utils/fs.hpp"
40 #include <boost/filesystem.hpp>
41 #include <boost/regex.hpp>
47 namespace security_containers {
52 bool regexMatchVector(const std::string& str, const std::vector<boost::regex>& v)
54 for (const boost::regex& toMatch: v) {
55 if (boost::regex_match(str, toMatch)) {
63 const std::string HOST_ID = "host";
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 mHostConnection.setGetContainerIdsCallback(bind(&ContainersManager::handleGetContainerIdsCall,
84 mHostConnection.setGetActiveContainerIdCallback(bind(&ContainersManager::handleGetActiveContainerIdCall,
87 mHostConnection.setSetActiveContainerCallback(bind(&ContainersManager::handleSetActiveContainerCall,
90 for (auto& containerConfig : mConfig.containerConfigs) {
91 std::string containerConfigPath;
93 if (containerConfig[0] == '/') {
94 containerConfigPath = containerConfig;
96 std::string baseConfigPath = utils::dirName(managerConfigPath);
97 containerConfigPath = utils::createFilePath(baseConfigPath, "/", containerConfig);
100 LOGD("Creating Container " << containerConfigPath);
101 std::unique_ptr<Container> c(new Container(containerConfigPath,
102 mConfig.runMountPointPrefix));
103 const std::string id = c->getId();
105 throw ContainerOperationException("Cannot use reserved container ID");
108 c->setNotifyActiveContainerCallback(bind(&ContainersManager::notifyActiveContainerHandler,
111 c->setDisplayOffCallback(bind(&ContainersManager::displayOffHandler,
114 c->setFileMoveRequestCallback(std::bind(&ContainersManager::handleContainerMoveFileRequest,
115 this, id, _1, _2, _3));
117 c->setProxyCallCallback(bind(&ContainersManager::handleProxyCall,
118 this, id, _1, _2, _3, _4, _5, _6, _7));
120 c->setDbusStateChangedCallback(bind(&ContainersManager::handleDbusStateChanged,
123 mContainers.insert(ContainerMap::value_type(id, std::move(c)));
126 // check if default container exists, throw ContainerOperationException if not found
127 if (mContainers.find(mConfig.defaultId) == mContainers.end()) {
128 LOGE("Provided default container ID " << mConfig.defaultId << " is invalid.");
129 throw ContainerOperationException("Provided default container ID " + mConfig.defaultId +
133 LOGD("ContainersManager object instantiated");
135 if (mConfig.inputConfig.enabled) {
136 LOGI("Registering input monitor [" << mConfig.inputConfig.device.c_str() << "]");
137 mSwitchingSequenceMonitor.reset(
138 new InputMonitor(mConfig.inputConfig,
139 std::bind(&ContainersManager::switchingSequenceMonitorNotify,
144 ContainersManager::~ContainersManager()
146 LOGD("Destroying ContainersManager object...");
148 if (!mDetachOnExit) {
151 } catch (ServerException&) {
152 LOGE("Failed to stop all of the containers");
156 LOGD("ContainersManager object destroyed");
159 void ContainersManager::focus(const std::string& containerId)
161 /* try to access the object first to throw immediately if it doesn't exist */
162 ContainerMap::mapped_type& foregroundContainer = mContainers.at(containerId);
164 for (auto& container : mContainers) {
165 LOGD(container.second->getId() << ": being sent to background");
166 container.second->goBackground();
168 mConfig.foregroundId = foregroundContainer->getId();
169 LOGD(mConfig.foregroundId << ": being sent to foreground");
170 foregroundContainer->goForeground();
173 void ContainersManager::startAll()
175 LOGI("Starting all containers");
177 bool isForegroundFound = false;
179 for (auto& container : mContainers) {
180 container.second->start();
182 if (container.first == mConfig.foregroundId) {
183 isForegroundFound = true;
184 LOGI(container.second->getId() << ": set as the foreground container");
185 container.second->goForeground();
189 if (!isForegroundFound) {
190 auto foregroundIterator = std::min_element(mContainers.begin(), mContainers.end(),
191 [](ContainerMap::value_type &c1, ContainerMap::value_type &c2) {
192 return c1.second->getPrivilege() < c2.second->getPrivilege();
195 mConfig.foregroundId = foregroundIterator->second->getId();
196 LOGI(mConfig.foregroundId << ": no foreground container configured, setting one with highest priority");
197 foregroundIterator->second->goForeground();
201 void ContainersManager::stopAll()
203 LOGI("Stopping all containers");
205 for (auto& container : mContainers) {
206 container.second->stop();
210 std::string ContainersManager::getRunningForegroundContainerId()
212 for (auto& container : mContainers) {
213 if (container.first == mConfig.foregroundId &&
214 container.second->isRunning()) {
215 return container.first;
218 return std::string();
221 void ContainersManager::switchingSequenceMonitorNotify()
223 LOGI("switchingSequenceMonitorNotify() called");
228 void ContainersManager::setContainersDetachOnExit()
230 mDetachOnExit = true;
232 for (auto& container : mContainers) {
233 container.second->setDetachOnExit();
237 void ContainersManager::notifyActiveContainerHandler(const std::string& caller,
238 const std::string& application,
239 const std::string& message)
241 LOGI("notifyActiveContainerHandler(" << caller << ", " << application << ", " << message
244 const std::string activeContainer = getRunningForegroundContainerId();
245 if (!activeContainer.empty() && caller != activeContainer) {
246 mContainers[activeContainer]->sendNotification(caller, application, message);
248 } catch(const SecurityContainersException&) {
249 LOGE("Notification from " << caller << " hasn't been sent");
253 void ContainersManager::displayOffHandler(const std::string& /*caller*/)
255 // get config of currently set container and switch if switchToDefaultAfterTimeout is true
256 const std::string activeContainerName = getRunningForegroundContainerId();
257 const auto& activeContainer = mContainers.find(activeContainerName);
259 if (activeContainer != mContainers.end() &&
260 activeContainer->second->isSwitchToDefaultAfterTimeoutAllowed()) {
261 LOGI("Switching to default container " << mConfig.defaultId);
262 focus(mConfig.defaultId);
266 void ContainersManager::handleContainerMoveFileRequest(const std::string& srcContainerId,
267 const std::string& dstContainerId,
268 const std::string& path,
269 dbus::MethodResultBuilder::Pointer result)
271 // TODO: this implementation is only a placeholder.
272 // There are too many unanswered questions and security concerns:
273 // 1. What about mount namespace, host might not see the source/destination
274 // file. The file might be a different file from a host perspective.
275 // 2. Copy vs move (speed and security concerns over already opened FDs)
276 // 3. Access to source and destination files - DAC, uid/gig
277 // 4. Access to source and destintation files - MAC, smack
278 // 5. Destination file uid/gid assignment
279 // 6. Destination file smack label assignment
280 // 7. Verifiability of the source path
282 // NOTE: other possible implementations include:
283 // 1. Sending file descriptors opened directly in each container through DBUS
284 // using something like g_dbus_message_set_unix_fd_list()
285 // 2. SCS forking and calling setns(MNT) in each container and opening files
286 // by itself, then passing FDs to the main process
287 // Now when the main process has obtained FDs (by either of those methods)
288 // it can do the copying by itself.
290 LOGI("File move requested\n"
291 << "src: " << srcContainerId << "\n"
292 << "dst: " << dstContainerId << "\n"
293 << "path: " << path);
295 ContainerMap::const_iterator srcIter = mContainers.find(srcContainerId);
296 if (srcIter == mContainers.end()) {
297 LOGE("Source container '" << srcContainerId << "' not found");
300 Container& srcContainer = *srcIter->second;
302 ContainerMap::const_iterator dstIter = mContainers.find(dstContainerId);
303 if (dstIter == mContainers.end()) {
304 LOGE("Destination container '" << dstContainerId << "' not found");
305 result->set(g_variant_new("(s)", api::container::FILE_MOVE_DESTINATION_NOT_FOUND.c_str()));
308 Container& dstContanier = *dstIter->second;
310 if (srcContainerId == dstContainerId) {
311 LOGE("Cannot send a file to yourself");
312 result->set(g_variant_new("(s)", api::container::FILE_MOVE_WRONG_DESTINATION.c_str()));
316 if (!regexMatchVector(path, srcContainer.getPermittedToSend())) {
317 LOGE("Source container has no permissions to send the file: " << path);
318 result->set(g_variant_new("(s)", api::container::FILE_MOVE_NO_PERMISSIONS_SEND.c_str()));
322 if (!regexMatchVector(path, dstContanier.getPermittedToRecv())) {
323 LOGE("Destination container has no permissions to receive the file: " << path);
324 result->set(g_variant_new("(s)", api::container::FILE_MOVE_NO_PERMISSIONS_RECEIVE.c_str()));
328 namespace fs = boost::filesystem;
329 std::string srcPath = fs::absolute(srcContainerId, mConfig.containersPath).string() + path;
330 std::string dstPath = fs::absolute(dstContainerId, mConfig.containersPath).string() + path;
332 if (!utils::moveFile(srcPath, dstPath)) {
333 LOGE("Failed to move the file: " << path);
334 result->set(g_variant_new("(s)", api::container::FILE_MOVE_FAILED.c_str()));
336 result->set(g_variant_new("(s)", api::container::FILE_MOVE_SUCCEEDED.c_str()));
338 dstContanier.sendNotification(srcContainerId, path, api::container::FILE_MOVE_SUCCEEDED);
339 } catch (ServerException&) {
340 LOGE("Notification to '" << dstContainerId << "' has not been sent");
345 void ContainersManager::handleProxyCall(const std::string& caller,
346 const std::string& target,
347 const std::string& targetBusName,
348 const std::string& targetObjectPath,
349 const std::string& targetInterface,
350 const std::string& targetMethod,
351 GVariant* parameters,
352 dbus::MethodResultBuilder::Pointer result)
354 if (!mProxyCallPolicy->isProxyCallAllowed(caller,
360 LOGW("Forbidden proxy call; " << caller << " -> " << target << "; " << targetBusName
361 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
362 result->setError(api::ERROR_FORBIDDEN, "Proxy call forbidden");
366 LOGI("Proxy call; " << caller << " -> " << target << "; " << targetBusName
367 << "; " << targetObjectPath << "; " << targetInterface << "; " << targetMethod);
369 auto asyncResultCallback = [result](dbus::AsyncMethodCallResult& asyncMethodCallResult) {
371 GVariant* targetResult = asyncMethodCallResult.get();
372 result->set(g_variant_new("(v)", targetResult));
373 } catch (dbus::DbusException& e) {
374 result->setError(api::ERROR_FORWARDED, e.what());
378 if (target == HOST_ID) {
379 mHostConnection.proxyCallAsync(targetBusName,
384 asyncResultCallback);
388 ContainerMap::const_iterator targetIter = mContainers.find(target);
389 if (targetIter == mContainers.end()) {
390 LOGE("Target container '" << target << "' not found");
391 result->setError(api::ERROR_UNKNOWN_ID, "Unknown proxy call target");
395 Container& targetContainer = *targetIter->second;
396 targetContainer.proxyCallAsync(targetBusName,
401 asyncResultCallback);
404 void ContainersManager::handleGetContainerDbuses(dbus::MethodResultBuilder::Pointer result)
406 std::vector<GVariant*> entries;
407 for (auto& container : mContainers) {
408 GVariant* containerId = g_variant_new_string(container.first.c_str());
409 GVariant* dbusAddress = g_variant_new_string(container.second->getDbusAddress().c_str());
410 GVariant* entry = g_variant_new_dict_entry(containerId, dbusAddress);
411 entries.push_back(entry);
413 GVariant* dict = g_variant_new_array(G_VARIANT_TYPE("{ss}"), entries.data(), entries.size());
414 result->set(g_variant_new("(*)", dict));
417 void ContainersManager::handleDbusStateChanged(const std::string& containerId,
418 const std::string& dbusAddress)
420 mHostConnection.signalContainerDbusState(containerId, dbusAddress);
423 void ContainersManager::handleGetContainerIdsCall(dbus::MethodResultBuilder::Pointer result)
425 std::vector<GVariant*> containerIds;
426 for(auto& container: mContainers){
427 containerIds.push_back(g_variant_new_string(container.first.c_str()));
430 GVariant* array = g_variant_new_array(G_VARIANT_TYPE("s"),
432 containerIds.size());
433 result->set(g_variant_new("(*)", array));
436 void ContainersManager::handleGetActiveContainerIdCall(dbus::MethodResultBuilder::Pointer result)
438 LOGI("GetActiveContainerId call");
439 if (mContainers[mConfig.foregroundId]->isRunning()){
440 result->set(g_variant_new("(s)", mConfig.foregroundId.c_str()));
442 result->set(g_variant_new("(s)", ""));
446 void ContainersManager::handleSetActiveContainerCall(const std::string& id,
447 dbus::MethodResultBuilder::Pointer result)
449 LOGI("SetActiveContainer call; Id=" << id );
450 auto container = mContainers.find(id);
451 if (container == mContainers.end()){
452 LOGE("No container with id=" << id );
453 result->setError(api::ERROR_UNKNOWN_ID, "No such container id");
457 if (container->second->isStopped()){
458 LOGE("Could not activate a stopped container");
459 result->setError(api::host::ERROR_CONTAINER_STOPPED,
460 "Could not activate a stopped container");
468 } // namespace security_containers