Make database at run-time
authorSangwan Kwon <sangwan.kwon@samsung.com>
Thu, 17 Oct 2019 05:01:03 +0000 (14:01 +0900)
committer권상완/Security 2Lab(SR)/Engineer/삼성전자 <sangwan.kwon@samsung.com>
Mon, 21 Oct 2019 05:41:24 +0000 (14:41 +0900)
Now policy-provider is able to set the initial value at run-time

Signed-off-by: Sangwan Kwon <sangwan.kwon@samsung.com>
data/script/create_schema.sql [new file with mode: 0644]
packaging/osquery.spec
src/policyd/CMakeLists.txt
src/policyd/core/logger.h
src/policyd/core/policy-manager.cpp
src/policyd/core/policy-storage.cpp
src/policyd/core/policy-storage.h
src/policyd/core/tests/storage-tests.cpp

diff --git a/data/script/create_schema.sql b/data/script/create_schema.sql
new file mode 100644 (file)
index 0000000..9aa918d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  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
+ */
+
+CREATE TABLE IF NOT EXISTS admin (
+    id        INTEGER PRIMARY KEY AUTOINCREMENT,
+    pkg       TEXT NOT NULL,
+    uid       INTEGER,
+    key       TEXT,
+    removable INTEGER
+);
+
+CREATE TABLE IF NOT EXISTS managed_policy (
+    id        INTEGER PRIMARY KEY AUTOINCREMENT,
+    aid       INTEGER,
+    pid       INTEGER,
+    value     INTEGER 
+);
+
+CREATE TABLE IF NOT EXISTS policy_definition (
+    id        INTEGER PRIMARY KEY AUTOINCREMENT,
+    scope     INTEGER,
+    name      TEXT NOT NULL,
+    ivalue    INTEGER
+);
+
+CREATE TRIGGER IF NOT EXISTS prepare_storage AFTER INSERT ON admin
+FOR EACH ROW
+BEGIN
+       INSERT INTO managed_policy(aid, pid, value)
+               SELECT NEW.ID, policy_definition.id, policy_definition.ivalue
+               FROM policy_definition;
+END;
+
+CREATE TRIGGER IF NOT EXISTS remove_storage AFTER DELETE ON admin
+FOR EACH ROW
+BEGIN
+       DELETE FROM managed_policy WHERE managed_policy.aid = OLD.ID;
+END;
index 776de9c..1db3e21 100644 (file)
@@ -28,13 +28,19 @@ Requires: boost-regex boost-system boost-thread boost-filesystem
 Requires: procps-ng
 Requires: libsystemd
 
-%global user_name        security_fw
-%global group_name       security_fw
-%global smack_label      System
+%global user_name   security_fw
+%global group_name  security_fw
+%global smack_label System
 
-%global dpm_base         %{TZ_SYS_DATA}/dpm
-%global dpm_event        %{dpm_base}/event
-%global dpm_plugins      %{_libdir}/dpm/plugins
+%global ro_dir %{_datadir}
+%global rw_dir %{TZ_SYS_DATA}
+
+%global vist_ro_dir %{ro_dir}/vist
+%global vist_rw_dir %{rw_dir}/vist
+
+%global vist_db_dir     %{vist_rw_dir}/db
+%global vist_plugin_dir %{vist_ro_dir}/plugin
+%global vist_script_dir %{vist_ro_dir}/script
 
 %description
 Osquery exposes an operating system as a high-performance relational database.
@@ -63,31 +69,28 @@ cp %SOURCE1 .
                 -DUSER_NAME=%{user_name} \
                 -DGROUP_NAME=%{group_name} \
                 -DSMACK_LABEL=%{smack_label} \
-                -DSCRIPT_INSTALL_DIR=%{_scriptdir} \
-                -DSYSTEMD_UNIT_INSTALL_DIR=%{_unitdir} \
-                -DDATA_INSTALL_DIR=%{dpm_base} \
-                -DEVENT_CONFIGURE_DIR=%{dpm_event} \
-                -DPLUGIN_INSTALL_DIR=%{dpm_plugins} \
-                -DDB_INSTALL_DIR=%{TZ_SYS_DB} \
-                -DRUN_INSTALL_DIR=%{TZ_SYS_RUN} \
-                -DAPP_INSTALL_PREFIX="%{TZ_SYS_RO_APP}" \
-                -DAPP_SHARE_PACKAGES_DIR="%{TZ_SYS_RO_PACKAGES}" \
-                -DPAMD_INSTALL_DIR=/etc/pam.d \
-                -DDBUS_CONFIGURE_DIR=/etc/dbus-1/system.d \
-                -DDBUS_SERVICE_DIR=/usr/share/dbus-1/system-services
+                -DDB_INSTALL_DIR:PATH=%{vist_db_dir} \
+                -DPLUGIN_INSTALL_DIR:PATH=%{vist_plugin_dir} \
+                -DSCRIPT_INSTALL_DIR:PATH=%{vist_script_dir}
 
 make %{?jobs:-j%jobs}
 
 %install
 %make_install
