Prepare security-manager for master-slave mode 73/30173/22
authorRafal Krypa <r.krypa@samsung.com>
Fri, 6 Feb 2015 17:25:11 +0000 (18:25 +0100)
committerRafal Krypa <r.krypa@samsung.com>
Tue, 31 Mar 2015 09:55:39 +0000 (11:55 +0200)
This commit prepares security-manager for work in master/slave mode.
In order to properly install/uninstall applications inside containers,
security-manager inside container (slave) must delegate calls related to
SMACK to security-manager outside a container (master).
Since entire master/slave mode is a huge change, it is divided into two
commits - this is the first one. Logic for master service and changes in
service to work as slave are left for second commit.

With this change security-manager launched without additional arguments should
work as it did.

Change-Id: If05cdeb2d2c35c046bf4cb46d884a3689dab57ad

18 files changed:
packaging/security-manager.spec
src/common/include/protocols.h
src/common/include/service_impl.h
src/common/protocols.cpp
src/common/service_impl.cpp
src/server/CMakeLists.txt
src/server/main/server-main.cpp
src/server/service/include/master-service.h [new file with mode: 0644]
src/server/service/include/service.h
src/server/service/master-service.cpp [new file with mode: 0644]
src/server/service/service.cpp
systemd/CMakeLists.txt
systemd/security-manager-master.service.in [new file with mode: 0644]
systemd/security-manager-master.socket [new file with mode: 0644]
systemd/security-manager-slave.service.in [new file with mode: 0644]
systemd/security-manager-slave.socket [new file with mode: 0644]
systemd/security-manager.service.in
systemd/security-manager.socket

index 5269334..d6d259b 100644 (file)
@@ -87,6 +87,8 @@ cp LICENSE %{buildroot}%{_datadir}/license/libsecurity-manager-client
 
 mkdir -p %{buildroot}/%{_unitdir}/sockets.target.wants
 ln -s ../security-manager.socket %{buildroot}/%{_unitdir}/sockets.target.wants/security-manager.socket
+ln -s ../security-manager-master.socket %{buildroot}/%{_unitdir}/sockets.target.wants/security-manager-master.socket
+ln -s ../security-manager-slave.socket %{buildroot}/%{_unitdir}/sockets.target.wants/security-manager-slave.socket
 
 %clean
 rm -rf %{buildroot}
@@ -136,7 +138,11 @@ fi
 
 %{_libdir}/libsecurity-manager-commons.so.*
 %attr(-,root,root) %{_unitdir}/security-manager.*
+%attr(-,root,root) %{_unitdir}/security-manager-master.*
+%attr(-,root,root) %{_unitdir}/security-manager-slave.*
 %attr(-,root,root) %{_unitdir}/sockets.target.wants/security-manager.*
+%attr(-,root,root) %{_unitdir}/sockets.target.wants/security-manager-master.*
+%attr(-,root,root) %{_unitdir}/sockets.target.wants/security-manager-slave.*
 %config(noreplace) %attr(0600,root,root) %{TZ_SYS_DB}/.security-manager.db
 %config(noreplace) %attr(0600,root,root) %{TZ_SYS_DB}/.security-manager.db-journal
 %{_datadir}/license/%{name}
