This commit adds worker that will be able to manage with mount namespace.
If mount namespace is not supported, security-manager will run without worker,
otherwise worker will be communicated with security-manager through IPC channel.
If app privilege status changes, worker will allow/deny access to filesystem directory
associated with this privilege.
Change-Id: I056cd752c228335c7b67a607bddc0934c7a79ddd
${COMMON_PATH}/permissible-set.cpp
${COMMON_PATH}/protocols.cpp
${COMMON_PATH}/message-buffer.cpp
+ ${COMMON_PATH}/nsmount-logic.cpp
${COMMON_PATH}/privilege_db.cpp
${COMMON_PATH}/smack-labels.cpp
${COMMON_PATH}/smack-rules.cpp
${COMMON_PATH}/smack-check.cpp
${COMMON_PATH}/service_impl.cpp
${COMMON_PATH}/tzplatform-config.cpp
+ ${COMMON_PATH}/worker.cpp
${COMMON_PATH}/privilege-info.cpp
${COMMON_PATH}/privilege-gids.cpp
${COMMON_PATH}/group2gid.cpp
public:
DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base)
DECLARE_EXCEPTION_TYPE(Base, FileError)
+ DECLARE_EXCEPTION_TYPE(Base, InvalidArgument)
};
} // namespace FS
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ *
+ * Security Manager NSS library
+ */
+/*
+ * @file nsmount-logic.h
+ * @author Bartlomiej Grzelewski <b.grzelewski@samsung.com>
+ * @version 1.0
+ * @brief
+ */
+
+
+#pragma once
+
+#include <cynara.h>
+#include <channel.h>
+
+namespace SecurityManager {
+
+class NSMountLogic {
+public:
+ NSMountLogic(Cynara &cynara, Channel &channel)
+ : m_cynara(cynara)
+ , m_channel(channel)
+ {}
+
+ struct Entry : public ISerializable {
+ typedef std::string Privilege;
+ typedef std::pair<Privilege,bool> PrivilegeStatus;
+ typedef std::vector<std::pair<Privilege, bool>> PrivilegeStatusVector;
+
+ Entry() {}
+
+ Entry(uid_t uid, std::string appName)
+ : uid(uid)
+ , appName(std::move(appName))
+ {}
+
+ explicit Entry(IStream &stream) {
+ Deserialization::Deserialize(stream, uid, appName, smackLabel, privilegeStatusVector);
+ }
+
+ virtual void Serialize(IStream &stream) const {
+ Serialization::Serialize(stream, uid, appName, smackLabel, privilegeStatusVector);
+ }
+
+ uid_t uid = 0;
+ std::string appName;
+ std::string smackLabel;
+ PrivilegeStatusVector privilegeStatusVector;
+ };
+
+ typedef std::vector<Entry> EntryVector;
+
+ bool check(std::function<void(Entry&)> cynaraFilter);
+
+ virtual ~NSMountLogic();
+protected:
+ void readFiles(void);
+ void cynaraCheck(void);
+ bool sendJobs();
+
+ EntryVector m_entryVector;
+ Cynara &m_cynara;
+ Channel &m_channel;
+};
+
+} // namespace SecurityManager
#include <vector>
+#include "channel.h"
#include "credentials.h"
#include "cynara.h"
#include "security-manager.h"
*/
int appBindNamespace(const Credentials &creds, const std::string &appName);
+ /**
+ * Register channel for communication with worker process.
+ *
+ * @param[in] channel
+ *
+ */
+ void RegisterChannel(Channel channel) { m_channel = std::move(channel); }
+
private:
bool authenticate(const Credentials &creds, const std::string &privilege);
PrivilegeDb m_privilegeDb;
CynaraAdmin m_cynaraAdmin;
PrivilegeGids m_privilegeGids;
+ Channel m_channel;
};
} /* namespace SecurityManager */
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/*
+ * @file worker.h
+ * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version 1.0
+ * @brief
+ */
+#pragma once
+
+#include <string>
+
+#include <channel.h>
+#include <nsmount-logic.h>
+
+namespace SecurityManager {
+
+class Worker {
+public:
+ typedef NSMountLogic::Entry Entry;
+
+ Worker(Channel channel);
+ int doWork(const NSMountLogic::EntryVector &entryVector);
+
+ void mainLoop();
+
+protected:
+ Channel m_channel;
+};
+
+} // namespace SecurityManager
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/*
+ * @file nsmount-logic.cpp
+ * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version 1.0
+ * @brief Logic for modifying up existing mount namespace.
+ */
+#include <string>
+
+#include <filesystem.h>
+#include <protocols.h>
+#include <message-buffer.h>
+#include <mount-namespace.h>
+#include <dpl/serialization.h>
+
+#include <nsmount-logic.h>
+
+namespace {
+
+auto notNumber = [](const std::string &input)
+{
+ try {
+ stoi(input);
+ } catch (const std::invalid_argument&) {
+ return true;
+ }
+ return false;
+};
+
+auto toNumber = [](const std::string &input)
+{
+ uid_t uid;
+ try {
+ uid = static_cast<uid_t>(stoi(input));
+ } catch (const std::invalid_argument &) {
+ ThrowMsg(FS::Exception::InvalidArgument, "Cound not translate " << input << " to uid_t");
+ } catch (const std::out_of_range &e) {
+ ThrowMsg(FS::Exception::InvalidArgument, "Cound not translate " << input << " to uid_t");
+ }
+ return uid;
+};
+
+} // namespace anonymous
+
+namespace SecurityManager {
+
+void NSMountLogic::readFiles(void)
+{
+ auto users = FS::getSubDirectoriesFromDirectory(MountNS::getUsersAppsMountPointsPath(), true);
+ auto last = std::remove_if(users.begin(), users.end(), notNumber);
+ users.erase(last, users.end());
+
+ for (auto &user : users) {
+ auto apps = FS::getFilesFromDirectory(MountNS::getUserAppsMountPointsPath(toNumber(user)));
+ for (auto &app : apps) {
+ m_entryVector.emplace_back(toNumber(user), app);
+ }
+ }
+}
+
+void NSMountLogic::cynaraCheck(void)
+{
+ for (auto &entry : m_entryVector) {
+ if (entry.smackLabel.empty())
+ continue;
+
+ auto storagePrivilegePathMap = MountNS::getPrivilegePathMap(entry.uid);
+
+ for (auto &p : storagePrivilegePathMap) {
+ entry.privilegeStatusVector.emplace_back(
+ std::make_pair(p.first, m_cynara.check(entry.smackLabel, p.first, std::to_string(entry.uid), std::string())));
+ }
+ }
+}
+
+bool NSMountLogic::sendJobs(void)
+{
+ int status;
+ MessageBuffer send, recv;
+ Serialization::Serialize(send, m_entryVector);
+ if (!m_channel.write(send)) {
+ LogError("Could not send data to worker!");
+ return false;
+ }
+ if (!m_channel.read(recv)) {
+ LogError("Could not recv data from worker!");
+ return false;
+ }
+ Deserialization::Deserialize(recv, status);
+ return status == 0;
+}
+
+bool NSMountLogic::check(std::function<void(Entry&)> cynaraFilter)
+{
+ try {
+ readFiles();
+
+ for (auto &entry : m_entryVector)
+ cynaraFilter(entry);
+
+ cynaraCheck();
+ return sendJobs();
+ } catch (const MessageBuffer::Exception::Base &e) {
+ LogError("Worker connection failed: " << e.DumpToString());
+ } catch (const FS::Exception::Base &e) {
+ LogError("Filesystem exception: " << e.DumpToString());
+ }
+ return false;
+}
+
+NSMountLogic::~NSMountLogic()
+{}
+
+} // namespace SecurityManager
#include <sys/smack.h>
#include <config.h>
+#include <nsmount-logic.h>
#include "protocols.h"
#include "privilege_db.h"
#include "cynara.h"
// Apply updates
m_cynaraAdmin.setPolicies(validatedPolicies);
+ if (MountNS::isMountNamespaceEnabled()) {
+ NSMountLogic mntLogic(m_cynara, m_channel);
+ mntLogic.check(
+ [&](NSMountLogic::Entry &e){ e.smackLabel = getAppProcessLabel(e.appName); });
+ }
+
} catch (const CynaraException::Base &e) {
LogError("Error while updating Cynara rules: " << e.DumpToString());
return SECURITY_MANAGER_ERROR_SERVER_ERROR;
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Rafal Krypa <r.krypa@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+/*
+ * @file worker.cpp
+ * @author Bartlomiej Grzelewski (b.grzelewski@samsung.com)
+ * @version 1.0
+ * @brief
+ */
+
+#include <string>
+
+#include <dpl/serialization.h>
+#include <channel.h>
+#include <filesystem.h>
+#include <mount-namespace.h>
+#include <protocols.h>
+#include <worker.h>
+
+namespace SecurityManager {
+
+Worker::Worker(Channel channel)
+ : m_channel(std::move(channel))
+{}
+
+int Worker::doWork(const NSMountLogic::EntryVector &entries)
+{
+ int status = 0;
+ bool inGlobalNamespace = false;
+
+ for (auto &entry : entries) {
+ try {
+ // in most cases entry.uid will not change between iterations
+ auto storagePrivilegePathMap = MountNS::getPrivilegePathMap(entry.uid);
+
+ if (!inGlobalNamespace && MountNS::enterMountNamespace(MountNS::MAIN_MOUNT_NAMESPACE)) {
+ inGlobalNamespace = true;
+ }
+
+ if (!inGlobalNamespace) {
+ LogError("Error entering global mount namespace. Environment of application: "
+ << entry.appName << " will not be setup correctly.");
+ continue;
+ }
+
+ if (MountNS::enterMountNamespace(MountNS::getUserAppMountPointPath(entry.uid, entry.appName))) {
+ inGlobalNamespace = false;
+ } else {
+ continue;
+ }
+
+ for (auto &privStatus : entry.privilegeStatusVector) {
+ auto &privName = privStatus.first;
+ auto &result = privStatus.second;
+ auto mapIter = storagePrivilegePathMap.find(privName);
+
+ if (mapIter == storagePrivilegePathMap.end())
+ continue;
+
+ MountNS::PathVector pathVector = mapIter->second;
+ for (auto &path : pathVector) {
+ bool allowed = !MountNS::isPathBound(MountNS::ACCESS_DENIED_DIR_PATH, path);
+
+ if (allowed == result)
+ continue;
+
+ if (result)
+ MountNS::uMount(path); // allow access to path
+ else
+ MountNS::bindMount(MountNS::ACCESS_DENIED_DIR_PATH, path); // forbid access
+ }
+ }
+ } catch (...) {
+ status = -1;
+ LogError("Could not set up access to path for application: " << entry.appName);
+ }
+ }
+ return status;
+}
+
+void Worker::mainLoop()
+{
+ NSMountLogic::EntryVector entryVector;
+
+ do {
+ int status;
+ NSMountLogic::EntryVector entryVector;
+ MessageBuffer recv;
+ MessageBuffer send;
+
+ if (!m_channel.read(recv)) {
+ LogError("Error reading command socket. The Security-manager worker will exit");
+ break;
+ }
+
+ Deserialization::Deserialize(recv, entryVector);
+ status = doWork(entryVector);
+ Serialization::Serialize(send, status);
+ m_channel.write(send);
+ } while (true);
+}
+
+} // namespace SecurityManager
#include <iostream>
+#include <channel.h>
+#include <mount-namespace.h>
#include <socket-manager.h>
#include <file-lock.h>
#include <service.h>
+#include <worker.h>
-#define REGISTER_SOCKET_SERVICE(manager, service) \
- registerSocketService<service>(manager, #service)
+#define REGISTER_SOCKET_SERVICE(manager, service, channel) \
+ registerSocketService<service>(manager, #service, channel)
template<typename T>
-bool registerSocketService(SecurityManager::SocketManager &manager,
- const std::string& serviceName)
+T* registerSocketService(SecurityManager::SocketManager &manager,
+ const std::string& serviceName,
+ Channel channel)
{
T *service = NULL;
try {
service = new T();
+ service->RegisterChannel(std::move(channel));
service->Start();
manager.RegisterSocketService(service);
- return true;
+ return service;
} catch (const SecurityManager::Exception &exception) {
LogError("Error in creating service " << serviceName <<
", details:\n" << exception.DumpToString());
service->Stop();
delete service;
}
- return false;
+ return nullptr;
+}
+
+int worker(Channel channel)
+{
+ SecurityManager::Singleton<SecurityManager::Log::LogSystem>::Instance().SetTag("SECURITY_MANAGER_WORKER");
+ SecurityManager::Worker worker(std::move(channel));
+ worker.mainLoop();
+ return 0;
+}
+
+int security_manager(Channel channel)
+{
+ SecurityManager::FileLocker serviceLock(SecurityManager::SERVICE_LOCK_FILE, true);
+ LogInfo("Start!");
+ SecurityManager::SocketManager manager;
+
+ if (!REGISTER_SOCKET_SERVICE(manager, SecurityManager::Service, std::move(channel))) {
+ LogError("Unable to create socket service. Exiting.");
+ return EXIT_FAILURE;
+ }
+
+ manager.MainLoop();
+ return 0;
}
int main()
{
UNHANDLED_EXCEPTION_HANDLER_BEGIN
{
- // initialize logging
SecurityManager::Singleton<SecurityManager::Log::LogSystem>::Instance().SetTag("SECURITY_MANAGER");
- SecurityManager::FileLocker serviceLock(SecurityManager::SERVICE_LOCK_FILE, true);
-
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
sigaddset(&mask, SIGPIPE);
+ sigaddset(&mask, SIGCHLD);
+
if (-1 == pthread_sigmask(SIG_BLOCK, &mask, NULL)) {
LogError("Error in pthread_sigmask");
return EXIT_FAILURE;
}
- LogInfo("Start!");
- SecurityManager::SocketManager manager;
+ if (!MountNS::isMountNamespaceEnabled()) {
+ // Lagacy systems without mount namespace.
+ // In this case security-manager will run without WORKER
+ return security_manager(Channel());
+ }
- if (!REGISTER_SOCKET_SERVICE(manager, SecurityManager::Service))
- {
- LogError("Unable to create socket service. Exiting.");
+ // New systems with mount namespace.
+ // In this case security-manager requires WORKER that will
+ // be able to manage with mount namespaces.
+ ChannelCreator creator;
+
+ if (!creator.init()) {
+ LogError("Error creating channel");
return EXIT_FAILURE;
}
+ Channel child(creator.child());
+ Channel parent(creator.parent());
+
+ int ret = fork();
- manager.MainLoop();
+ if (-1 == ret) {
+ LogError("Error in fork");
+ return EXIT_FAILURE;
+ }
+
+ if (ret == 0) {
+ // child
+ parent.closeAll();
+ return worker(std::move(child));
+ } else {
+ // parent
+ child.closeAll();
+ return security_manager(std::move(parent));
+ }
} catch (const SecurityManager::FileLocker::Exception::LockFailed &e) {
LogError("Unable to get a file lock. Exiting.");
return EXIT_FAILURE;
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/smack.h>
+#include <sys/wait.h>
#include <linux/xattr.h>
#include <sys/un.h>
#include <sys/stat.h>
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD);
if (-1 == pthread_sigmask(SIG_BLOCK, &mask, NULL))
return -1;
return signalfd(-1, &mask, 0);
if (manager)
manager->MainLoopStop();
return;
+ } else if (siginfo->ssi_signo == SIGCHLD) {
+ int status;
+ waitpid(-1, &status, WNOHANG);
+ return;
}
LogInfo("This should not happend. Got signal: " << siginfo->ssi_signo);
#include "base-service.h"
#include "credentials.h"
#include "service_impl.h"
+#include <channel.h>
namespace SecurityManager {
public:
Service();
ServiceDescriptionVector GetServiceDescription();
-
+ void RegisterChannel(Channel channel) {
+ serviceImpl.RegisterChannel(std::move(channel));
+ }
private:
/**