-mkdir -p %{buildroot}%{dpm_event}
-mkdir -p %{buildroot}%{dpm_plugins}
-mkdir -p %{buildroot}/%{_unitdir}/multi-user.target.wants
-mkdir -p %{buildroot}/%{_unitdir}/sockets.target.wants
+mkdir -p %{buildroot}/%{vist_db_dir}
+mkdir -p %{buildroot}/%{vist_plugin_dir}
+mkdir -p %{buildroot}/%{vist_script_dir}
+
+cp data/script/*.sql %{buildroot}/%{vist_script_dir}
 
 %clean
 rm -rf %{buildroot}
 
+%files
+%manifest %{name}.manifest
+%{vist_script_dir}/*.sql
+%dir %attr(-, %{user_name}, %{group_name}) %{vist_db_dir}
+
 ## Test Package ##############################################################
 %package test 
 Summary: Osquery test
@@ -99,14 +102,13 @@ Requires: gtest
 Testcases for osquery 
 
 %files test
-%manifest %{name}.manifest
 %{_bindir}/osquery-test
 %{_bindir}/apix-test
 %{_bindir}/policyd-test
 
-## DPM Plugins - ############################################################
+## ViST Plugins - ###########################################################
 %package policyd-plugins
-Summary: DPM plugins
+Summary: ViST plugins
 Group: Security/Other
 ## Common
 BuildRequires: pkgconfig(buxton2)
@@ -127,4 +129,4 @@ Provides plugins for device policy manager
 
 %files policyd-plugins
 %manifest packaging/%{name}-plugins.manifest
-%{_libdir}/dpm/plugins/bluetooth
+%{vist_plugin_dir}/bluetooth
index 5445bfe..db86726 100644 (file)
@@ -24,12 +24,9 @@ PKG_CHECK_MODULES(POLICYD_DEPS REQUIRED ${DEPENDENCY})
 
 INCLUDE_DIRECTORIES(SYSTEM . ${POLICYD_DEPS_INCLUDE_DIRS})
 
-ADD_DEFINITIONS(-DDATA_PATH="${DATA_INSTALL_DIR}"
-                               -DRUN_PATH="${RUN_INSTALL_DIR}"
-                               -DDB_PATH="${DB_INSTALL_DIR}/.dpm.db"
+ADD_DEFINITIONS(-DDB_PATH="${DB_INSTALL_DIR}/.vist.db"
                                -DPLUGIN_INSTALL_DIR="${PLUGIN_INSTALL_DIR}"
-                               -DEVENT_CONFIGURE_DIR="${EVENT_CONFIGURE_DIR}")
-ADD_DEFINITIONS(-DUG_WAYLAND)
+                               -DSCRIPT_INSTALL_DIR="${SCRIPT_INSTALL_DIR}")
 
 ADD_SUBDIRECTORY(core)
 ADD_SUBDIRECTORY(sdk)
index c52433e..2589467 100644 (file)
@@ -21,7 +21,7 @@
 #include <memory>
 #include <mutex>
 
-#define DPM Logger::GetLogSink("DPM")
+#define VIST Logger::GetLogSink("VIST")
 
 namespace policyd {
 
index 156f68f..dba0d52 100644 (file)
@@ -26,12 +26,12 @@ PolicyManager::PolicyManager() : storage(DB_PATH)
 {
        loadProviders(PLUGIN_INSTALL_DIR);
        int cnt = loadPolicies();
-       INFO(DPM, std::to_string(cnt) + "-policies loaded");
+       INFO(VIST, std::to_string(cnt) + "-policies loaded");
 }
 
 std::pair<int, int> PolicyManager::loadProviders(const std::string& path)
 {
-       INFO(DPM, "Load policies from :" << path);
+       INFO(VIST, "Load policies from :" << path);
        klay::File dir(path);
        if (!dir.exists() || !dir.isDirectory())
                throw std::invalid_argument("Plugin directory is wrong.: " + path);
@@ -44,18 +44,18 @@ std::pair<int, int> PolicyManager::loadProviders(const std::string& path)
 
                try {
                        auto provider = PolicyLoader::load(iter->getPath());
-                       DEBUG(DPM, "Loaded provider: " << provider->getName());
+                       DEBUG(VIST, "Loaded provider: " << provider->getName());
                        this->providers.emplace_back(std::move(provider));
                } catch (const std::exception& e) {
                        ++failed;
-                       ERROR(DPM, "Failed to load: " << iter->getPath() << e.what());
+                       ERROR(VIST, "Failed to load: " << iter->getPath() << e.what());
                        continue;
                }
 
                ++passed;
        }
 
-       INFO(DPM, "Loaded result >> passed: " << passed << ", failed: " << failed);
+       INFO(VIST, "Loaded result >> passed: " << passed << ", failed: " << failed);
        return std::make_pair(passed, failed);
 }
 
@@ -66,16 +66,26 @@ int PolicyManager::loadPolicies()
                this->domain.insert(p->domain.cbegin(), p->domain.cend());
        }
 
+       bool changed = false;
        for (const auto& g : global) {
-               if (!storage.exists(g.first))
-                       throw std::runtime_error("Policy does not exist.: " + g.first);
+               if (!storage.exists(g.first)) {
+                       INFO(VIST, "Define global policy: " << g.first);
+                       storage.define(0, g.first, g.second->getInitial().value);
+                       changed = true;
+               }
        }
 
        for (const auto& d : domain) {
-               if (!storage.exists(d.first))
-                       throw std::runtime_error("Policy does not exist.: " + d.first);
+               if (!storage.exists(d.first)) {
+                       INFO(VIST, "Define domain policy: " << d.first);
+                       storage.define(1, d.first, d.second->getInitial().value);
+                       changed = true;
+               }
        }
 
+       if (changed)
+               storage.syncPolicyDefinition();
+
        return global.size() + domain.size();
 }
 
index 2fe2ece..0029bf3 100644 (file)
@@ -22,6 +22,8 @@
 #include <klay/db/statement.h>
 #include <klay/exception.h>
 
+#include <fstream>
+
 using namespace query_builder;
 using namespace policyd::schema;
 
@@ -45,6 +47,10 @@ auto definitionTable = make_table("policy_definition",
                                                                  make_column("scope", &PolicyDefinition::scope),
                                                                  make_column("name", &PolicyDefinition::name),
                                                                  make_column("ivalue", &PolicyDefinition::ivalue));
+
+const std::string SCRIPT_BASE = SCRIPT_INSTALL_DIR;
+const std::string SCRIPT_CREATE_SCHEMA  = "create_schema";
+
 } // anonymous namespace
 
 namespace policyd {
@@ -54,12 +60,15 @@ PolicyStorage::PolicyStorage(const std::string& path) :
                                                                                                        database::Connection::ReadWrite |
                                                                                                        database::Connection::Create))
 {
+       database->exec("PRAGMA foreign_keys = ON;");
+       database->exec(getScript(SCRIPT_CREATE_SCHEMA));
+
        sync();
 }
 
 void PolicyStorage::sync()
 {
-       DEBUG(DPM, "Sync policy storage to cache object.");
+       DEBUG(VIST, "Sync policy storage to cache object.");
        syncPolicyDefinition();
        syncAdmin();
        syncManagedPolicy();
@@ -77,7 +86,7 @@ void PolicyStorage::syncPolicyDefinition()
                pd.scope = stmt.getColumn(1);
                pd.name = std::string(stmt.getColumn(2));
                pd.ivalue = stmt.getColumn(3);
-               DEBUG(DPM, "Defined policy:" + pd.name);
+               DEBUG(VIST, "Defined policy:" + pd.name);
                this->definitions.emplace(pd.name, std::move(pd));
        }
 }
@@ -117,12 +126,50 @@ void PolicyStorage::syncManagedPolicy()
        }
 }
 
+std::string PolicyStorage::getScript(const std::string& name)
+{
+       std::string path = SCRIPT_BASE + "/" + name + ".sql";
+       std::ifstream is(path);
+       if (is.fail())
+               throw std::invalid_argument("Failed to open script: " + path); 
+
+       std::istreambuf_iterator<char> begin(is), end;
+       auto content = std::string(begin, end);
+       if (content.empty())
+               throw std::runtime_error("Failed to read script: " + path); 
+
+       return content;
+}
+
+void PolicyStorage::define(int scope, const std::string& policy, int ivalue)
+{
+       if (definitions.find(policy) != definitions.end()) {
+               INFO(VIST, "Policy is already defined: " + policy);
+               return;
+       }
+
+       PolicyDefinition pd;
+       pd.scope = scope;
+       pd.name = policy;
+       pd.ivalue = ivalue;
+
+       std::string insertQuery = definitionTable.insert(&PolicyDefinition::scope,
+                                                                                                        &PolicyDefinition::name,
+                                                                                                        &PolicyDefinition::ivalue);
+       database::Statement stmt(*database, insertQuery);
+       stmt.bind(1, pd.scope);
+       stmt.bind(2, pd.name);
+       stmt.bind(3, pd.ivalue);
+       if (!stmt.exec())
+               throw std::runtime_error("Failed to define policy: " + pd.name);
+}
+
 void PolicyStorage::enroll(const std::string& name, uid_t uid)
 {
        std::string alias = getAlias(name, uid);
-       INFO(DPM, "Enroll admin: " + alias);
+       INFO(VIST, "Enroll admin: " + alias);
        if (admins.find(alias) != admins.end()) {
-               ERROR(DPM, "Admin is aleady enrolled.: " + alias);
+               ERROR(VIST, "Admin is aleady enrolled.: " + alias);
                return;
        }
 
@@ -140,7 +187,7 @@ void PolicyStorage::enroll(const std::string& name, uid_t uid)
        stmt.bind(3, admin.key);
        stmt.bind(4, admin.removable);
        if (!stmt.exec())
-               throw std::runtime_error("Failed to enroll admin.: " + admin.pkg);
+               throw std::runtime_error("Failed to enroll admin: " + admin.pkg);
 
        /// Sync admin for getting admin ID.
        syncAdmin();
@@ -148,15 +195,15 @@ void PolicyStorage::enroll(const std::string& name, uid_t uid)
        syncManagedPolicy();
 
        int count = managedPolicies.size() / admins.size();
-       INFO(DPM, "Admin[" + alias + "] manages " + std::to_string(count) + "-policies.");
+       INFO(VIST, "Admin[" + alias + "] manages " + std::to_string(count) + "-policies.");
 }
 
 void PolicyStorage::disenroll(const std::string& name, uid_t uid)
 {
        std::string alias = getAlias(name, uid);
-       INFO(DPM, "Disenroll admin: " + alias);
+       INFO(VIST, "Disenroll admin: " + alias);
        if (admins.find(alias) == admins.end()) {
-               ERROR(DPM, "Not exist admin: " + alias);
+               ERROR(VIST, "Not exist admin: " + alias);
                return;
        } else {
                admins.erase(alias);
@@ -182,7 +229,7 @@ void PolicyStorage::update(const std::string& name, uid_t uid,
        if (definitions.find(policy) == definitions.end())
                throw std::runtime_error("Not exist policy: " + policy);
 
-       DEBUG(DPM, "Policy-update is called by admin: " + alias + ", about: " + policy +
+       DEBUG(VIST, "Policy-update is called by admin: " + alias + ", about: " + policy +
                           ", value: " + std::to_string(value));
 
        int policyId = definitions[policy].id;
@@ -226,8 +273,8 @@ PolicyValue PolicyStorage::strictest(const std::string& policy, uid_t uid)
                else
                        strictest->value = (*strictest < value) ? strictest->value : value;
 
-               DEBUG(DPM, "The strictest of policy[" + policy +
-                                  "] : " + std::to_string(strictest->value));
+               DEBUG(VIST, "The strictest of policy[" + policy +
+                                   "] : " + std::to_string(strictest->value));
        }
 
        if (strictest == nullptr)
index 697c899..439106e 100644 (file)
@@ -37,6 +37,10 @@ public:
        /// TODO(Sangwan): Consider to support lazy sync
        void sync();
 
+       void syncPolicyDefinition();
+       void syncAdmin();
+       void syncManagedPolicy();
+
        inline bool exists(const std::string& policy) const noexcept {
                return definitions.find(policy) != definitions.end();
        }
@@ -48,6 +52,7 @@ public:
        void enroll(const std::string& admin, uid_t uid);
        void disenroll(const std::string& admin, uid_t uid);
 
+       void define(int scope, const std::string& policy, int ivalue);
        void update(const std::string& admin, uid_t uid,
                                const std::string& policy, const PolicyValue& value);
 
@@ -56,10 +61,7 @@ public:
        std::unordered_map<std::string, PolicyValue> strictest(uid_t uid = 0);
 
 private:
-       void syncPolicyDefinition();
-       void syncAdmin();
-       void syncManagedPolicy();
-
+       std::string getScript(const std::string& name);
        std::string getAlias(const std::string& name, uid_t uid) const noexcept;
        int getUid(int adminId) const noexcept;
 
index 311e831..b0f7ab8 100644 (file)
@@ -41,12 +41,13 @@ TEST_F(PolicyStorageTests, initialize) {
        bool isRaised = false;
 
        try {
+               // DB is maden at run-time
                PolicyStorage storage("/tmp/dummy");
        } catch (const std::exception&) {
                isRaised = true;
        }
 
-       EXPECT_TRUE(isRaised);
+       EXPECT_FALSE(isRaised);
 }
 
 TEST_F(PolicyStorageTests, enrollment) {