index 9f3b11b..8360a26 100644 (file)
@@ -120,6 +120,8 @@ struct user_req {
 namespace SecurityManager {
 
 extern char const * const SERVICE_SOCKET;
+extern char const * const MASTER_SERVICE_SOCKET;
+extern char const * const SLAVE_SERVICE_SOCKET;
 
 enum class SecurityModuleCall
 {
index 827496a..fa999b6 100644 (file)
@@ -35,6 +35,17 @@ namespace SecurityManager {
 namespace ServiceImpl {
 
 /**
+ * Retrieves ID (UID and PID) of peer connected to socket
+ *
+ * @param[in]  Socket file descriptor
+ * @param[out] UID of connected peer. Function does not modify the variable if ID retrieval fails.
+ * @param[out] PID of connected peer. Function does not modify the variable if ID retrieval fails.
+ *
+ * @return True if peer ID was successfully retrieved, false otherwise.
+ */
+bool getPeerID(int sock, uid_t &uid, pid_t &pid);
+
+/**
  * Process application installation request.
  *
  * @param[in] req installation request
index 798b9d6..eac619a 100644 (file)
@@ -32,6 +32,10 @@ namespace SecurityManager {
 
 char const * const SERVICE_SOCKET =
         SOCKET_PATH_PREFIX "security-manager.socket";
+char const * const MASTER_SERVICE_SOCKET =
+        SOCKET_PATH_PREFIX "security-manager-master.socket";
+char const * const SLAVE_SERVICE_SOCKET =
+        SOCKET_PATH_PREFIX "security-manager-slave.socket";
 
 } // namespace SecurityManager
 
index 7fd621c..20ee7a9 100644 (file)
@@ -27,6 +27,7 @@
 #include <grp.h>
 #include <limits.h>
 #include <pwd.h>
+#include <sys/socket.h>
 
 #include <cstring>
 #include <algorithm>
@@ -159,6 +160,20 @@ static inline bool isSubDir(const char *parent, const char *subdir)
     return (*subdir == '/');
 }
 
+bool getPeerID(int sock, uid_t &uid, pid_t &pid)
+{
+    struct ucred cr;
+    socklen_t len = sizeof(cr);
+
+    if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &len)) {
+        uid = cr.uid;
+        pid = cr.pid;
+        return true;
+    }
+
+    return false;
+}
+
 static bool getUserAppDir(const uid_t &uid, std::string &userAppDir)
 {
     struct tzplatform_context *tz_ctx = nullptr;
index 753eb96..8890aa3 100644 (file)
@@ -3,7 +3,7 @@ PKG_CHECK_MODULES(SERVER_DEP
     libsystemd-daemon
     )
 
-FIND_PACKAGE(Boost REQUIRED)
+FIND_PACKAGE(Boost REQUIRED COMPONENTS program_options)
 FIND_PACKAGE(Threads REQUIRED)
 
 INCLUDE_DIRECTORIES(SYSTEM
@@ -27,6 +27,7 @@ SET(SERVER_SOURCES
     ${SERVER_PATH}/main/server-main.cpp
     ${SERVER_PATH}/service/base-service.cpp
     ${SERVER_PATH}/service/service.cpp
+    ${SERVER_PATH}/service/master-service.cpp
     )
 
 ADD_EXECUTABLE(${TARGET_SERVER} ${SERVER_SOURCES})
@@ -39,6 +40,7 @@ TARGET_LINK_LIBRARIES(${TARGET_SERVER}
     ${TARGET_COMMON}
     ${CMAKE_THREAD_LIBS_INIT}
     ${SERVER_DEP_LIBRARIES}
+    ${Boost_LIBRARIES}
     )
 
 INSTALL(TARGETS ${TARGET_SERVER} DESTINATION ${BIN_INSTALL_DIR})
index c9609e2..f8e1f71 100644 (file)
 #include <dpl/singleton.h>
 #include <dpl/singleton_safe_impl.h>
 
+#include <boost/program_options.hpp>
+#include <iostream>
+
 #include <socket-manager.h>
 #include <file-lock.h>
 
 #include <service.h>
+#include <master-service.h>
+
+namespace po = boost::program_options;
 
 IMPLEMENT_SAFE_SINGLETON(SecurityManager::Log::LogSystem);
 
-#define REGISTER_SOCKET_SERVICE(manager, service) \
-    registerSocketService<service>(manager, #service)
+#define REGISTER_SOCKET_SERVICE(manager, service, allocator) \
+    registerSocketService<service>(manager, #service, allocator)
 
 template<typename T>
-bool registerSocketService(SecurityManager::SocketManager &manager, const std::string& serviceName)
+bool registerSocketService(SecurityManager::SocketManager &manager,
+                           const std::string& serviceName,
+                           const std::function<T*(void)>& serviceAllocator)
 {
     T *service = NULL;
     try {
-        service = new T();
+        service = serviceAllocator();
         service->Create();
         manager.RegisterSocketService(service);
         return true;
@@ -62,12 +70,59 @@ bool registerSocketService(SecurityManager::SocketManager &manager, const std::s
     return false;
 }
 
-int main(void) {
-
+int main(int argc, char* argv[])
+{
     UNHANDLED_EXCEPTION_HANDLER_BEGIN
     {
+        // initialize logging
         SecurityManager::Singleton<SecurityManager::Log::LogSystem>::Instance().SetTag("SECURITY_MANAGER");
 
+        // parse arguments
+        bool masterMode = false, slaveMode = false;
+        po::options_description optDesc("Allowed options");
+
+        optDesc.add_options()
+        ("help,h", "Print this help message")
+        ("master,m", "Enable master mode")
+        ("slave,s", "Enable slave mode")
+        ;
+
+        po::variables_map vm;
+        po::basic_parsed_options<char> parsed =
+                po::command_line_parser(argc, argv).options(optDesc).allow_unregistered().run();
+
+        std::vector<std::string> unrecognizedOptions =
+             po::collect_unrecognized(parsed.options, po::include_positional);
+
+        if (!unrecognizedOptions.empty()) {
+            std::cerr << "Unrecognized options: ";
+
+            for (auto& uo : unrecognizedOptions) {
+                std::cerr << ' ' << uo;
+            }
+
+            std::cerr << std::endl << std::endl;
+            std::cerr << optDesc << std::endl;
+
+            return EXIT_FAILURE;
+        }
+
+        po::store(parsed, vm);
+        po::notify(vm);
+
+        if (vm.count("help")) {
+            std::cout << optDesc << std::endl;
+            return EXIT_SUCCESS;
+        }
+
+        masterMode = vm.count("master") > 0;
+        slaveMode = vm.count("slave") > 0;
+
+        if (masterMode && slaveMode) {
+            LogError("Cannot be both master and slave!");
+            return EXIT_FAILURE;
+        }
+
         SecurityManager::FileLocker serviceLock(SecurityManager::SERVICE_LOCK_FILE,
                                                 true);
 
@@ -77,15 +132,24 @@ int main(void) {
         sigaddset(&mask, SIGPIPE);
         if (-1 == pthread_sigmask(SIG_BLOCK, &mask, NULL)) {
             LogError("Error in pthread_sigmask");
-            return 1;
+            return EXIT_FAILURE;
         }
 
         LogInfo("Start!");
         SecurityManager::SocketManager manager;
 
-        if (!REGISTER_SOCKET_SERVICE(manager, SecurityManager::Service)) {
-            LogError("Unable to create socket service. Exiting.");
-            return EXIT_FAILURE;
+        if (masterMode) {
+            if (!REGISTER_SOCKET_SERVICE(manager, SecurityManager::MasterService,
+                    []() { return new SecurityManager::MasterService(); } )) {
+                LogError("Unable to create master socket service. Exiting.");
+                return EXIT_FAILURE;
+            }
+        } else {
+            if (!REGISTER_SOCKET_SERVICE(manager, SecurityManager::Service,
+                    [&slaveMode]() { return new SecurityManager::Service(slaveMode); } )) {
+                LogError("Unable to create socket service. Exiting.");
+                return EXIT_FAILURE;
+            }
         }
 
         manager.MainLoop();
@@ -94,5 +158,5 @@ int main(void) {
         return EXIT_FAILURE;
     }
     UNHANDLED_EXCEPTION_HANDLER_END
-    return 0;
+    return EXIT_SUCCESS;
 }
diff --git a/src/server/service/include/master-service.h b/src/server/service/include/master-service.h
new file mode 100644 (file)
index 0000000..170bfd9
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *  Copyright (c) 2000 - 2014 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        master-service.h
+ * @author      Lukasz Kostyra <l.kostyra@samsung.com>
+ * @author      Rafal Krypa <r.krypa@samsung.com>
+ * @brief       Implementation of security-manager master service
+ */
+
+#ifndef _SECURITY_MANAGER_MASTER_SERVICE_
+#define _SECURITY_MANAGER_MASTER_SERVICE_
+
+#include "base-service.h"
+
+namespace SecurityManager {
+
+class MasterServiceException
+{
+public:
+    DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base)
+    DECLARE_EXCEPTION_TYPE(Base, InvalidAction)
+};
+
+class MasterService :
+    public SecurityManager::BaseService
+{
+public:
+    MasterService();
+    ServiceDescriptionVector GetServiceDescription();
+
+private:
+
+    /**
+     * Handle request from a client
+     *
+     * @param  conn        Socket connection information
+     * @param  buffer      Raw received data buffer
+     * @param  interfaceID identifier used to distinguish source socket
+     * @return             true on success
+     */
+    bool processOne(const ConnectionID &conn, MessageBuffer &buffer, InterfaceID interfaceID);
+};
+
+} // namespace SecurityManager
+
+#endif // _SECURITY_MANAGER_MASTER_SERVICE_
index 883c989..765d0d0 100644 (file)
@@ -40,10 +40,12 @@ class Service :
     public SecurityManager::BaseService
 {
 public:
-    Service();
+    Service(const bool isSlave);
     ServiceDescriptionVector GetServiceDescription();
 
 private:
+    const bool m_isSlave;
+
     /**
      * Handle request from a client
      *
diff --git a/src/server/service/master-service.cpp b/src/server/service/master-service.cpp
new file mode 100644 (file)
index 0000000..2afae00
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ *  Copyright (c) 2000 - 2014 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        master-service.cpp
+ * @author      Lukasz Kostyra <l.kostyra@samsung.com>
+ * @author      Rafal Krypa <r.krypa@samsung.com>
+ * @brief       Implementation of security-manager master service.
+ */
+
+#include <generic-socket-manager.h>
+
+#include <dpl/log/log.h>
+#include <dpl/serialization.h>
+
+#include "protocols.h"
+#include "master-service.h"
+#include "service_impl.h"
+
+namespace SecurityManager {
+
+const InterfaceID IFACE = 1;
+
+MasterService::MasterService()
+{
+}
+
+GenericSocketService::ServiceDescriptionVector MasterService::GetServiceDescription()
+{
+    return ServiceDescriptionVector {
+        {MASTER_SERVICE_SOCKET, "security-manager-master", IFACE},
+    };
+}
+
+bool MasterService::processOne(const ConnectionID &conn, MessageBuffer &buffer,
+                                  InterfaceID interfaceID)
+{
+    LogDebug("Iteration begin. Interface = " << interfaceID);
+
+    //waiting for all data
+    if (!buffer.Ready()) {
+        return false;
+    }
+
+    MessageBuffer send;
+    bool retval = false;
+
+    uid_t uid;
+    pid_t pid;
+
+    if (!ServiceImpl::getPeerID(conn.sock, uid, pid)) {
+        LogError("Closing socket because of error: unable to get peer's uid and pid");
+        m_serviceManager->Close(conn);
+        return false;
+    }
+
+    if (IFACE == interfaceID) {
+        Try {
+            // deserialize API call type
+            int call_type_int;
+            Deserialization::Deserialize(buffer, call_type_int);
+            SecurityModuleCall call_type = static_cast<SecurityModuleCall>(call_type_int);
+
+            switch (call_type) {
+                default:
+                    LogError("Invalid call: " << call_type_int);
+                    Throw(MasterServiceException::InvalidAction);
+            }
+            // if we reach this point, the protocol is OK
+            retval = true;
+        } Catch (MessageBuffer::Exception::Base) {
+            LogError("Broken protocol.");
+        } Catch (MasterServiceException::Base) {
+            LogError("Broken protocol.");
+        } catch (const std::exception &e) {
+            LogError("STD exception " << e.what());
+        } catch (...) {
+            LogError("Unknown exception");
+        }
+    }
+    else {
+        LogError("Wrong interface");
+    }
+
+    if (retval) {
+        //send response
+        m_serviceManager->Write(conn, send.Pop());
+    } else {
+        LogError("Closing socket because of error");
+        m_serviceManager->Close(conn);
+    }
+
+    return retval;
+}
+
+
+} // namespace SecurityManager
index e0925b1..522356f 100644 (file)
@@ -37,19 +37,29 @@ namespace SecurityManager {
 
 const InterfaceID IFACE = 1;
 
-Service::Service()
+Service::Service(const bool isSlave):
+        m_isSlave(isSlave)
 {
 }
 
 GenericSocketService::ServiceDescriptionVector Service::GetServiceDescription()
 {
-    return ServiceDescriptionVector {
-        {SERVICE_SOCKET,  /* path */
-         "*",   /* smackLabel label (not used, we rely on systemd) */
-         IFACE, /* InterfaceID */
-         false, /* useSendMsg */
-         true}, /* systemdOnly */
-    };
+    if (m_isSlave)
+        return ServiceDescriptionVector {
+            {SLAVE_SERVICE_SOCKET,  /* path */
+             "*",   /* smackLabel label (not used, we rely on systemd) */
+             IFACE, /* InterfaceID */
+             false, /* useSendMsg */
+             true}, /* systemdOnly */
+        };
+    else
+        return ServiceDescriptionVector {
+            {SERVICE_SOCKET,  /* path */
+             "*",   /* smackLabel label (not used, we rely on systemd) */
+             IFACE, /* InterfaceID */
+             false, /* useSendMsg */
+             true}, /* systemdOnly */
+        };
 }
 
 static bool getPeerID(int sock, uid_t &uid, pid_t &pid, std::string &smackLabel)
index 90c0ec1..02814a5 100644 (file)
@@ -1,8 +1,14 @@
 CONFIGURE_FILE(security-manager.service.in security-manager.service @ONLY)
+CONFIGURE_FILE(security-manager-master.service.in security-manager-master.service @ONLY)
+CONFIGURE_FILE(security-manager-slave.service.in security-manager-slave.service @ONLY)
 
 INSTALL(FILES
     security-manager.service
     security-manager.socket
+    security-manager-master.service
+    security-manager-master.socket
+    security-manager-slave.service
+    security-manager-slave.socket
     DESTINATION
     ${SYSTEMD_INSTALL_DIR}
 )
diff --git a/systemd/security-manager-master.service.in b/systemd/security-manager-master.service.in
new file mode 100644 (file)
index 0000000..ef14a57
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=Start the security manager master
+ConditionVirtualization=!lxc
+ConditionPathExists=/usr/share/.zones/enabled
+
+[Service]
+Type=notify
+ExecStart=@BIN_INSTALL_DIR@/security-manager --master
+
+Sockets=security-manager-master.socket
diff --git a/systemd/security-manager-master.socket b/systemd/security-manager-master.socket
new file mode 100644 (file)
index 0000000..d41eae0
--- /dev/null
@@ -0,0 +1,13 @@
+[Socket]
+ListenStream=/run/security-manager-master.socket
+SocketMode=0700
+SmackLabelIPIn=System
+SmackLabelIPOut=System
+Service=security-manager-master.service
+
+[Unit]
+ConditionVirtualization=!lxc
+ConditionPathExists=/usr/share/.zones/enabled
+
+[Install]
+WantedBy=sockets.target
diff --git a/systemd/security-manager-slave.service.in b/systemd/security-manager-slave.service.in
new file mode 100644 (file)
index 0000000..6eb7505
--- /dev/null
@@ -0,0 +1,9 @@
+[Unit]
+Description=Start the security manager slave
+ConditionVirtualization=lxc
+
+[Service]
+Type=notify
+ExecStart=@BIN_INSTALL_DIR@/security-manager --slave
+
+Sockets=security-manager-slave.socket
diff --git a/systemd/security-manager-slave.socket b/systemd/security-manager-slave.socket
new file mode 100644 (file)
index 0000000..8d0a2c6
--- /dev/null
@@ -0,0 +1,10 @@
+[Socket]
+ListenStream=/run/security-manager-slave.socket
+Symlinks=/run/security-manager.socket
+SocketMode=0777
+SmackLabelIPIn=*
+SmackLabelIPOut=@
+Service=security-manager-slave.service
+
+[Unit]
+ConditionVirtualization=lxc
index 23fd1b2..f15ab90 100644 (file)
@@ -1,5 +1,7 @@
 [Unit]
 Description=Start the security manager
+ConditionVirtualization=!lxc
+ConditionPathExists=!/usr/share/.zones/enabled
 
 [Service]
 Type=notify
index af1c1da..95411e8 100644 (file)
@@ -7,5 +7,9 @@ SmackLabelIPOut=@
 # TODO: move to separate systemd service
 Service=security-manager.service
 
+[Unit]
+ConditionVirtualization=!lxc
+ConditionPathExists=!/usr/share/.zones/enabled
+
 [Install]
 WantedBy=sockets.target