From c0eca3d00ed82d17f8f83e3788a19b6ac2ba1c15 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Thu, 19 Mar 2020 16:54:04 +0100 Subject: [PATCH 01/16] CheckProperDrop class unit tests Change-Id: I1c867a319a5c14cf5ba67eb502e85505d00291c5 --- packaging/security-manager.spec | 1 + test/CMakeLists.txt | 9 + test/test_check_proper_drop.cpp | 576 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 586 insertions(+) create mode 100644 test/test_check_proper_drop.cpp diff --git a/packaging/security-manager.spec b/packaging/security-manager.spec index 127509f..753d4b6 100644 --- a/packaging/security-manager.spec +++ b/packaging/security-manager.spec @@ -87,6 +87,7 @@ Summary: Security manager unit test binaries Group: Security/Development Requires: boost-iostreams Requires: boost-test +Requires: boost-filesystem %description -n security-manager-tests Internal test for security manager implementation. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 453b70a..7fc1cf3 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -27,8 +27,12 @@ PKG_CHECK_MODULES(COMMON_DEP REQUIRED libtzplatform-config security-privilege-manager mount + libcap + libprocps ) +FIND_PACKAGE(Threads REQUIRED) + IF(DPL_WITH_DLOG) PKG_CHECK_MODULES(DLOG_DEP REQUIRED dlog) ENDIF(DPL_WITH_DLOG) @@ -60,6 +64,7 @@ SET(SM_TESTS_SOURCES ${SM_TEST_SRC}/test_privilege_db_migration.cpp ${SM_TEST_SRC}/test_smack-labels.cpp ${SM_TEST_SRC}/test_smack-rules.cpp + ${SM_TEST_SRC}/test_check_proper_drop.cpp ${DPL_PATH}/core/src/assert.cpp ${DPL_PATH}/core/src/colors.cpp ${DPL_PATH}/core/src/errno_string.cpp @@ -80,6 +85,7 @@ SET(SM_TESTS_SOURCES ${PROJECT_SOURCE_DIR}/src/common/filesystem.cpp ${PROJECT_SOURCE_DIR}/src/common/tzplatform-config.cpp ${PROJECT_SOURCE_DIR}/src/common/utils.cpp + ${PROJECT_SOURCE_DIR}/src/client/check-proper-drop.cpp ${GEN_PATH}/db.h ) @@ -131,6 +137,7 @@ INCLUDE_DIRECTORIES( ${COMMON_DEP_INCLUDE_DIRS} ${DLOG_DEP_INCLUDE_DIRS} ${SM_TEST_SRC} + ${Threads_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/src/include ${PROJECT_SOURCE_DIR}/src/client/include ${PROJECT_SOURCE_DIR}/src/common/include @@ -152,8 +159,10 @@ ADD_DEPENDENCIES(${TARGET_SM_PERFORMANCE_TESTS} generate) TARGET_LINK_LIBRARIES(${TARGET_SM_TESTS} ${COMMON_DEP_LIBRARIES} ${DLOG_DEP_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} boost_unit_test_framework boost_iostreams + boost_filesystem -ldl -lcrypt ) diff --git a/test/test_check_proper_drop.cpp b/test/test_check_proper_drop.cpp new file mode 100644 index 0000000..b6235e9 --- /dev/null +++ b/test/test_check_proper_drop.cpp @@ -0,0 +1,576 @@ +/* + * Copyright (c) 2020 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 + */ + +/** + * @file test_check_proper_drop.cpp + * @author Lukasz Pawelczyk (l.pawelczyk@samsung.com) + * @version 1.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace SecurityManager; + + +namespace { + +std::mutex mutex; +std::condition_variable cond; +std::condition_variable count; +unsigned counter = 0; +bool cancel = false; + +const ::size_t LABEL_SIZE = 255; + +const std::string NO_LABEL = ""; +// should work with any label if not for onlycaps in Tizen: +const std::string OTHER_LABEL = "User::Shell"; +const std::string ROGUE_LABEL = "rogue_label"; + +using Caps = std::vector<::cap_value_t>; + +const Caps NO_CAPS; +const Caps SMACK_CAPS = { + CAP_MAC_ADMIN, + CAP_MAC_OVERRIDE +}; +const Caps ROGUE_CAPS = { + CAP_NET_ADMIN, + CAP_SYS_ADMIN +}; + +namespace fs = boost::filesystem; +namespace ch = std::chrono; + +/* In theory after thread has been joined it is guaranteed that the + * thread has been terminated. In practive its /proc/PID/task entries + * might still exist and it's sometimes even possible to get its + * status, caps, label, etc. This fixture class waits for all the + * threads to be cleaned up properly by the kernel. It's used by tests + * that require no other threads to exist in the moment the test + * starts. */ +class NoThreadsAssert +{ +public: + NoThreadsAssert() + { + const fs::path path = "/proc/self/task"; + if (!fs::is_directory(path)) { + ThrowMsg(CommonException::InternalError, + "Something is wrong with the /proc. Is it mounted?"); + } + + ch::steady_clock::time_point begin = ch::steady_clock::now(); + + for (;;) { + size_t n = number_of_entries(path); + if (n == 1) + break; + + ch::steady_clock::time_point now = ch::steady_clock::now(); + auto diff = ch::duration_cast(now - begin).count(); + if (diff > 3000) { + ThrowMsg(CommonException::InternalError, + "Timed out, process still has threads, test cannot proceed"); + } + std::this_thread::sleep_for(ch::milliseconds(10)); + } + } + +private: + size_t number_of_entries(const fs::path& dir) + { + size_t count = 0; + for (const fs::directory_entry& e: fs::directory_iterator(dir)) { + (void)e; + ++count; + } + return count; + } +}; + +class Smack +{ +public: + Smack(bool should_restore) + : restore(should_restore) + { + const ::pid_t tid = ::syscall(SYS_gettid); + path = "/proc/" + std::to_string(tid) + "/attr/current"; + } + + ~Smack() + { + if (saved_label.empty()) + return; + + if (restore) { + bool ret = set_self_label(saved_label); + if (!ret) { + std::cout + << "Failed to restore label, further tests might fail: " + << ::strerror(errno) << std::endl; + } + } + } + + bool set(const std::string& label) + { + if (saved_label.empty()) { + saved_label = get_self_label(); + if (saved_label.empty()) { + std::cout << "Failed to get current label: " + << ::strerror(errno) << std::endl; + return false; + } + } + + bool ret = set_self_label(label); + if (!ret) { + std::cout << "Failed to set new label: " + << ::strerror(errno) << std::endl; + return false; + } + + return true; + } + +private: + bool set_self_label(const std::string& label) + { + int fd = ::open(path.c_str(), O_WRONLY); + if (fd < 0) + return false; + + int ret = TEMP_FAILURE_RETRY(::write(fd, label.c_str(), label.length())); + + ::close(fd); + return ret == (::ssize_t)label.length(); + } + + std::string get_self_label() + { + char label[LABEL_SIZE + 1] = {0}; + + int fd = ::open(path.c_str(), O_RDONLY); + if (fd < 0) + return ""; + + TEMP_FAILURE_RETRY(::read(fd, label, LABEL_SIZE)); + + ::close(fd); + return label; + } + + std::string saved_label; + bool restore; + + std::string path; +}; + +class Privs +{ +public: + Privs(bool should_restore) + : saved_cap(NULL), + restore(should_restore) + {} + + ~Privs() + { + if (saved_cap == NULL) + return; + + if (restore) { + int ret = ::cap_set_proc(saved_cap); + if (ret != 0) { + std::cout + << "Failed to restore capabilities, further tests might fail: " + << ::strerror(errno) << std::endl; + } + } + + ::cap_free(saved_cap); + } + + bool drop(const std::vector& to_drop) + { + int ret; + cap_t new_cap = NULL; + + if (saved_cap == NULL) { + saved_cap = ::cap_get_proc(); + if (saved_cap == NULL) { + std::cout << "Failed to get current capabilities: " + << ::strerror(errno) << std::endl; + goto fail; + } + } + + new_cap = ::cap_dup(saved_cap); + if (new_cap == NULL) { + std::cout << "Failed to duplicate capabilities: " + << ::strerror(errno) << std::endl; + goto fail; + } + + ret = ::cap_set_flag(new_cap, CAP_EFFECTIVE, to_drop.size(), + to_drop.data(), CAP_CLEAR); + if (ret != 0) { + std::cout << "Failed to configure capabilities: " + << ::strerror(errno) << std::endl; + goto fail; + } + + ret = ::cap_set_proc(new_cap); + if (ret != 0) { + std::cout << "Failed to drop capabilities: " + << ::strerror(errno) << std::endl; + goto fail; + } + + ::cap_free(new_cap); + return true; + + fail: + ::cap_free(new_cap); + return false; + } + +private: + cap_t saved_cap; + bool restore; +}; + +struct Thread { + Thread(const std::string& l, const Caps& c) + : label(l), + caps(c), + ret(false), + thread(std::thread(&Thread::thread_routine, this)) + {} + + ~Thread() + { + if (thread.joinable()) { + thread.join(); + } + } + + Thread(const Thread&) = delete; + Thread(Thread&& other) = default; + + Thread& operator=(const Thread&) = delete; + Thread& operator=(Thread&& other) = default; + + void thread_routine() + { + ret = false; + std::string id = "Thread: (unknown) "; + + try { + const ::pid_t tid = ::syscall(SYS_gettid); + id = "Thread: " + std::to_string(tid) + "/" + label + " "; + + std::cout << id << "reporting for duty" << std::endl; + + if (!label.empty()) { + Smack smack(false); + if (!smack.set(label)) { + std::cout << id << "failed to set label" << std::endl; + return; + } + } + + if (!caps.empty()) { + Privs privs(false); + if (!privs.drop(caps)) { + std::cout << id << "failed to drop privs" << std::endl; + return; + } + } + + { + std::unique_lock lock(mutex); + counter++; + count.notify_one(); + cond.wait(lock, [] { return cancel; }); + } + + std::cout << id << "properly canceled" << std::endl; + ret = true; + } catch (const std::exception &e) { + std::cout << id << "thrown: " << e.what() << std::endl; + } + } + + std::string label; + Caps caps; + + bool ret; + std::thread thread; +}; + +struct ThreadInfo { + std::string label; + Caps caps; +}; + +class Threads { +public: + Threads(const std::vector& thread_infos) + { + /* Reset the global variables before anything else. They're + * used to synchronize threads' start and cancel */ + counter = 0; + cancel = false; + + /* This is important, it's not only an optimization. Without + * this the vector will reallocate and move its objects while + * threads are running. */ + threads.reserve(thread_infos.size()); + + for (auto &ti: thread_infos) { + threads.emplace_back(ti.label, ti.caps); + } + + /* wait for all threads to launch */ + std::unique_lock lock(mutex); + bool ret = count.wait_for(lock, ch::seconds(3), + [this] { return counter == threads.size(); }); + if (!ret) { + std::cout << "Timed out waiting for threads" << std::endl; + } + } + + ~Threads() + { + /* Emergency handle if cancel_threads_and_check_ret() was not + * called. Threads will join by themselves in their own + * destructors */ + if (!threads.empty()) { + cancel = true; + cond.notify_all(); + } + } + + bool cancel_threads_and_check_ret() + { + { + std::unique_lock lock(mutex); + cancel = true; + cond.notify_all(); + } + + bool all_true = true; + for (auto &t: threads) { + t.thread.join(); + if (!t.ret) { + std::cout << "Thread with label: " << t.label + << " returned false" << std::endl; + all_true = false; + } + } + + threads.clear(); + return all_true; + } + +private: + std::vector threads; +}; + +} // namespace + + +BOOST_AUTO_TEST_SUITE(CHECK_PROPER_DROP_TEST) + +/* Everything should succeed */ +BOOST_FIXTURE_TEST_CASE(T1700__no_threads, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.checkThreads()); + BOOST_REQUIRE(ret == true); +} + +/* Everything should succeed */ +BOOST_FIXTURE_TEST_CASE(T1701__threads_unmodified, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + + Threads t({{NO_LABEL, NO_CAPS}, + {NO_LABEL, NO_CAPS}, + {NO_LABEL, NO_CAPS}}); + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.checkThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE(t.cancel_threads_and_check_ret() == true); +} + +/* Everything should succeed */ +BOOST_FIXTURE_TEST_CASE(T1702__threads_unprivileged, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + Privs privs(true); + + Threads t({{NO_LABEL, SMACK_CAPS}, + {NO_LABEL, SMACK_CAPS}, + {NO_LABEL, SMACK_CAPS}}); + BOOST_REQUIRE(privs.drop(SMACK_CAPS) == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.checkThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE(t.cancel_threads_and_check_ret() == true); +} + +/* Everything should succeed */ +BOOST_FIXTURE_TEST_CASE(T1704__threads_labeled, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + Smack smack(true); + + Threads t({{OTHER_LABEL, NO_CAPS}, + {OTHER_LABEL, NO_CAPS}, + {OTHER_LABEL, NO_CAPS}}); + BOOST_REQUIRE(smack.set(OTHER_LABEL) == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.checkThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE(t.cancel_threads_and_check_ret() == true); +} + +/* Everything should succeed */ +BOOST_FIXTURE_TEST_CASE(T1704__threads_labeled_unprivileged, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + Smack smack(true); + Privs privs(true); + + Threads t({{OTHER_LABEL, SMACK_CAPS}, + {OTHER_LABEL, SMACK_CAPS}, + {OTHER_LABEL, SMACK_CAPS}}); + BOOST_REQUIRE(smack.set(OTHER_LABEL) == true); + BOOST_REQUIRE(privs.drop(SMACK_CAPS) == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.checkThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE(t.cancel_threads_and_check_ret() == true); +} + +/* checkThreads should fail due to different label between main + * thread and one of the children */ +BOOST_FIXTURE_TEST_CASE(T1711__threads__rogue_label, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + + Threads t({{ NO_LABEL, NO_CAPS}, + { NO_LABEL, NO_CAPS}, + {ROGUE_LABEL, NO_CAPS}}); + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.checkThreads()); + BOOST_REQUIRE(ret == false); + + BOOST_REQUIRE(t.cancel_threads_and_check_ret() == true); +} + +/* checkThreads should fail due to different caps between main + * thread and one of the children */ +BOOST_FIXTURE_TEST_CASE(T1712__threads__rogue_caps, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + + Threads t({{NO_LABEL, NO_CAPS}, + {NO_LABEL, NO_CAPS}, + {NO_LABEL, ROGUE_CAPS}}); + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.checkThreads()); + BOOST_REQUIRE(ret == false); + + BOOST_REQUIRE(t.cancel_threads_and_check_ret() == true); +} + +/* getThreads should fail due to the main thread having no access to + * one of the children */ +BOOST_FIXTURE_TEST_CASE(T1721__threads_unprivileged__rogue_label, NoThreadsAssert) +{ + CheckProperDrop cpd; + bool ret; + Privs privs(true); + + Threads t({{ NO_LABEL, SMACK_CAPS}, + { NO_LABEL, SMACK_CAPS}, + {ROGUE_LABEL, SMACK_CAPS}}); + BOOST_REQUIRE(privs.drop(SMACK_CAPS) == true); + + BOOST_REQUIRE_NO_THROW(ret = cpd.getThreads()); + BOOST_REQUIRE(ret == false); + + BOOST_REQUIRE(t.cancel_threads_and_check_ret() == true); +} + +BOOST_AUTO_TEST_SUITE_END() -- 2.7.4 From f3cf72725ee34d42e588dcb43d4e9ee4902565c9 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Wed, 8 Apr 2020 12:55:23 +0200 Subject: [PATCH 02/16] Fix ignoring ENOENT Also better error logging for check-proper-drop Change-Id: I42bfff586d3a5d14a39ffbe16a8dfddea720d085 --- src/client/check-proper-drop.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/client/check-proper-drop.cpp b/src/client/check-proper-drop.cpp index 23fb136..9deac7e 100644 --- a/src/client/check-proper-drop.cpp +++ b/src/client/check-proper-drop.cpp @@ -32,6 +32,7 @@ #include #include +#include namespace { @@ -63,6 +64,7 @@ proc_t* readtask_priv(PROCTAB * const PT, const proc_t * const p) ret = PT->taskreader(PT,p,t,path); if (errno && errno != ENOENT) goto out; + errno = 0; if (ret != NULL) return ret; } @@ -93,7 +95,7 @@ bool CheckProperDrop::getThreads() m_proc = readproc(proctabPtr.get(), nullptr); if (!m_proc) ThrowMsg(Exception::ProcError, - "Unable to read process information for " << pid); + "Unable to read process information for " << m_pid); proc_t *thread; while ((thread = readtask_priv(proctabPtr.get(), m_proc))) { @@ -104,12 +106,17 @@ bool CheckProperDrop::getThreads() } if (errno == EACCES) { LogError("Permission denied while reading proc data, some threads might not be " - "synchronized for " << pid); + "synchronized for " << m_pid); return false; } - if (errno) + if (errno) { + static const unsigned ERROR_MSG_LEN = 1024; + char error_msg[ERROR_MSG_LEN]; + const char *e = strerror_r(errno, error_msg, ERROR_MSG_LEN); + ThrowMsg(Exception::ProcError, - "Unable to read process information for " << pid); + "Unable to read process information for " << m_pid << ": " << e); + } LogDebug("Reading proc data for " << m_threads.size() << " additional threads beside main thread"); return true; -- 2.7.4 From 3d155f6c38055e91a9af2572bb0e3405c1f453b3 Mon Sep 17 00:00:00 2001 From: Konrad Lipinski Date: Thu, 9 Apr 2020 16:19:08 +0200 Subject: [PATCH 03/16] Make prepare_app more robust with respect to thread termination Since CheckProperDrop now silently ignores ENOENT when reading thread proc entries, security_manager_sync_threads_internal should strive to do the same when signalling threads via tgkill. This will not, of course, eliminate race conditions - the entire thing is inherently racy. Bonus: * prepare_app contract prohibits concurrent thread creation/termination * per HQ request, EACCES readproc log now suggests a possible race condition in the caller Change-Id: Icf5d3e732540c4832d47e3e80f1592dab6f3ce35 --- src/client/check-proper-drop.cpp | 4 ++-- src/client/client-security-manager.cpp | 13 +++++++++++-- src/include/app-runtime.h | 1 + 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/client/check-proper-drop.cpp b/src/client/check-proper-drop.cpp index 9deac7e..6566f3d 100644 --- a/src/client/check-proper-drop.cpp +++ b/src/client/check-proper-drop.cpp @@ -105,8 +105,8 @@ bool CheckProperDrop::getThreads() freeproc(thread); } if (errno == EACCES) { - LogError("Permission denied while reading proc data, some threads might not be " - "synchronized for " << m_pid); + LogError("Thread data read access denied while running prepare_app for pid " << m_pid + << ". Possible race condition: the caller may have created threads during prepare_app execution."); return false; } if (errno) { diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 967d394..33665dc 100644 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -581,6 +581,8 @@ static inline int security_manager_sync_threads_internal(const std::string &app_ if (smack_check()) g_p_app_label = &app_label; + LogDebug("number of threads to sync: " << files.size() - 1); + if (files.size() > 1) { const pid_t cur_pid = getpid(); const pid_t cur_tid = Syscall::gettid(); @@ -614,18 +616,25 @@ static inline int security_manager_sync_threads_internal(const std::string &app_ std::atomic_thread_fence(std::memory_order_release); + int threads_gone = 0; for (auto const &e : files) { const int tid = atoi(e.c_str()); if (tid == static_cast(cur_tid)) continue; if (Syscall::tgkill(cur_pid, tid, SIGSETXID) < 0) { - LogError("Error in tgkill()" << GetErrnoString(errno)); + const auto err = errno; + if (ESRCH == err) // thread already gone + threads_gone++; + else + LogError("Error in tgkill()" << GetErrnoString(err)); + continue; } } - LogDebug("sent_signals_count: " << files.size() - 1); + g_threads_count -= threads_gone; + LogDebug("number of threads already gone (signals unsent): " << threads_gone); for (int i = MAX_SIG_WAIT_TIME; g_threads_count && i; i--) usleep(1000); // 1 ms diff --git a/src/include/app-runtime.h b/src/include/app-runtime.h index d193358..1dccb37 100644 --- a/src/include/app-runtime.h +++ b/src/include/app-runtime.h @@ -100,6 +100,7 @@ int security_manager_prepare_app_candidate(void); * already called and all existing threads are already in the same namespaces. * * This function can be called in multithreaded environment. + * However, threads must not be spawned or terminated while the function's running. * * \param[in] app_id Application identifier * \return API return code or error code -- 2.7.4 From 43668afd926ff677db10d65bf3c77523e4406280 Mon Sep 17 00:00:00 2001 From: Tomasz Swierczek Date: Fri, 10 Apr 2020 12:43:39 +0200 Subject: [PATCH 04/16] Release 0.5.22 * Make prepare_app more robust with respect to thread termination * Fix ignoring ENOENT * CheckProperDrop class unit tests * Add new core privilege: notification.admin * Do not ignore EACCES (and other errors) while getting threads info * Unify path generation * Add single manifest file for each RPM package Change-Id: I3ba0fcd56821fa453947e3efa3543d5babcc56a5 --- packaging/security-manager.changes | 14 ++++++++++++++ packaging/security-manager.spec | 2 +- pc/security-manager.pc.in | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/packaging/security-manager.changes b/packaging/security-manager.changes index 055b0dc..2ef67eb 100644 --- a/packaging/security-manager.changes +++ b/packaging/security-manager.changes @@ -1,3 +1,17 @@ +Release: 1.5.22 +Date: 2020.04.10 +Name: Release 1.5.22 +Description: +Make prepare_app more robust with respect to thread termination +Fix ignoring ENOENT +CheckProperDrop class unit tests +Add new core privilege: notification.admin +Do not ignore EACCES (and other errors) while getting threads info +Unify path generation +Add single manifest file for each RPM package + +############################### + Release: 1.5.21 Date: 2020.02.28 Name: Release 1.5.21 diff --git a/packaging/security-manager.spec b/packaging/security-manager.spec index 753d4b6..0d50227 100644 --- a/packaging/security-manager.spec +++ b/packaging/security-manager.spec @@ -1,6 +1,6 @@ Name: security-manager Summary: Security manager and utilities -Version: 1.5.21 +Version: 1.5.22 Release: 0 Group: Security/Service License: Apache-2.0 diff --git a/pc/security-manager.pc.in b/pc/security-manager.pc.in index 48c4c7f..9ac13bb 100644 --- a/pc/security-manager.pc.in +++ b/pc/security-manager.pc.in @@ -5,7 +5,7 @@ includedir=${prefix}/include Name: security-manager Description: Security Manager Package -Version: 1.5.21 +Version: 1.5.22 Requires: Libs: -L${libdir} -lsecurity-manager-client Cflags: -I${includedir}/security-manager -- 2.7.4 From 09b630dde16d38a8d4d3cba4901ae7f993c8b2e6 Mon Sep 17 00:00:00 2001 From: Dariusz Michaluk Date: Tue, 14 Apr 2020 15:09:35 +0200 Subject: [PATCH 05/16] Fix security-manager worker Move worker process to main mount namespace after finishing job. Change-Id: Ic0ed8011ecc8fab04a237c6a96190f4a8cc5d266 --- src/common/worker.cpp | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/common/worker.cpp b/src/common/worker.cpp index 466f50d..70d0d4a 100644 --- a/src/common/worker.cpp +++ b/src/common/worker.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017 - 2019 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2017 - 2020 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Tomasz Swierczek * @@ -40,29 +40,22 @@ Worker::Worker(Channel channel) int Worker::doWork(const NSMountLogic::EntryVector &entries) { int status = 0; - bool inGlobalNamespace = false; + bool inGlobalNamespace = true; 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.smackLabel << " will not be setup correctly."); - continue; - } - - if (MountNS::enterMountNamespace(MountNS::getUserAppServiceMountPointPath(entry.uid, entry.smackLabel, entry.pid))) { + auto appNamespace = MountNS::getUserAppServiceMountPointPath(entry.uid, entry.smackLabel, entry.pid); + if (MountNS::enterMountNamespace(appNamespace)) { inGlobalNamespace = false; } else { + status = -1; + LogError("Error entering app mount namespace. Environment of application: " + << entry.smackLabel << "for user: " << entry.uid << " will not be setup correctly."); continue; } + // in most cases entry.uid will not change between iterations + auto storagePrivilegePathMap = MountNS::getPrivilegePathMap(entry.uid); for (auto &privStatus : entry.privilegeStatusVector) { auto &privName = privStatus.first; auto &allowed = privStatus.second; @@ -77,13 +70,22 @@ int Worker::doWork(const NSMountLogic::EntryVector &entries) "directory " << privilegePath.dstPath << " doesn't exist"); continue; } - if (SECURITY_MANAGER_SUCCESS != applyPrivilegePath(allowed, privilegePath)) + if (SECURITY_MANAGER_SUCCESS != applyPrivilegePath(allowed, privilegePath)) { status = -1; + LogError("Environment of application: " << entry.smackLabel << "for user: " + << entry.uid << " will not be setup correctly."); + } } } } catch (...) { status = -1; - LogError("Could not set up access to path for application: " << entry.smackLabel); + LogError("Environment of application: " << entry.smackLabel << "for user: " + << entry.uid << " will not be setup correctly."); + } + + if (!inGlobalNamespace && !MountNS::enterMountNamespace(MountNS::MAIN_MOUNT_NAMESPACE)) { + status = -1; + LogError("Error entering global mount namespace."); } } return status; -- 2.7.4 From 8a6f4495da807157f597eff29294d56191e7ebe6 Mon Sep 17 00:00:00 2001 From: Zofia Grzelewska Date: Wed, 12 Feb 2020 18:50:21 +0100 Subject: [PATCH 06/16] Add privilege-Smack mapping Add privilege-Smack mapping configuration: * privilege-smack.list which describes privilege mapping to Smack label and Smack rules template * priv-rules-default-template.smack which is an example of Smack rules template for privilege * this implementation currently only applies policy on application launch (no runtime policy changes modify it) and draft implementation. IMPORTANT: This mechanism can be used, when *only one* user is used on Tizen. Change-Id: Iafc999793e6fe465279d0e63ca087ae6b836181a --- CMakeLists.txt | 6 ++ policy/CMakeLists.txt | 19 +++-- policy/priv-rules-default-template.smack | 2 + policy/privilege-smack.list | 16 +++++ src/common/include/config.h | 2 + src/common/include/service_impl.h | 17 ++++- src/common/include/smack-rules.h | 47 +++++++++--- src/common/service_impl.cpp | 68 +++++++++++++----- src/common/smack-rules.cpp | 118 ++++++++++++++++++++++++++++++- src/server/service/service.cpp | 12 +++- 10 files changed, 270 insertions(+), 37 deletions(-) create mode 100644 policy/priv-rules-default-template.smack create mode 100644 policy/privilege-smack.list diff --git a/CMakeLists.txt b/CMakeLists.txt index 79733d4..d6f2f60 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,11 +64,17 @@ SET(DB_TEST_INSTALL_DIR CACHE PATH "Read-only test database directory") +SET(PRIV_MAPPING_INSTALL_SUBDIR + "privilege-mapping" + CACHE PATH + "Read-only privilege Smack mapping directory") + ADD_DEFINITIONS("-DDB_INSTALL_DIR=\"${DB_INSTALL_DIR}\"") ADD_DEFINITIONS("-DLOCAL_STATE_INSTALL_DIR=\"${LOCAL_STATE_INSTALL_DIR}\"") ADD_DEFINITIONS("-DDATA_INSTALL_DIR=\"${DATA_INSTALL_DIR}\"") ADD_DEFINITIONS("-DPOLICY_INSTALL_DIR=\"${POLICY_INSTALL_DIR}\"") ADD_DEFINITIONS("-DDB_TEST_INSTALL_DIR=\"${DB_TEST_INSTALL_DIR}\"") +ADD_DEFINITIONS("-DPRIV_MAPPING_INSTALL_SUBDIR=\"${PRIV_MAPPING_INSTALL_SUBDIR}\"") ############################# compiler flags ################################## diff --git a/policy/CMakeLists.txt b/policy/CMakeLists.txt index 2848fe2..499606e 100644 --- a/policy/CMakeLists.txt +++ b/policy/CMakeLists.txt @@ -4,12 +4,21 @@ FILE(GLOB USERTYPE_POLICY_FILES usertype-*.profile) CONFIGURE_FILE(security-manager-policy-reload.in security-manager-policy-reload @ONLY) +SET(PRIV_MAPPING_TEMPLATE_FILES + "priv-rules-default-template.smack") +SET(INSTALL_TEMPLATE_FILES + "app-rules-template.smack" + "pkg-rules-template.smack" + "author-rules-template.smack" + ) + INSTALL(FILES ${USERTYPE_POLICY_FILES} DESTINATION ${POLICY_INSTALL_DIR}) -INSTALL(FILES "app-rules-template.smack" DESTINATION ${POLICY_INSTALL_DIR}) -INSTALL(FILES "pkg-rules-template.smack" DESTINATION ${POLICY_INSTALL_DIR}) -INSTALL(FILES "author-rules-template.smack" DESTINATION ${POLICY_INSTALL_DIR}) +INSTALL(FILES ${PRIV_MAPPING_TEMPLATE_FILES} + DESTINATION "${POLICY_INSTALL_DIR}/${PRIV_MAPPING_INSTALL_SUBDIR}") +INSTALL(FILES ${INSTALL_TEMPLATE_FILES} DESTINATION ${POLICY_INSTALL_DIR}) INSTALL(FILES "privilege-group.list" DESTINATION ${POLICY_INSTALL_DIR}) INSTALL(FILES "privilege-mount.list" DESTINATION ${POLICY_INSTALL_DIR}) +INSTALL(FILES "privilege-smack.list" DESTINATION ${POLICY_INSTALL_DIR}) INSTALL(FILES "privilege-managed-by-systemd-for-daemons.list" DESTINATION ${POLICY_INSTALL_DIR}) INSTALL(PROGRAMS "update.sh" DESTINATION ${POLICY_INSTALL_DIR}) INSTALL(DIRECTORY "updates" USE_SOURCE_PERMISSIONS DESTINATION ${POLICY_INSTALL_DIR}) @@ -23,7 +32,7 @@ SET(GENERATOR "./generate-rule-code") ADD_CUSTOM_COMMAND(OUTPUT ${GEN_FILE} COMMAND mkdir -p "${GEN_PATH}" - COMMAND ${GENERATOR} *.smack > ${GEN_FILE} - DEPENDS ${GENERATOR} "*.smack" + COMMAND ${GENERATOR} ${INSTALL_TEMPLATE_FILES} > ${GEN_FILE} + DEPENDS ${GENERATOR} ${INSTALL_TEMPLATE_FILES} ) ADD_CUSTOM_TARGET(generate_rule_template_code DEPENDS ${GEN_FILE}) diff --git a/policy/priv-rules-default-template.smack b/policy/priv-rules-default-template.smack new file mode 100644 index 0000000..09c5be6 --- /dev/null +++ b/policy/priv-rules-default-template.smack @@ -0,0 +1,2 @@ +~PROCESS~ ~PRIVILEGE~ w +~PRIVILEGE~ ~PROCESS~ w diff --git a/policy/privilege-smack.list b/policy/privilege-smack.list new file mode 100644 index 0000000..f73816a --- /dev/null +++ b/policy/privilege-smack.list @@ -0,0 +1,16 @@ +# Configuration of Smack label and rules mapping of privileges +# Format: +# - Each line of " " describes +# single mapping +# * : name of enforced privilege +# * : unique Smack label mapped to given privilege +# * : full filename of existing template file in security-manager +# policy configuration dir (usually /usr/share/security-manager/policy/), which will be +# used to generate Smack rules +# +# - may be set to special value "default". +# In such case 'priv-rules-default-template.smack' will be used. +# +# - lines starting with '#' or empty lines are ignored + +http://tizen.org/privilege/internet System::Privilege::Internet default diff --git a/src/common/include/config.h b/src/common/include/config.h index 281b838..1fef74f 100644 --- a/src/common/include/config.h +++ b/src/common/include/config.h @@ -42,8 +42,10 @@ #define APPS_LABELS_FILE "apps-labels" /* Policy files */ + #define PRIVILEGE_GROUP_LIST_FILE POLICY_INSTALL_DIR "/privilege-group.list" #define PRIVILEGE_MOUNT_LIST_FILE POLICY_INSTALL_DIR "/privilege-mount.list" +#define PRIVILEGE_SMACK_LIST_FILE POLICY_INSTALL_DIR "/privilege-smack.list" #define PRIVILEGE_SYSTEMD_LIST_FILE POLICY_INSTALL_DIR "/privilege-managed-by-systemd-for-daemons.list" #define SKEL_DIR "/etc/skel" diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index 80c0a82..76239b2 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -175,20 +175,33 @@ public: int policyGetDesc(std::vector &descriptions); /** + * Get vector of privileges allowed for given application. + * + * @param[in] appProcessLabel Smack label of application process + * @param[in] uid user id of given application process + * @param[out] allowedPrivilege vector of privileges allowed for given application + * + * @return API return code, as defined in protocols.h + */ + int getAppAllowedPrivileges(const std::string &appProcessLabel, uid_t uid, + std::vector &allowedPrivileges); + + /** * Process query for resources group list and supplementary groups allowed for the application. * For given \ref appProcessLabel and \ref uid, calculate allowed privileges that give * direct access to file system resources. For each permission Cynara will be * queried. * Returns set of group ids that are permitted. * - * @param[in] creds credentials of the requesting process * @param[in] appProcessLabel application name + * @param[in] allowedPrivileges privileges allowed for application * @param[out] forbiddenGroups returned sorted vector of forbidden groups * @param[out] allowedGroups returned sorted vector of allowed groups * * @return API return code, as defined in protocols.h */ - int getForbiddenAndAllowedGroups(const Credentials &creds, const std::string &appProcessLabel, + int getForbiddenAndAllowedGroups(const std::string &appProcessLabel, + const std::vector &allowedPrivileges, std::vector &forbiddenGroups, std::vector &allowedGroups); /** diff --git a/src/common/include/smack-rules.h b/src/common/include/smack-rules.h index 8398ee0..b69c170 100644 --- a/src/common/include/smack-rules.h +++ b/src/common/include/smack-rules.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2020 Samsung Electronics Co., Ltd. All rights reserved * * Contact: Rafal Krypa * @@ -61,6 +61,20 @@ public: const std::string &pkgName, const int authorId); + void addFromPrivTemplate( + const RuleVector &templateRules, + const std::string &appProcessLabel, + const std::string &privilegeLabel, + const std::string &pkgName, + int authorId); + + void addFromPrivTemplateFile( + const std::string &templatePath, + const std::string &appProcessLabel, + const std::string &privilegeLabel, + const std::string &pkgName, + int authorId); + void apply() const; void clear() const; @@ -92,6 +106,23 @@ public: const Labels &pkgLabels); /** + * Enable privilege-specific smack rules for given application + * + * Function creates privilege-specific smack rules using predefined templates. + * Rules are applied to the kernel. + * + * @param[in] appProcessLabel - process label of the application + * @param[in] pkgName - package id of given application + * @param[in] authorId - author id of given application (if not applicable, set to -1) + * @param[in] privileges - a list of privileges allowed for given application + */ + static void enablePrivilegeRules( + const std::string &appProcessLabel, + const std::string &pkgName, + int authorId, + const std::vector &privileges); + + /** * Uninstall package-specific smack rules. * * Function loads package-specific smack rules, revokes them from the kernel. @@ -179,6 +210,13 @@ public: bool isPathSharedNoMore, bool isTargetSharingNoMore); + /** + * Revoke rules for which label of given \ref appName is a subject. + * + * @param[in] appProcessLabel = application process label + */ + static void revokeAppSubject(const std::string &appProcessLabel); + private: static void useTemplate( const std::string &templatePath, @@ -198,13 +236,6 @@ private: const std::string &replace); smack_accesses *m_handle; - - /** - * Revoke rules for which label of given \ref appName is a subject. - * - * @param[in] appProcessLabel = application process label - */ - static void revokeAppSubject(const std::string &appProcessLabel); }; } // namespace SecurityManager diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 98b86b8..6f40dd1 100644 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -1400,37 +1400,54 @@ int ServiceImpl::policyGetDesc(std::vector &levels) return ret; } -int ServiceImpl::getForbiddenAndAllowedGroups(const Credentials &creds, const std::string &appProcessLabel, - std::vector &forbiddenGroups, std::vector &allowedGroups) +int ServiceImpl::getAppAllowedPrivileges(const std::string &appProcessLabel, + uid_t uid, std::vector &allowedPrivileges) { try { - LogDebug("smack label: " << appProcessLabel); + LogDebug("Getting allowed privileges for " << appProcessLabel << " and user " << uid); + std::string uidStr = std::to_string(uid); std::vector privileges; - - std::string uidStr = std::to_string(creds.uid); m_cynaraAdmin.getAppPolicy(appProcessLabel, uidStr, privileges); m_cynaraAdmin.getAppPolicy(appProcessLabel, CYNARA_ADMIN_WILDCARD, privileges); m_cynaraAdmin.getAppPolicy(CYNARA_ADMIN_WILDCARD, CYNARA_ADMIN_WILDCARD, privileges); vectorRemoveDuplicates(privileges); + for (auto &privilege : privileges) { + if (m_cynara.check(appProcessLabel, privilege, uidStr, "")) { + allowedPrivileges.push_back(privilege); + } + } + } catch (const CynaraException::Base &e) { + LogError("Error while querying Cynara for permissions: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_SERVER_ERROR; + } catch (const std::bad_alloc &e) { + LogError("Memory allocation failed " << e.what()); + return SECURITY_MANAGER_ERROR_MEMORY; + } - std::string pidStr = std::to_string(creds.pid); - for (const auto &privilege : privileges) { + return SECURITY_MANAGER_SUCCESS; +} + +int ServiceImpl::getForbiddenAndAllowedGroups( + const std::string &appProcessLabel, const std::vector &allowedPrivileges, + std::vector &forbiddenGroups, std::vector &allowedGroups) +{ + + try { + LogDebug("smack label: " << appProcessLabel); + + for (const auto &privilege : allowedPrivileges) { const auto &pgids = m_privilegeGids.getGids(privilege); - LogDebug("Considering privilege " << privilege << " with " << + LogDebug("Using privilege " << privilege << " with " << pgids.size() << " groups assigned"); if (pgids.empty()) continue; - if (m_cynara.check(appProcessLabel, privilege, uidStr, pidStr)) { - allowedGroups.insert(allowedGroups.end(), pgids.begin(), pgids.end()); - LogDebug("Cynara allowed, adding groups"); - } else { - LogDebug("Cynara denied, not adding groups"); - } + allowedGroups.insert(allowedGroups.end(), pgids.begin(), pgids.end()); + } vectorRemoveDuplicates(allowedGroups); // sorted @@ -1438,9 +1455,6 @@ int ServiceImpl::getForbiddenAndAllowedGroups(const Credentials &creds, const st forbiddenGroups.reserve(gids.size()); std::set_difference(gids.begin(), gids.end(), allowedGroups.begin(), allowedGroups.end(), std::back_inserter(forbiddenGroups)); // sorted - } catch (const CynaraException::Base &e) { - LogError("Error while querying Cynara for permissions: " << e.DumpToString()); - return SECURITY_MANAGER_ERROR_SERVER_ERROR; } catch (const std::bad_alloc &e) { LogError("Memory allocation failed: " << e.what()); return SECURITY_MANAGER_ERROR_MEMORY; @@ -2133,7 +2147,25 @@ int ServiceImpl::prepareApp(const Credentials &creds, const std::string &appName return SECURITY_MANAGER_ERROR_UNKNOWN; label = SmackLabels::generateProcessLabel(appName, pkgName, isHybrid); - int ret = getForbiddenAndAllowedGroups(creds, label, forbiddenGroups, allowedGroups); + std::vector allowedPrivileges; + int ret = getAppAllowedPrivileges(label, creds.uid, allowedPrivileges); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogError("Failed to fetch allowed privileges for " << label); + return ret; + } + + int authorId; + m_privilegeDb.GetPkgAuthorId(pkgName, authorId); + + std::vector pkgLabels; + getPkgLabels(pkgName, pkgLabels); + + SmackRules::revokeAppSubject(label); + SmackRules::installApplicationRules(label, pkgName, authorId, pkgLabels); + SmackRules::enablePrivilegeRules(label, pkgName, authorId, allowedPrivileges); + + ret = getForbiddenAndAllowedGroups(label, allowedPrivileges, forbiddenGroups, + allowedGroups); return ret != SECURITY_MANAGER_SUCCESS ? ret : appSetupNamespace(creds, label, privilegeVector, privilegeStatusVector); } diff --git a/src/common/smack-rules.cpp b/src/common/smack-rules.cpp index df3fd44..d7963d8 100644 --- a/src/common/smack-rules.cpp +++ b/src/common/smack-rules.cpp @@ -32,6 +32,7 @@ #include #include +#include "config.h" #include "config-file.h" #include "dpl/log/log.h" #include "dpl/errno_string.h" @@ -48,22 +49,31 @@ const std::string SMACK_PROCESS_LABEL_TEMPLATE = "~PROCESS~"; const std::string SMACK_PATH_RW_LABEL_TEMPLATE = "~PATH_RW~"; const std::string SMACK_PATH_RO_LABEL_TEMPLATE = "~PATH_RO~"; const std::string SMACK_PATH_TRUSTED_LABEL_TEMPLATE = "~PATH_TRUSTED~"; +const std::string SMACK_PRIVILEGE_LABEL_TEMPLATE = "~PRIVILEGE~"; enum POLICY_FILE { APP_RULES_TEMPLATE, PKG_RULES_TEMPLATE, - AUTHOR_RULES_TEMPLATE + AUTHOR_RULES_TEMPLATE, + PRIV_DEFAULT_RULES_TEMPLATE, + PRIV_RULES_TEMPLATE }; const std::string POLICY_DIR_STR = POLICY_INSTALL_DIR; +const std::string PRIV_MAPPING_DIR_STR = POLICY_INSTALL_DIR "/" PRIV_MAPPING_INSTALL_SUBDIR; std::map POLICY_FILE_PATH_MAP = { {POLICY_FILE::APP_RULES_TEMPLATE, POLICY_DIR_STR + "/app-rules-template.smack"}, {POLICY_FILE::PKG_RULES_TEMPLATE, POLICY_DIR_STR + "/pkg-rules-template.smack"}, - {POLICY_FILE::AUTHOR_RULES_TEMPLATE, POLICY_DIR_STR + "/author-rules-template.smack"} + {POLICY_FILE::AUTHOR_RULES_TEMPLATE, POLICY_DIR_STR + "/author-rules-template.smack"}, + {POLICY_FILE::PRIV_DEFAULT_RULES_TEMPLATE, + PRIV_MAPPING_DIR_STR + "/priv-rules-default-template.smack"} }; -std::string getPolicyFile(enum POLICY_FILE policyFile) { +std::string getPolicyFile(enum POLICY_FILE policyFile, const std::string &privFile = "") { + if (policyFile == POLICY_FILE::PRIV_RULES_TEMPLATE) { + return PRIV_MAPPING_DIR_STR + "/" + privFile; + } return POLICY_FILE_PATH_MAP[policyFile]; } @@ -77,6 +87,8 @@ const std::string SMACK_SYSTEM_PRIVILEGED = "System::Privileged"; const std::string SMACK_APP_PATH_SYSTEM_PERMS = "rwxat"; const std::string SMACK_APP_PATH_USER_PERMS = "rwxat"; +const std::string PRIV_TEMPLATE_DEFAULT = "default"; + SmackRules::SmackRules() { if (smack_accesses_new(&m_handle) < 0) { @@ -169,6 +181,64 @@ void SmackRules::addFromTemplate( } } +void SmackRules::addFromPrivTemplate( + const RuleVector &templateRules, + const std::string &appProcessLabel, + const std::string &privilegeLabel, + const std::string &pkgName, + int authorId) +{ + std::string pathRWLabel, pathROLabel; + std::string pathTrustedLabel; + + if (!pkgName.empty()) { + pathRWLabel = SmackLabels::generatePathRWLabel(pkgName); + pathROLabel = SmackLabels::generatePathROLabel(pkgName); + } + + if (authorId >= 0) + pathTrustedLabel = SmackLabels::generatePathTrustedLabel(authorId); + + for (auto &rule : templateRules) { + if (rule.size() != 3) { + LogError("Invalid rule template: " << rule.size() << " tokens"); + ThrowMsg(SmackException::FileError, "Invalid rule template: " << rule.size() << " tokens"); + } + + std::string subject = rule[0]; + std::string object = rule[1]; + std::string permissions = rule[2]; + + strReplace(subject, SMACK_PROCESS_LABEL_TEMPLATE, appProcessLabel); + strReplace(subject, SMACK_PRIVILEGE_LABEL_TEMPLATE, privilegeLabel); + strReplace(object, SMACK_PROCESS_LABEL_TEMPLATE, appProcessLabel); + strReplace(object, SMACK_PRIVILEGE_LABEL_TEMPLATE, privilegeLabel); + strReplace(object, SMACK_PATH_RW_LABEL_TEMPLATE, pathRWLabel); + strReplace(object, SMACK_PATH_RO_LABEL_TEMPLATE, pathROLabel); + strReplace(object, SMACK_PATH_TRUSTED_LABEL_TEMPLATE, pathTrustedLabel); + + if (subject.empty() || object.empty()) + continue; + add(subject, object, permissions); + } +} + +void SmackRules::addFromPrivTemplateFile( + const std::string &templatePath, + const std::string &appProcessLabel, + const std::string &privilegeLabel, + const std::string &pkgName, + int authorId) +{ + try { + addFromPrivTemplate(ConfigFile(templatePath).read(), appProcessLabel, privilegeLabel, + pkgName, authorId); + } catch (FS::Exception::Base &) { + LogError("Error reading template file: " << templatePath); + ThrowMsg(SmackException::FileError, "Error reading template file: " << templatePath); + } +} + void SmackRules::generatePackageCrossDeps(const Labels &pkgLabels) { LogDebug("Generating cross-package rules"); @@ -215,6 +285,48 @@ void SmackRules::installApplicationRules( updatePackageRules(pkgName, pkgLabels); } +void SmackRules::enablePrivilegeRules( + const std::string &appProcessLabel, + const std::string &pkgName, + int authorId, + const std::vector &privileges) +{ + if (privileges.empty()) { + // No rules to apply + return; + } + + SmackRules smackRules; + + auto privMapping = ConfigFile(PRIVILEGE_SMACK_LIST_FILE).read(); + + for (auto &mapping : privMapping) { + if (mapping.size() != 3) + continue; + + auto &privName = mapping[0]; + auto &privLabel = mapping[1]; + auto &privTemplate = mapping[2]; + + if (privTemplate == PRIV_TEMPLATE_DEFAULT) { + privTemplate = getPolicyFile(POLICY_FILE::PRIV_DEFAULT_RULES_TEMPLATE); + } else { + privTemplate = getPolicyFile(POLICY_FILE::PRIV_RULES_TEMPLATE, privTemplate); + } + + for (auto &privilege : privileges) { + if (privilege == privName) { + smackRules.addFromPrivTemplateFile(privTemplate, appProcessLabel, privLabel, + pkgName, authorId); + break; + } + } + } + + if (smack_check()) + smackRules.apply(); +} + void SmackRules::updatePackageRules( const std::string &pkgName, const Labels &pkgLabels) diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index e29a807..e367b12 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -351,7 +351,17 @@ void Service::processGetForbiddenAndAllowedGroups(MessageBuffer &buffer, Message std::vector forbiddenGroups, allowedGroups; Deserialization::Deserialize(buffer, appName); - int ret = serviceImpl.getForbiddenAndAllowedGroups(creds, serviceImpl.getProcessLabel(appName), forbiddenGroups, allowedGroups); + + std::string label = serviceImpl.getProcessLabel(appName); + std::vector allowedPrivileges; + int ret = serviceImpl.getAppAllowedPrivileges(label, creds.uid, allowedPrivileges); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogError("Failed to fetch allowed privileges for " << label); + Serialization::Serialize(send, ret); + return; + } + ret = serviceImpl.getForbiddenAndAllowedGroups(label, allowedPrivileges, forbiddenGroups, + allowedGroups); Serialization::Serialize(send, ret); if (ret == SECURITY_MANAGER_SUCCESS) { Serialization::Serialize(send, forbiddenGroups, allowedGroups); -- 2.7.4 From c81a3db471ac152a8936dabe082c588ce5ee61c8 Mon Sep 17 00:00:00 2001 From: Zofia Abramowska Date: Mon, 30 Mar 2020 16:22:36 +0200 Subject: [PATCH 07/16] Change cynara client check to admin check for allowed privs Cynara client check will trigger custom plugins evaluation. This would be an unwanted behavior, as getAppAllowedPrivileges should return current state without involvement of the user. Using Cynara admin check we can achieve the same thing without triggering of the plugins. Change-Id: I6d60f9d70fa0d39ac6e9d108fef40227ba9e62d6 --- src/common/service_impl.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 6f40dd1..8be980f 100644 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -1414,7 +1414,11 @@ int ServiceImpl::getAppAllowedPrivileges(const std::string &appProcessLabel, vectorRemoveDuplicates(privileges); for (auto &privilege : privileges) { - if (m_cynara.check(appProcessLabel, privilege, uidStr, "")) { + int result = CYNARA_ADMIN_DENY; + std::string resultExtra; + m_cynaraAdmin.check(appProcessLabel, uidStr, privilege, CynaraAdmin::Buckets[Bucket::PRIVACY_MANAGER], result, resultExtra, true); + if (result == CYNARA_ADMIN_ALLOW) { + LogDebug("Application " << appProcessLabel << " has " << privilege << " allowed"); allowedPrivileges.push_back(privilege); } } -- 2.7.4 From 388432915d33959ce1237b1fe0840e8c3896df46 Mon Sep 17 00:00:00 2001 From: Zofia Abramowska Date: Thu, 26 Mar 2020 13:47:59 +0100 Subject: [PATCH 08/16] Change privilege and privilege status vector names for clarity PrivilegeVector and privilegeStatusVector passed to prepareApp are not general privileges, but privileges related to paths. This commit changes variables names to make it more clear. Change-Id: I66a05ea0db305ded53ed1d47f60496cd5fda8636 --- src/client/client-security-manager.cpp | 20 ++++++++++---------- src/common/include/service_impl.h | 14 +++++++------- src/common/service_impl.cpp | 14 +++++++------- src/server/service/service.cpp | 12 ++++++------ 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 33665dc..576bcf9 100644 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -466,7 +466,7 @@ static int fetchForbiddenAndAllowedGroups(const std::string &appName, std::vecto static int prepareAppInitialSetupAndFetch(const std::string &appName, const MountNS::PrivilegePathsMap &privilegePathsMap, std::string &label, std::string &pkgName, bool &enabledSharedRO, std::vector &forbiddenGroups, std::vector &allowedGroups, - std::vector &privilegeStatusVector) + std::vector &privPathsStatusVector) { ClientRequest request(SecurityModuleCall::PREPARE_APP); if (request.send(appName, serializeKeysAsVector(privilegePathsMap)).failed()) { @@ -474,7 +474,7 @@ static int prepareAppInitialSetupAndFetch(const std::string &appName, const Moun return request.getStatus(); } - request.recv(forbiddenGroups, allowedGroups, privilegeStatusVector, label, pkgName, enabledSharedRO); + request.recv(forbiddenGroups, allowedGroups, privPathsStatusVector, label, pkgName, enabledSharedRO); return SECURITY_MANAGER_SUCCESS; } @@ -831,13 +831,13 @@ static int setupSharedRO(const std::string &pkg_name, bool enabledSharedRO, cons } static int applyPrivileges(const MountNS::PrivilegePathsMap &privilegePathMap, - const std::vector &privilegeStatusVector, const std::string &appLabel) + const std::vector &privPathsStatusVector, const std::string &appLabel) { - if (privilegeStatusVector.empty()) + if (privPathsStatusVector.empty()) return SECURITY_MANAGER_SUCCESS; auto it = privilegePathMap.begin(); - for (const auto &privilegeStatus : privilegeStatusVector) { + for (const auto &privilegeStatus : privPathsStatusVector) { for (auto &privilegePath : it->second) { if (FS::directoryStatus(privilegePath.dstPath) == 0) { @@ -882,7 +882,7 @@ int security_manager_prepare_app_candidate(void) } static inline int security_manager_setup_namespace_internal(const MountNS::PrivilegePathsMap &privilegePathMap, - const std::string &pkg_name, bool enabledSharedRO, const std::vector &privilegeStatusVector, + const std::string &pkg_name, bool enabledSharedRO, const std::vector &privPathsStatusVector, const std::string &app_label) { // mount namespace setup was made by other process when userAppsRWSharedDir is read only, we can skip it @@ -900,7 +900,7 @@ static inline int security_manager_setup_namespace_internal(const MountNS::Privi return ret; } - ret = applyPrivileges(privilegePathMap, privilegeStatusVector, app_label); + ret = applyPrivileges(privilegePathMap, privPathsStatusVector, app_label); if (ret != SECURITY_MANAGER_SUCCESS) { LogError("Failed to setup app namespace: " << security_manager_strerror(static_cast(ret))); return ret; @@ -926,10 +926,10 @@ int security_manager_prepare_app(const char *app_name) std::string appLabel, pkgName; bool enabledSharedRO; std::vector forbiddenGroups, allowedGroups; - std::vector privilegeStatusVector; + std::vector privPathsStatusVector; auto privilegePathMap = MountNS::getPrivilegePathMap(getuid()); int ret = prepareAppInitialSetupAndFetch(app_name, privilegePathMap, appLabel, pkgName, enabledSharedRO, - forbiddenGroups, allowedGroups, privilegeStatusVector); + forbiddenGroups, allowedGroups, privPathsStatusVector); if (ret != SECURITY_MANAGER_SUCCESS) { LogError("Failed to get app info for appName: " << app_name); return ret; @@ -941,7 +941,7 @@ int security_manager_prepare_app(const char *app_name) return ret; } - ret = security_manager_setup_namespace_internal(privilegePathMap, pkgName, enabledSharedRO, privilegeStatusVector, appLabel); + ret = security_manager_setup_namespace_internal(privilegePathMap, pkgName, enabledSharedRO, privPathsStatusVector, appLabel); if (ret != SECURITY_MANAGER_SUCCESS) { LogError("Unable to setup namespace for application " << app_name); return ret; diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index 76239b2..fd8e7a1 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -367,21 +367,21 @@ public: /** * Get app info (process label, package name, shared_ro flag) * - * @param[in] creds credentials of the requesting process - * @param[in] appName application identifier - * @param[in] privilegeVector privileges to query + * @param[in] creds credentials of the requesting process + * @param[in] appName application identifier + * @param[in] pathPrivVector paths related privileges to query * @param[out] label generated label * @param[out] pkgName application package name * @param[out] enabledSharedRO placeholder for check shared_ro result * @param[out] forbiddenGroups sorted vector of forbidden groups * @param[out] allowedGroups sorted vector of allowed groups - * @param[out] privilegeStatusVector results of respective privilege queries + * @param[out] pathPrivStatusVector results of respective paths related privilege queries * * @return API return code, as defined in protocols.h */ - int prepareApp(const Credentials &creds, const std::string &appName, const std::vector &privilegeVector, + int prepareApp(const Credentials &creds, const std::string &appName, const std::vector &privPathsVector, std::string &label, std::string &pkgName, bool &enabledSharedRO, - std::vector &forbiddenGroups, std::vector &allowedGroups, std::vector &privilegeStatusVector); + std::vector &forbiddenGroups, std::vector &allowedGroups, std::vector &privPathsStatusVector); private: int appInstallInitialChecks(const Credentials &creds, @@ -398,7 +398,7 @@ private: int appInstallSmackRules(app_inst_req &req, InstallHelper &ih); int appSetupNamespace(const Credentials &creds, const std::string &appProcessLabel, - const std::vector &privilegeVector, std::vector &privilegeStatusVector); + const std::vector &privPathsVector, std::vector &privPathsStatusVector); int appUninstallInitialChecks(const Credentials &creds, app_inst_req &req, diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 8be980f..2dd7bf8 100644 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -2010,7 +2010,7 @@ int ServiceImpl::getClientPrivilegeLicense( } int ServiceImpl::appSetupNamespace(const Credentials &creds, const std::string &appProcessLabel, - const std::vector &privilegeVector, std::vector &privilegeStatusVector) + const std::vector &privPathsVector, std::vector &privPathsStatusVector) { int ret; if (!authenticate(creds, PRIVILEGE_APP_NAMESPACE)) { @@ -2049,9 +2049,9 @@ int ServiceImpl::appSetupNamespace(const Credentials &creds, const std::string & return ret; } - privilegeStatusVector.reserve(privilegeVector.size()); - for (auto &privilege : privilegeVector) - privilegeStatusVector.push_back(m_cynara.check(appProcessLabel, privilege, uidStr, {})); + privPathsStatusVector.reserve(privPathsVector.size()); + for (auto &privilege : privPathsVector) + privPathsStatusVector.push_back(m_cynara.check(appProcessLabel, privilege, uidStr, {})); } catch (const PrivilegeDb::Exception::Base &e) { LogError("Error while getting shared_ro flag from database: " << e.DumpToString()); @@ -2141,9 +2141,9 @@ std::string ServiceImpl::getProcessLabel(const std::string &appName) return getAppProcessLabel(appName); } -int ServiceImpl::prepareApp(const Credentials &creds, const std::string &appName, const std::vector &privilegeVector, +int ServiceImpl::prepareApp(const Credentials &creds, const std::string &appName, const std::vector &privPathsVector, std::string &label, std::string &pkgName, bool &enabledSharedRO, - std::vector &forbiddenGroups, std::vector &allowedGroups, std::vector &privilegeStatusVector) + std::vector &forbiddenGroups, std::vector &allowedGroups, std::vector &privPathsStatusVector) { LogDebug("Requested prepareApp for application " << appName); bool isHybrid; @@ -2171,7 +2171,7 @@ int ServiceImpl::prepareApp(const Credentials &creds, const std::string &appName ret = getForbiddenAndAllowedGroups(label, allowedPrivileges, forbiddenGroups, allowedGroups); return ret != SECURITY_MANAGER_SUCCESS ? ret - : appSetupNamespace(creds, label, privilegeVector, privilegeStatusVector); + : appSetupNamespace(creds, label, privPathsVector, privPathsStatusVector); } } /* namespace SecurityManager */ diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index e367b12..478c2e3 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -503,15 +503,15 @@ void Service::prepareApp(MessageBuffer &buffer, MessageBuffer &send, const Crede { std::string appName, pkgName, label; bool enabledSharedRO; - std::vector privilegeVector; + std::vector privPathsVector; std::vector forbiddenGroups, allowedGroups; - std::vector privilegeStatusVector; - Deserialization::Deserialize(buffer, appName, privilegeVector); - int ret = serviceImpl.prepareApp(creds, appName, privilegeVector, - label, pkgName, enabledSharedRO, forbiddenGroups, allowedGroups, privilegeStatusVector); + std::vector privPathsStatusVector; + Deserialization::Deserialize(buffer, appName, privPathsVector); + int ret = serviceImpl.prepareApp(creds, appName, privPathsVector, + label, pkgName, enabledSharedRO, forbiddenGroups, allowedGroups, privPathsStatusVector); Serialization::Serialize(send, ret); if (ret == SECURITY_MANAGER_SUCCESS) - Serialization::Serialize(send, forbiddenGroups, allowedGroups, privilegeStatusVector, label, pkgName, enabledSharedRO); + Serialization::Serialize(send, forbiddenGroups, allowedGroups, privPathsStatusVector, label, pkgName, enabledSharedRO); } } // namespace SecurityManager -- 2.7.4 From c629a26bf52930da04e1f00f41f1d7af44216531 Mon Sep 17 00:00:00 2001 From: Zofia Grzelewska Date: Fri, 28 Feb 2020 17:25:45 +0100 Subject: [PATCH 09/16] Add restriction for privilege smack mapping rules Do not support rules, which are not based only on privilege or application based labels. Change-Id: Ib86cac1c8b362f8b4549148be96915a16e323e65 --- policy/privilege-smack.list | 6 ++++++ src/common/smack-rules.cpp | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/policy/privilege-smack.list b/policy/privilege-smack.list index f73816a..a95a151 100644 --- a/policy/privilege-smack.list +++ b/policy/privilege-smack.list @@ -12,5 +12,11 @@ # In such case 'priv-rules-default-template.smack' will be used. # # - lines starting with '#' or empty lines are ignored +# +# IMPORTANT NOTICE: +# This mechanism is only for special cases. Rules provided in privilege template +# will only be accepted, when they are between privilege label and application +# based labels (e.g. application process label, application read-only path label). +# Other rules will be ignored. http://tizen.org/privilege/internet System::Privilege::Internet default diff --git a/src/common/smack-rules.cpp b/src/common/smack-rules.cpp index d7963d8..469f69e 100644 --- a/src/common/smack-rules.cpp +++ b/src/common/smack-rules.cpp @@ -209,6 +209,12 @@ void SmackRules::addFromPrivTemplate( std::string object = rule[1]; std::string permissions = rule[2]; + if (subject[0] != '~' || object[0] != '~') { + LogWarning("Unsupported rule <" + << subject << " " << object << " " << permissions + << "> detected. Ignoring"); + } + strReplace(subject, SMACK_PROCESS_LABEL_TEMPLATE, appProcessLabel); strReplace(subject, SMACK_PRIVILEGE_LABEL_TEMPLATE, privilegeLabel); strReplace(object, SMACK_PROCESS_LABEL_TEMPLATE, appProcessLabel); -- 2.7.4 From 360ee5d49da64e7db10b3b0bf677b62452b61ead Mon Sep 17 00:00:00 2001 From: Zofia Grzelewska Date: Tue, 3 Mar 2020 16:10:19 +0100 Subject: [PATCH 10/16] Split smack API wrapper and rules management Split smack API wrapper (SmackAccesses) and rules generation and management (SmackRules) into separate classes. Make SmackRules a class, not a namespace, in a preparation for pre-loading of rules template files. Change-Id: I695a7cbaef404462909b80271d0775a2c725d4f3 --- src/common/CMakeLists.txt | 2 + src/common/include/service_impl.h | 24 +-- src/common/include/smack-accesses.h | 55 +++++++ src/common/include/smack-common.h | 52 +++++++ src/common/include/smack-labels.h | 41 ++++-- src/common/include/smack-rules.h | 178 ++++++++++------------- src/common/service_impl.cpp | 92 ++++++------ src/common/smack-accesses.cpp | 79 ++++++++++ src/common/smack-common.cpp | 65 +++++++++ src/common/smack-labels.cpp | 52 ++++--- src/common/smack-rules.cpp | 282 +++++++++++++++--------------------- test/CMakeLists.txt | 4 +- test/test_smack-rules.cpp | 57 ++++---- 13 files changed, 593 insertions(+), 390 deletions(-) create mode 100644 src/common/include/smack-accesses.h create mode 100644 src/common/include/smack-common.h create mode 100644 src/common/smack-accesses.cpp create mode 100644 src/common/smack-common.cpp diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 8c2f986..4c76ac9 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -61,6 +61,8 @@ SET(COMMON_SOURCES ${COMMON_PATH}/nsmount-logic.cpp ${COMMON_PATH}/privilege_db.cpp ${COMMON_PATH}/smack-labels.cpp + ${COMMON_PATH}/smack-accesses.cpp + ${COMMON_PATH}/smack-common.cpp ${COMMON_PATH}/smack-rules.cpp ${COMMON_PATH}/smack-check.cpp ${COMMON_PATH}/service_impl.cpp diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index fd8e7a1..33a3d1d 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -33,6 +33,7 @@ #include "cynara.h" #include "nsmount-logic.h" #include "security-manager.h" +#include "smack-common.h" #include "smack-rules.h" #include "protocols.h" #include "privilege_db.h" @@ -54,7 +55,7 @@ struct UninstallHelper { bool removePkg; bool removeAuthor; int authorId; - SmackRules::Labels pkgLabels; + Smack::Labels pkgLabels; std::vector removeApps; AppDefinedPrivilegesVector oldAppDefinedPrivileges; @@ -183,7 +184,7 @@ public: * * @return API return code, as defined in protocols.h */ - int getAppAllowedPrivileges(const std::string &appProcessLabel, uid_t uid, + int getAppAllowedPrivileges(const Smack::Label &appProcessLabel, uid_t uid, std::vector &allowedPrivileges); /** @@ -200,7 +201,7 @@ public: * * @return API return code, as defined in protocols.h */ - int getForbiddenAndAllowedGroups(const std::string &appProcessLabel, + int getForbiddenAndAllowedGroups(const Smack::Label &appProcessLabel, const std::vector &allowedPrivileges, std::vector &forbiddenGroups, std::vector &allowedGroups); @@ -362,7 +363,7 @@ public: * * @return generated label */ - std::string getProcessLabel(const std::string &appName); + Smack::Label getProcessLabel(const std::string &appName); /** * Get app info (process label, package name, shared_ro flag) @@ -380,7 +381,7 @@ public: * @return API return code, as defined in protocols.h */ int prepareApp(const Credentials &creds, const std::string &appName, const std::vector &privPathsVector, - std::string &label, std::string &pkgName, bool &enabledSharedRO, + Smack::Label &label, std::string &pkgName, bool &enabledSharedRO, std::vector &forbiddenGroups, std::vector &allowedGroups, std::vector &privPathsStatusVector); private: @@ -397,7 +398,7 @@ private: int appInstallSmackRules(app_inst_req &req, InstallHelper &ih); - int appSetupNamespace(const Credentials &creds, const std::string &appProcessLabel, + int appSetupNamespace(const Credentials &creds, const Smack::Label &appProcessLabel, const std::vector &privPathsVector, std::vector &privPathsStatusVector); int appUninstallInitialChecks(const Credentials &creds, @@ -426,7 +427,7 @@ private: const uid_t &uid, bool isSharedRO); - void getPkgLabels(const std::string &pkgName, SmackRules::Labels &pkgsLabels); + void getPkgLabels(const std::string &pkgName, Smack::Labels &pkgsLabels); int squashDropPrivateSharing(const std::string &ownerAppName, const std::string &targetAppName, @@ -434,16 +435,16 @@ private: int dropOnePrivateSharing(const std::string &ownerAppName, const std::string &ownerPkgName, - const SmackRules::Labels &ownerPkgLabels, + const Smack::Labels &ownerPkgLabels, const std::string &targetAppName, - const std::string &targetAppLabel, + const Smack::Label &targetAppLabel, const std::string &path); void updatePermissibleSet(uid_t uid, int type); - std::string getAppProcessLabel(const std::string &appName, const std::string &pkgName); + Smack::Label getAppProcessLabel(const std::string &appName, const std::string &pkgName); - std::string getAppProcessLabel(const std::string &appName); + Smack::Label getAppProcessLabel(const std::string &appName); bool sharingExists(const std::string &targetAppName, const std::string &path); @@ -452,6 +453,7 @@ private: int getAppDefinedPrivilegeDescription(uid_t uid, const std::string &privilege, std::string &appName, std::string &pkgName, std::string &license); Cynara m_cynara; + SmackRules m_smackRules; PrivilegeDb m_privilegeDb; CynaraAdmin m_cynaraAdmin; PrivilegeGids m_privilegeGids; diff --git a/src/common/include/smack-accesses.h b/src/common/include/smack-accesses.h new file mode 100644 index 0000000..74ed2d7 --- /dev/null +++ b/src/common/include/smack-accesses.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2014-2020 Samsung Electronics Co., Ltd. All rights reserved + * + * Contact: Rafal Krypa + * + * 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 smack-accesses.h + * @author Jacek Bukarewicz + * @author Zofia Abramowska + * @version 1.0 + * @brief Header file of a class managing smack rules + * + */ +#pragma once + +#include "smack-common.h" + +struct smack_accesses; + +namespace SecurityManager { + +class SmackAccesses +{ +public: + SmackAccesses(); + virtual ~SmackAccesses(); + + SmackAccesses(SmackAccesses &other) = delete; + SmackAccesses& operator=(const SmackAccesses &other) = delete; + + void add(const Smack::Label &subject, const Smack::Label &object, + const Smack::Permission &permissions); + void add(const Smack::Rule &rule); + void addModify(const Smack::Label &subject, const Smack::Label &object, + const Smack::Permission &allowPermissions, const Smack::Permission &denyPermissions); + + void apply() const; + void clear() const; +private: + smack_accesses *m_handle; +}; + +} // namespace SecurityManager diff --git a/src/common/include/smack-common.h b/src/common/include/smack-common.h new file mode 100644 index 0000000..254e863 --- /dev/null +++ b/src/common/include/smack-common.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2020 Samsung Electronics Co., Ltd. All rights reserved + * + * Contact: Rafal Krypa + * + * 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 smack-common.h + * @author Zofia Abramowska + * @version 1.0 + * @brief Header file of a smack utility types and functions + * + */ + +#pragma once + +#include +#include + +namespace SecurityManager { + +namespace Smack { +typedef std::string Label; +typedef std::string Permission; + +struct Rule { + Label subject; + Label object; + Permission permissions; +}; + +typedef std::vector Rules; +typedef std::vector TemplateRules; +typedef std::vector