2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "common/tools.h"
19 #include <app_manager.h>
20 #include <pkgmgr-info.h>
21 #include <privilegemgr/privilege_manager.h>
25 #ifdef PRIVILEGE_USE_DB
27 #include "common/current_application.h"
28 #elif PRIVILEGE_USE_ACE
29 //#include <privilege_checker.h>
30 #elif PRIVILEGE_USE_CYNARA
33 #include <cynara/cynara-client.h>
34 #include <sys/smack.h>
37 #include "common/logger.h"
38 #include "common/scope_exit.h"
39 #include "common/current_application.h"
44 void ReportSuccess(picojson::object& out) {
46 out.insert(std::make_pair("status", picojson::value("success")));
49 void ReportSuccess(const picojson::value& result, picojson::object& out) {
51 out.insert(std::make_pair("status", picojson::value("success")));
52 out.insert(std::make_pair("result", result));
55 void ReportError(picojson::object& out) {
56 LoggerE("Error without error code");
57 out.insert(std::make_pair("status", picojson::value("error")));
60 void ReportError(const PlatformException& ex, picojson::object& out) {
61 LoggerE("PlatformException: %s, message: %s", ex.name().c_str(), ex.message().c_str());
62 out.insert(std::make_pair("status", picojson::value("error")));
63 out.insert(std::make_pair("error", ex.ToJSON()));
66 void ReportError(const PlatformResult& error, picojson::object* out) {
67 LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
68 error.message().c_str());
69 out->insert(std::make_pair("status", picojson::value("error")));
70 out->insert(std::make_pair("error", error.ToJSON()));
73 void PushJSONToBinary(const picojson::value& in, std::vector<uint8_t>* out) {
75 auto out_json_data = in.serialize();
76 // As binary data used in 'out' vector after a JSON can be coded as 1, 2, 4 or
77 // 8 bytes per element, there could be an error in JS layer when trying to
78 // parse element starting from 'wrong' index depending on data length. To
79 // overcome a problem we always allign the array to end with length of bytes
80 // being a multiply of 8 to keep simplicity of implementation.
81 size_t trailing_bytes = (out_json_data.size() + 4) % 8;
82 if (trailing_bytes != 0) {
83 std::string align_bytes((8 - trailing_bytes), ' ');
84 out_json_data.insert(out_json_data.end(), align_bytes.begin(), align_bytes.end());
87 size_t out_json_size = out_json_data.size();
88 auto out_json_size_vec = std::vector<uint8_t>{
89 static_cast<uint8_t>(out_json_size >> 24), static_cast<uint8_t>(out_json_size >> 16),
90 static_cast<uint8_t>(out_json_size >> 8), static_cast<uint8_t>(out_json_size)};
91 out->insert(out->end(), out_json_size_vec.begin(), out_json_size_vec.end());
92 out->insert(out->end(), out_json_data.begin(), out_json_data.end());
95 void ReportSuccessToBinary(const picojson::value& result, std::vector<uint8_t>* out) {
97 picojson::value wrapped_result{picojson::object{}};
98 picojson::object& obj = wrapped_result.get<picojson::object>();
100 ReportSuccess(result, obj);
101 PushJSONToBinary(wrapped_result, out);
104 void ReportErrorToBinary(const PlatformResult& error, std::vector<uint8_t>* out) {
105 LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
106 error.message().c_str());
107 picojson::value wrapped_result{picojson::object{}};
108 picojson::object& obj = wrapped_result.get<picojson::object>();
110 ReportError(error, &obj);
111 PushJSONToBinary(wrapped_result, out);
114 void ReportDataToBinary(const std::vector<uint8_t>& in, std::vector<uint8_t>* out) {
116 unsigned long long out_data_size = in.size();
117 auto out_data_size_vec = std::vector<uint8_t>{
118 static_cast<uint8_t>(out_data_size >> 56), static_cast<uint8_t>(out_data_size >> 48),
119 static_cast<uint8_t>(out_data_size >> 40), static_cast<uint8_t>(out_data_size >> 32),
120 static_cast<uint8_t>(out_data_size >> 24), static_cast<uint8_t>(out_data_size >> 16),
121 static_cast<uint8_t>(out_data_size >> 8), static_cast<uint8_t>(out_data_size)};
122 out->insert(out->end(), out_data_size_vec.begin(), out_data_size_vec.end());
123 out->insert(out->end(), in.begin(), in.end());
128 #ifdef PRIVILEGE_USE_DB
130 class AccessControlImpl {
132 AccessControlImpl() : initialized_(false) {
133 ScopeLogger("Privilege access checked using DB.");
135 const char* kWrtDBPath = "/opt/dbspace/.wrt.db";
136 sqlite3* db = nullptr;
138 int ret = sqlite3_open(kWrtDBPath, &db);
139 if (SQLITE_OK != ret) {
140 LoggerE("Failed to access WRT database");
145 "select name from WidgetFeature where app_id = "
146 "(select app_id from WidgetInfo where tizen_appid = ?)"
148 const std::string app_id = common::CurrentApplication::GetInstance().GetApplicationId();
149 sqlite3_stmt* stmt = nullptr;
151 ret = sqlite3_prepare_v2(db, kQuery, -1, &stmt, nullptr);
152 ret |= sqlite3_bind_text(stmt, 1, app_id.c_str(), -1, SQLITE_TRANSIENT);
155 sqlite3_finalize(stmt);
159 if (SQLITE_OK != ret) {
160 LoggerE("Failed to query WRT database");
164 while (sqlite3_step(stmt) == SQLITE_ROW) {
165 const char* privilege = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
166 SLoggerD("Granted: %s", privilege);
167 granted_privileges_.push_back(privilege);
173 ~AccessControlImpl() {
176 bool CheckAccess(const std::vector<std::string>& privileges) {
182 for (const auto& privilege : privileges) {
183 if (std::find(granted_privileges_.begin(), granted_privileges_.end(), privilege) ==
184 granted_privileges_.end()) {
194 std::vector<std::string> granted_privileges_;
197 #elif PRIVILEGE_USE_ACE
199 class AccessControlImpl {
201 AccessControlImpl() {
202 LoggerD("Privilege access checked using ACE.");
205 ~AccessControlImpl() {
208 bool CheckAccess(const std::vector<std::string>& privileges) {
215 #elif PRIVILEGE_USE_CYNARA
217 class AccessControlImpl {
219 AccessControlImpl() : cynara_(nullptr) {
220 ScopeLogger("Privilege access checked using Cynara.");
222 char* smack_label = nullptr;
230 snprintf(path, sizeof(path), "/proc/%lu/attr/current", static_cast<unsigned long>(gettid()));
231 int ret = smack_getlabel(path, &smack_label, SMACK_LABEL_ACCESS);
233 if (0 == ret && nullptr != smack_label && 0 < strlen(smack_label)) {
236 SLoggerD("uid: [%u]", uid);
237 SLoggerD("smack label: [%s]", smack_label);
239 uid_ = std::to_string(uid);
240 smack_label_ = smack_label;
242 LoggerE("Failed to get smack label");
246 ret = cynara_initialize(&cynara_, nullptr);
247 if (CYNARA_API_SUCCESS != ret) {
248 LoggerE("Failed to initialize Cynara");
253 ~AccessControlImpl() {
255 auto ret = cynara_finish(cynara_);
256 if (CYNARA_API_SUCCESS != ret) {
257 LoggerE("Failed to finalize Cynara");
263 bool CheckAccess(const std::vector<std::string>& privileges) {
265 for (const auto& privilege : privileges) {
266 if (CYNARA_API_ACCESS_ALLOWED != cynara_check(cynara_, // p_cynara
267 smack_label_.c_str(), // client
268 "", // client_session
269 uid_.c_str(), // user
270 privilege.c_str()) // privilege
284 std::string smack_label_;
289 class AccessControlImpl {
291 AccessControlImpl() {
292 LoggerD("Privilege access - deny all.");
295 bool CheckAccess(const std::vector<std::string>& privileges) {
302 class AccessControl {
304 static AccessControl& GetInstance() {
305 thread_local AccessControl instance;
309 bool CheckAccess(const std::string& privilege) {
310 return CheckAccess(std::vector<std::string>{privilege});
313 bool CheckAccess(const std::vector<std::string>& privileges) {
314 return impl_.CheckAccess(privileges);
322 AccessControlImpl impl_;
327 PlatformResult CheckAccess(const std::string& privilege) {
328 return CheckAccess(std::vector<std::string>{privilege});
331 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
335 * This lock is crucial to avoid concurrent access of static variables in this function and
336 * in AccessControlImpl class.
337 * Concurrent access is likely in web service apps. If multiple are running, each of them is
339 * a thread of the same process.
341 static std::mutex check_access_mutex;
342 const std::lock_guard<std::mutex> lock{check_access_mutex};
344 // Local cache of mapped privilege strings. This routine can be called many times, especially
345 // during application launch, generating a high overhead of retrieving mapped privileges from
346 // the underlying databases. This is especially the case since the same mappings can end up
347 // being retrieved several times.
348 using MappedPrivilegeCache = std::map<std::string, std::vector<std::string>>;
349 thread_local MappedPrivilegeCache mapped_privilege_cache;
351 std::string api_version;
352 PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
356 LoggerD("Application api version: %s", api_version.c_str());
358 for (auto input_priv : privileges) {
359 LoggerD("Input privilege: %s", input_priv.c_str());
360 GList* input_glist = nullptr;
361 GList* mapped_glist = nullptr;
364 g_list_free(input_glist);
365 g_list_free(mapped_glist);
368 std::vector<std::string>* mapped_vector_ptr = nullptr;
370 // Check if mapped privilege is in local cache first
371 MappedPrivilegeCache::const_iterator it = mapped_privilege_cache.find(input_priv);
372 if (mapped_privilege_cache.end() == it) {
373 LoggerD("Mapped privileges - need to be fetched from database");
374 // Not in cache - retrieve from underlying databases.
375 input_glist = g_list_append(input_glist, (void*)input_priv.c_str());
376 int ret = privilege_manager_get_mapped_privilege_list(
377 api_version.c_str(), PRVMGR_PACKAGE_TYPE_WRT, input_glist, &mapped_glist);
378 if (PRVMGR_ERR_NONE != ret) {
379 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get mapped privilege list");
382 // initialize empty cache vector
383 mapped_privilege_cache[input_priv] = std::vector<std::string>();
384 LoggerD("Mapped privileges:");
385 auto push_elem = [](gpointer data, gpointer user_data) -> void {
386 if (data && user_data) {
387 std::vector<std::string>* mapped_vector =
388 static_cast<std::vector<std::string>*>(user_data);
389 char* char_data = static_cast<char*>(data);
390 mapped_vector->push_back(char_data);
391 LoggerD("mapped to: %s", char_data);
394 // fill the vector with data
395 g_list_foreach(mapped_glist, push_elem, &mapped_privilege_cache[input_priv]);
396 mapped_vector_ptr = &mapped_privilege_cache[input_priv];
398 // Retrieve from local cache
399 LoggerD("Mapped privileges already in cache");
400 mapped_vector_ptr = (std::vector<std::string>*)&(it->second);
403 if (!AccessControl::GetInstance().CheckAccess(*mapped_vector_ptr)) {
404 for (const auto& mapped_priv : *mapped_vector_ptr) {
405 LoggerD("Access to privilege: %s has been denied.", mapped_priv.c_str());
407 return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
410 return PlatformResult(ErrorCode::NO_ERROR);
413 PlatformResult GetPkgApiVersion(std::string* api_version) {
416 // Local cache of API version string. This can be expensive to retrieve from
417 // underlying databases and this routine can be called many times during
418 // application launch.
420 // TODO: don't use PID to identify the application when getting
421 // API version (since Tizen 6.5 web service apps share PID)
422 static std::string cached_api_version;
423 static int cached_pid = -1;
425 char* api_ver = nullptr;
426 pkgmgrinfo_pkginfo_h pkginfo_handle = nullptr;
429 if (pkginfo_handle) {
430 pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
434 pid_t pid = getpid();
435 if (cached_pid == pid) {
436 *api_version = cached_api_version; // Retrieve from local cache
438 std::string pkgid =common::CurrentApplication::GetInstance().GetPackageId();
440 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
443 int ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid.c_str(), getuid(), &pkginfo_handle);
444 if (ret != PMINFO_R_OK) {
445 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkginfo_h");
448 ret = pkgmgrinfo_pkginfo_get_api_version(pkginfo_handle, &api_ver);
449 if (ret != PMINFO_R_OK) {
450 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get api version");
453 cached_api_version = api_ver; // Save in local cache
456 *api_version = api_ver;
459 return PlatformResult(ErrorCode::NO_ERROR);
462 std::vector<std::string> SplitString(const std::string& str, const std::string& delim) {
463 std::vector<std::string> tokens;
464 size_t prev = 0, pos = 0;
466 pos = str.find(delim, prev);
467 if (pos == std::string::npos) pos = str.length();
468 std::string token = str.substr(prev, pos - prev);
469 if (!token.empty()) tokens.push_back(token);
470 prev = pos + delim.length();
471 } while (pos < str.length() && prev < str.length());
475 bool IsAppVersionEarlierThan(const std::string& ver) {
478 auto res = GetPkgApiVersion(&app_ver);
481 "Failed to get application version, "
482 "assuming that application version is same as platform");
485 std::vector<std::string> vec_app = SplitString(app_ver, ".");
486 std::vector<std::string> vec_ref = SplitString(ver, ".");
488 size_t length = std::min(vec_app.size(), vec_ref.size());
489 for (size_t i = 0; i < length; ++i) {
490 int num_ver = std::stoi(vec_ref[i]);
491 int num_app = std::stoi(vec_app[i]);
492 if (num_app < num_ver) {
494 } else if (num_app > num_ver) {
502 * RequestStoragePrivilegeChecker structure is used to check whether the file stating is needed.
503 * File stating is done to ensure that the application has given access to files on
504 * internal/external memory. The checking is done since Tizen 5.0 for applications, which
505 * require at least 5.0 version of Tizen.
507 struct RequestStoragePrivilegeChecker {
508 bool isStorageAccessCheckNeeded;
509 RequestStoragePrivilegeChecker() : isStorageAccessCheckNeeded(!IsAppVersionEarlierThan("5.0")) {
513 bool IsStoragePrivilegeCheckNeeded() {
514 thread_local RequestStoragePrivilegeChecker checker{};
515 return checker.isStorageAccessCheckNeeded;
518 std::string GetErrorString(int error_code) {
519 static const size_t kSize = 1024;
520 char msg[kSize] = {0};
521 strerror_r(error_code, msg, kSize);
525 int HexToInt(char c) {
526 if (c >= '0' && c <= '9') {
528 } else if (c >= 'A' && c <= 'Z') {
535 unsigned char* HexToBin(const char* hex, int size, unsigned char* bin, int bin_size) {
536 for (int i = 0; i < size - 1 && i / 2 < bin_size; i += 2) {
537 bin[i / 2] = HexToInt(hex[i]) << 4;
538 bin[i / 2] |= HexToInt(hex[i + 1]);
543 char* BinToHex(const unsigned char* bin, int size, char* hex, int hex_size) {
544 static const char* const digits = "0123456789ABCDEF";
545 for (int i = 0; i < size && i < hex_size / 2; i++) {
546 hex[i * 2] = digits[bin[i] >> 4];
547 hex[i * 2 + 1] = digits[bin[i] & 0xF];
552 bool IsPathValid(const std::string& path) {
556 * Directory dot-referencing is not allowed
558 return std::string::npos == path.find("/../") && std::string::npos == path.find("/./") &&
559 0 != path.find("./") && 0 != path.find("../") && path.length() - 2 != path.rfind("/.") &&
560 path.length() - 3 != path.rfind("/..");
563 PlatformResult CheckStorageAccess(const std::string& path) {
564 ScopeLogger("path: [%s]", path.c_str());
566 char* real_path = realpath(path.c_str(), nullptr);
570 if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
571 LoggerE("The application does not have necessary privilege to access given file: [%s]",
573 return PlatformResult(ErrorCode::SECURITY_ERR,
574 "Permission denied: accessing files requires proper storage privilege");
576 return PlatformResult(ErrorCode::NO_ERROR);
579 TizenResult CheckStorageAccessAndReturn(const std::string& path) {
580 ScopeLogger("path: [%s]", path.c_str());
582 char* real_path = realpath(path.c_str(), nullptr);
586 if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
587 LoggerE("The application does not have necessary privilege to access given file: [%s]",
589 return SecurityError("Permission denied: accessing files requires proper storage privilege");
591 return TizenSuccess();
594 PlatformResult CheckFileStatus(const std::string& path) {
599 if (stat(path.c_str(), &buf)) {
600 LoggerD("Failed to stat path: %s", path.c_str());
602 if (ENOENT == errno) {
603 return PlatformResult(ErrorCode::NOT_FOUND_ERR, "File does not exist: " + path);
604 } else if (EACCES == errno) {
605 return PlatformResult(ErrorCode::IO_ERR, "The user cannot access the file: " + path);
608 LoggerD("stat() error: %s", common::tools::GetErrorString(errno).c_str());
609 return PlatformResult(ErrorCode::UNKNOWN_ERR, "Cannot get status of the file: " + path);
612 if (!S_ISREG(buf.st_mode)) {
613 return PlatformResult(ErrorCode::NOT_FOUND_ERR,
614 "Path does not point to a regular file: " + path);
617 if (!(S_IRUSR & buf.st_mode)) {
618 return PlatformResult(ErrorCode::IO_ERR, "The user cannot read the file: " + path);
621 return PlatformResult(ErrorCode::NO_ERROR);
624 PlatformResult CheckFileAvailability(const std::string& path) {
627 if (!IsPathValid(path)) {
628 return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid path: " + path);
631 return CheckFileStatus(path);
634 void PrintDeprecationWarningFor(const char* className, const char* replacement) {
635 if (nullptr == replacement) {
636 LoggerW("DEPRECATION WARNING: %s is deprecated and using it is not recommended.", className);
639 "DEPRECATION WARNING: %s is deprecated and using it is not recommended. Try using %s "
641 className, replacement);
645 std::string ConvertToLowerCase(const std::string& input_string) {
646 std::string output_string = input_string;
647 std::transform(output_string.begin(), output_string.end(), output_string.begin(), ::tolower);
648 return output_string;
651 std::string ltrim(const std::string& s) {
654 auto pos = str.find_first_not_of(" ");
659 std::string ConvertUriToPath(const std::string& str) {
660 const std::string URI_PREFIX = "file://";
662 std::string path = ltrim(str);
663 std::string prefix = path.substr(0, URI_PREFIX.size());
665 if (prefix == URI_PREFIX) {
666 return path.substr(URI_PREFIX.size());
673 } // namespace common