From 5ae6f2040e622344d771914f5cad7b0c376dd8b0 Mon Sep 17 00:00:00 2001 From: Changgyu Choi Date: Tue, 27 Apr 2021 11:16:38 +0900 Subject: [PATCH] Add to check Privilege When the server receives requests, it checks privilege asynchronously. Only requests that habe passed the cynara check will be processed. Change-Id: I675f527a4366826fab4c68c1ce1abd06e08b394c Signed-off-by: Changgyu Choi --- CMakeLists.txt | 64 ++++++++------- packaging/pkgmgr-info.spec | 2 + src/common/socket/abstract_socket.cc | 33 ++++---- src/common/socket/abstract_socket.hh | 4 + src/common/socket/data_socket.cc | 4 +- src/server/pkg_request.cc | 8 ++ src/server/pkg_request.hh | 2 + src/server/runner.cc | 151 ++++++++++++++++++++++++++++++++++- src/server/runner.hh | 2 +- src/server/worker_thread.cc | 4 - 10 files changed, 219 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9943d02..fe98ba5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,43 +24,45 @@ INCLUDE_DIRECTORIES( ### Required packages INCLUDE(FindPkgConfig) - -pkg_check_modules(libpkgs REQUIRED - glib-2.0 - gio-2.0 - gio-unix-2.0 - dlog - vconf - libtzplatform-config - libsmack - bundle - capi-system-info - minizip - libsystemd - parcel +pkg_check_modules(pkgs REQUIRED + glib-2.0 + gio-2.0 + gio-unix-2.0 + dlog + vconf + sqlite3 + libxml-2.0 + libtzplatform-config + libsmack + bundle + cynara-client + cynara-session + cynara-creds-socket ) - -FOREACH(flag ${libpkgs_CFLAGS}) +FOREACH(flag ${pkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) -pkg_check_modules(libpkgs_server REQUIRED - glib-2.0 - gio-2.0 - gio-unix-2.0 - dlog - vconf - sqlite3 - libtzplatform-config - libsmack - bundle - capi-system-info - minizip - libsystemd - parcel +pkg_check_modules(libpkgs REQUIRED + glib-2.0 + gio-2.0 + gio-unix-2.0 + dlog + vconf + sqlite3 + libtzplatform-config + libsmack + bundle + capi-system-info + minizip + libsystemd + parcel + cynara-client-async + cynara-session + cynara-creds-socket ) -FOREACH(flag ${libpkgs_server_CFLAGS}) +FOREACH(flag ${libpkgs_CFLAGS}) SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}") ENDFOREACH(flag) diff --git a/packaging/pkgmgr-info.spec b/packaging/pkgmgr-info.spec index 1668c7d..5ea4ccb 100755 --- a/packaging/pkgmgr-info.spec +++ b/packaging/pkgmgr-info.spec @@ -24,6 +24,8 @@ BuildRequires: pkgconfig(minizip) BuildRequires: pkgconfig(libsystemd) BuildRequires: pkgconfig(gmock) BuildRequires: pkgconfig(parcel) +BuildRequires: pkgconfig(cynara-client-async) +BuildRequires: pkgconfig(cynara-session) %if 0%{?gcov:1} BuildRequires: lcov diff --git a/src/common/socket/abstract_socket.cc b/src/common/socket/abstract_socket.cc index ad9d185..506bbe1 100644 --- a/src/common/socket/abstract_socket.cc +++ b/src/common/socket/abstract_socket.cc @@ -28,9 +28,9 @@ namespace pkgmgr_common { namespace socket { AbstractSocket::AbstractSocket(std::string path) - : path_(std::move(path)), fd_(-1), addr_{} {} + : path_(std::move(path)), fd_(-1), pid_(-1), uid_(-1), addr_{} {} -AbstractSocket::AbstractSocket(int fd) : fd_(fd), addr_{} {} +AbstractSocket::AbstractSocket(int fd) : fd_(fd), pid_(-1), uid_(-1), addr_{} {} AbstractSocket::~AbstractSocket() { Disconnect(); @@ -91,19 +91,9 @@ int AbstractSocket::GetFd() { return fd_; } std::string AbstractSocket::GetPath() { return path_; } -pid_t AbstractSocket::GetPID() { - int r; - struct ucred cred = {}; - socklen_t len = sizeof(cred); - - r = getsockopt(fd_, SOL_SOCKET, SO_PEERCRED, &cred, &len); - if (r < 0) { - LOGE("getsockopt has failed, errno[%d]", errno); - return -1; - } +pid_t AbstractSocket::GetPID() { return pid_; } - return cred.pid; -} +uid_t AbstractSocket::GetUID() { return uid_; } void AbstractSocket::SetOption() { int size = 2048; @@ -128,9 +118,24 @@ int AbstractSocket::Create() { addr_.sun_family = AF_UNIX; snprintf(addr_.sun_path, sizeof(addr_.sun_path), "%s", path_.c_str()); SetOption(); + GetFdInfo(); return 0; } +void AbstractSocket::GetFdInfo() { + int r; + struct ucred cred = {}; + socklen_t len = sizeof(cred); + + r = getsockopt(fd_, SOL_SOCKET, SO_PEERCRED, &cred, &len); + if (r < 0) { + LOGE("getsockopt has failed, errno[%d]", errno); + return; + } + + pid_ = cred.pid; + uid_ = cred.uid; +} void AbstractSocket::Disconnect() { if (fd_ > 0) close(fd_); diff --git a/src/common/socket/abstract_socket.hh b/src/common/socket/abstract_socket.hh index bed1ec7..fecd9ba 100644 --- a/src/common/socket/abstract_socket.hh +++ b/src/common/socket/abstract_socket.hh @@ -42,14 +42,18 @@ class EXPORT_API AbstractSocket { int GetFd(); std::string GetPath(); pid_t GetPID(); + uid_t GetUID(); protected: void SetOption(); int Create(); + void GetFdInfo(); protected: std::string path_; int fd_; + pid_t pid_; + uid_t uid_; struct sockaddr_un addr_; }; diff --git a/src/common/socket/data_socket.cc b/src/common/socket/data_socket.cc index 1799355..22d47a3 100644 --- a/src/common/socket/data_socket.cc +++ b/src/common/socket/data_socket.cc @@ -19,7 +19,9 @@ namespace pkgmgr_common { namespace socket { -DataSocket::DataSocket(int fd) : AbstractSocket(fd) {} +DataSocket::DataSocket(int fd) : AbstractSocket(fd) { + GetFdInfo(); +} } // namespace socket } // namespace pkgmgr_common diff --git a/src/server/pkg_request.cc b/src/server/pkg_request.cc index 0fc5f67..f4767dc 100644 --- a/src/server/pkg_request.cc +++ b/src/server/pkg_request.cc @@ -40,10 +40,18 @@ int PkgRequest::GetSize() { return data_size_; } +int PkgRequest::GetFd() { + return socket_->GetFd(); +} + pid_t PkgRequest::GetSenderPID() { return socket_->GetPID(); } +uid_t PkgRequest::GetSenderUID() { + return socket_->GetUID(); +} + pkgmgr_common::ReqType PkgRequest::GetRequestType() { return request_type_; } diff --git a/src/server/pkg_request.hh b/src/server/pkg_request.hh index b93a620..f22f608 100644 --- a/src/server/pkg_request.hh +++ b/src/server/pkg_request.hh @@ -36,7 +36,9 @@ class EXPORT_API PkgRequest { ~PkgRequest(); unsigned char* GetData(); int GetSize(); + int GetFd(); pid_t GetSenderPID(); + uid_t GetSenderUID(); pkgmgr_common::ReqType GetRequestType(); bool ReceiveData(); bool SendData(unsigned char* data, int size); diff --git a/src/server/runner.cc b/src/server/runner.cc index 866a91b..1dae51a 100644 --- a/src/server/runner.cc +++ b/src/server/runner.cc @@ -14,12 +14,18 @@ * limitations under the License. */ +#include +#include +#include +#include #include #include #include #include #include +#include +#include #include "pkg_request.hh" #include "runner.hh" @@ -32,14 +38,137 @@ #define LOG_TAG "PKGMGR_INFO" namespace pkgmgr_server { +namespace { +cynara_async* cynara = nullptr; +guint cynara_sid; static const std::string SOCK_PATH = "/run/pkgmgr-info-server"; +const char PRIVILEGE_PACKAGE_MANAGER_ADMIN[] = "http://tizen.org/privilege/packagemanager.admin"; +std::unordered_map> req_map; +std::unordered_map> cynara_id_map; + +/* cynara methods */ +std::vector GetPrivileges(pkgmgr_common::ReqType type) { + std::vector ret; + if (type == pkgmgr_common::SET_CERT_INFO) { + ret.emplace_back(PRIVILEGE_PACKAGE_MANAGER_ADMIN); + } else if (type == pkgmgr_common::SET_PKG_INFO) { + ret.emplace_back(PRIVILEGE_PACKAGE_MANAGER_ADMIN); + } + + return ret; +} + +void ReplyCb(cynara_check_id id, cynara_async_call_cause cause, + int resp, void* data) { + auto runner = static_cast(data); + switch (cause) { + case CYNARA_CALL_CAUSE_ANSWER: + if (resp == CYNARA_API_ACCESS_ALLOWED) { + auto it = cynara_id_map.find(id); + if (it == cynara_id_map.end()) { + LOGE("Invalid request"); + break; + } + LOGD("Allowed request"); + runner->QueueRequest(it->second); + cynara_id_map.erase(it); + } + break; + case CYNARA_CALL_CAUSE_CANCEL: + case CYNARA_CALL_CAUSE_FINISH: + case CYNARA_CALL_CAUSE_SERVICE_NOT_AVAILABLE: + default: + LOGE("Cynara: resp: not answer"); + break; + } +} + +gboolean ProcessCb(gint fd, GIOCondition cond, gpointer data) { + if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) { + cynara_sid = 0; + return G_SOURCE_REMOVE; + } + + int ret = cynara_async_process(cynara); + if (ret != CYNARA_API_SUCCESS) + LOGE("process error %d", ret); + + return G_SOURCE_CONTINUE; +} + +void StatusCb(int old_fd, int new_fd, cynara_async_status status, void* data) { + if (old_fd != -1) { + if (cynara_sid) { + g_source_remove(cynara_sid); + cynara_sid = 0; + } + } + if (new_fd != -1) { + auto cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL; + if (status == CYNARA_STATUS_FOR_RW) + cond |= G_IO_OUT; + + cynara_sid = g_unix_fd_add(new_fd, static_cast(cond), + ProcessCb, data); + } +} + +void CheckPrivilege(Runner* runner, + const std::shared_ptr& req, + const std::vector& privileges) { + if (privileges.empty()) { + LOGD("Allowed request"); + runner->QueueRequest(req); + return; + } + + int ret; + if (cynara == nullptr) { + ret = cynara_async_initialize(&cynara, NULL, StatusCb, nullptr); + if (ret != CYNARA_API_SUCCESS) { + LOGE("Failed to initialize cynara"); + return; + } + } + char* smack_label; + ret = cynara_creds_socket_get_client(req->GetFd(), CLIENT_METHOD_SMACK, + &smack_label); + if (ret != CYNARA_API_SUCCESS) { + LOGE("Failed to get smack label"); + return; + } + std::unique_ptr lblPtr(smack_label, std::free); + char* session = cynara_session_from_pid(req->GetSenderPID()); + if (session == nullptr) { + LOGE("Failed to get client session (pid:%d)", req->GetSenderPID()); + return; + } + std::unique_ptr sessPtr(session, std::free); + cynara_check_id id; + bool check = false; + + + + for (auto& priv : privileges) { + ret = cynara_async_create_request(cynara, smack_label, session, + std::to_string(req->GetSenderUID()).c_str(), priv.c_str(), + &id, ReplyCb, runner); + if (check == false) { + cynara_id_map[id] = req; + check = true; + } + } +} + +} // namespace Runner::Runner(unsigned int thread_num) { /* thread_num_ <= hardware_concurrency */ thread_num_ = std::min(thread_num, std::thread::hardware_concurrency()); server_ = std::make_unique(SOCK_PATH); + cynara_async_initialize(&cynara, nullptr, StatusCb, nullptr); thread_pool_ = std::make_unique(thread_num_); auto condition = static_cast( G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL); @@ -56,6 +185,15 @@ Runner::Runner(unsigned int thread_num) { Runner::~Runner() { g_source_remove(sid_); + if (cynara_sid) { + g_source_remove(cynara_sid); + cynara_sid = 0; + } + + if (cynara != nullptr) { + cynara_async_finish(cynara); + cynara = nullptr; + } vconf_ignore_key_changed(VCONFKEY_LANGSET, OnLanguageChange); } @@ -73,7 +211,13 @@ int Runner::OnReceiveRequest(int fd, GIOCondition cond, void* user_data) { return G_SOURCE_CONTINUE; } - runner->QueueRequest(client_fd); + auto req = std::make_shared(client_fd); + if (req->ReceiveData()) { + pkgmgr_common::ReqType type = req->GetRequestType(); + std::vector&& privileges = GetPrivileges(type); + CheckPrivilege(runner, req, privileges); + } + return G_SOURCE_CONTINUE; } @@ -87,9 +231,8 @@ void Runner::OnLanguageChange(keynode_t* key, void* user_data) { runner->thread_pool_->SetLocale(locale.get()); } -bool Runner::QueueRequest(int client_fd) { - auto req = std::make_shared(client_fd); - thread_pool_->PushQueue(req); +bool Runner::QueueRequest(std::shared_ptr req) { + thread_pool_->PushQueue(std::move(req)); return true; } diff --git a/src/server/runner.hh b/src/server/runner.hh index 678e89e..be78dca 100644 --- a/src/server/runner.hh +++ b/src/server/runner.hh @@ -37,11 +37,11 @@ class EXPORT_API Runner { public: Runner(unsigned int thread_num); ~Runner(); + bool QueueRequest(std::shared_ptr req); private: static int OnReceiveRequest(int fd, GIOCondition cond, void* user_data); static void OnLanguageChange(keynode_t* key, void* user_data); - bool QueueRequest(int client_fd); private: int sid_; diff --git a/src/server/worker_thread.cc b/src/server/worker_thread.cc index 9581534..7bbd42a 100644 --- a/src/server/worker_thread.cc +++ b/src/server/worker_thread.cc @@ -101,10 +101,6 @@ void WorkerThread::Run() { req = PopQueue(); } - if (req->ReceiveData() == false) { - LOGE("Failed to ReceiveData"); - continue; - } pkgmgr_common::ReqType type = req->GetRequestType(); LOGW("Request type(%s), pid(%d)", pkgmgr_common::ReqTypeToString(type), req->GetSenderPID()); -- 2.7.4