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"
43 void ReportSuccess(picojson::object& out) {
45 out.insert(std::make_pair("status", picojson::value("success")));
48 void ReportSuccess(const picojson::value& result, picojson::object& out) {
50 out.insert(std::make_pair("status", picojson::value("success")));
51 out.insert(std::make_pair("result", result));
54 void ReportError(picojson::object& out) {
55 LoggerE("Error without error code");
56 out.insert(std::make_pair("status", picojson::value("error")));
59 void ReportError(const PlatformException& ex, picojson::object& out) {
60 LoggerE("PlatformException: %s, message: %s", ex.name().c_str(), ex.message().c_str());
61 out.insert(std::make_pair("status", picojson::value("error")));
62 out.insert(std::make_pair("error", ex.ToJSON()));
65 void ReportError(const PlatformResult& error, picojson::object* out) {
66 LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
67 error.message().c_str());
68 out->insert(std::make_pair("status", picojson::value("error")));
69 out->insert(std::make_pair("error", error.ToJSON()));
72 void PushJSONToBinary(const picojson::value& in, std::vector<uint8_t>* out) {
74 auto out_json_data = in.serialize();
75 // As binary data used in 'out' vector after a JSON can be coded as 1, 2, 4 or
76 // 8 bytes per element, there could be an error in JS layer when trying to
77 // parse element starting from 'wrong' index depending on data length. To
78 // overcome a problem we always allign the array to end with length of bytes
79 // being a multiply of 8 to keep simplicity of implementation.
80 size_t trailing_bytes = (out_json_data.size() + 4) % 8;
81 if (trailing_bytes != 0) {
82 std::string align_bytes((8 - trailing_bytes), ' ');
83 out_json_data.insert(out_json_data.end(), align_bytes.begin(), align_bytes.end());
86 size_t out_json_size = out_json_data.size();
87 auto out_json_size_vec = std::vector<uint8_t>{
88 static_cast<uint8_t>(out_json_size >> 24), static_cast<uint8_t>(out_json_size >> 16),
89 static_cast<uint8_t>(out_json_size >> 8), static_cast<uint8_t>(out_json_size)};
90 out->insert(out->end(), out_json_size_vec.begin(), out_json_size_vec.end());
91 out->insert(out->end(), out_json_data.begin(), out_json_data.end());
94 void ReportSuccessToBinary(const picojson::value& result, std::vector<uint8_t>* out) {
96 picojson::value wrapped_result{picojson::object{}};
97 picojson::object& obj = wrapped_result.get<picojson::object>();
99 ReportSuccess(result, obj);
100 PushJSONToBinary(wrapped_result, out);
103 void ReportErrorToBinary(const PlatformResult& error, std::vector<uint8_t>* out) {
104 LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
105 error.message().c_str());
106 picojson::value wrapped_result{picojson::object{}};
107 picojson::object& obj = wrapped_result.get<picojson::object>();
109 ReportError(error, &obj);
110 PushJSONToBinary(wrapped_result, out);
113 void ReportDataToBinary(const std::vector<uint8_t>& in, std::vector<uint8_t>* out) {
115 unsigned long long out_data_size = in.size();
116 auto out_data_size_vec = std::vector<uint8_t>{
117 static_cast<uint8_t>(out_data_size >> 56), static_cast<uint8_t>(out_data_size >> 48),
118 static_cast<uint8_t>(out_data_size >> 40), static_cast<uint8_t>(out_data_size >> 32),
119 static_cast<uint8_t>(out_data_size >> 24), static_cast<uint8_t>(out_data_size >> 16),
120 static_cast<uint8_t>(out_data_size >> 8), static_cast<uint8_t>(out_data_size)};
121 out->insert(out->end(), out_data_size_vec.begin(), out_data_size_vec.end());
122 out->insert(out->end(), in.begin(), in.end());
127 #ifdef PRIVILEGE_USE_DB
129 class AccessControlImpl {
131 AccessControlImpl() : initialized_(false) {
132 ScopeLogger("Privilege access checked using DB.");
134 const char* kWrtDBPath = "/opt/dbspace/.wrt.db";
135 sqlite3* db = nullptr;
137 int ret = sqlite3_open(kWrtDBPath, &db);
138 if (SQLITE_OK != ret) {
139 LoggerE("Failed to access WRT database");
144 "select name from WidgetFeature where app_id = "
145 "(select app_id from WidgetInfo where tizen_appid = ?)"
147 const std::string app_id = common::CurrentApplication::GetInstance().GetApplicationId();
148 sqlite3_stmt* stmt = nullptr;
150 ret = sqlite3_prepare_v2(db, kQuery, -1, &stmt, nullptr);
151 ret |= sqlite3_bind_text(stmt, 1, app_id.c_str(), -1, SQLITE_TRANSIENT);
154 sqlite3_finalize(stmt);
158 if (SQLITE_OK != ret) {
159 LoggerE("Failed to query WRT database");
163 while (sqlite3_step(stmt) == SQLITE_ROW) {
164 const char* privilege = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
165 SLoggerD("Granted: %s", privilege);
166 granted_privileges_.push_back(privilege);
172 ~AccessControlImpl() {
175 bool CheckAccess(const std::vector<std::string>& privileges) {
181 for (const auto& privilege : privileges) {
182 if (std::find(granted_privileges_.begin(), granted_privileges_.end(), privilege) ==
183 granted_privileges_.end()) {
193 std::vector<std::string> granted_privileges_;
196 #elif PRIVILEGE_USE_ACE
198 class AccessControlImpl {
200 AccessControlImpl() {
201 LoggerD("Privilege access checked using ACE.");
204 ~AccessControlImpl() {
207 bool CheckAccess(const std::vector<std::string>& privileges) {
214 #elif PRIVILEGE_USE_CYNARA
216 class AccessControlImpl {
218 AccessControlImpl() : cynara_(nullptr) {
219 ScopeLogger("Privilege access checked using Cynara.");
221 char* smack_label = nullptr;
229 snprintf(path, sizeof(path), "/proc/%lu/attr/current", static_cast<unsigned long>(gettid()));
230 int ret = smack_getlabel(path, &smack_label, SMACK_LABEL_ACCESS);
232 if (0 == ret && nullptr != smack_label && 0 < strlen(smack_label)) {
235 SLoggerD("uid: [%u]", uid);
236 SLoggerD("smack label: [%s]", smack_label);
238 uid_ = std::to_string(uid);
239 smack_label_ = smack_label;
241 LoggerE("Failed to get smack label");
245 ret = cynara_initialize(&cynara_, nullptr);
246 if (CYNARA_API_SUCCESS != ret) {
247 LoggerE("Failed to initialize Cynara");
252 ~AccessControlImpl() {
254 auto ret = cynara_finish(cynara_);
255 if (CYNARA_API_SUCCESS != ret) {
256 LoggerE("Failed to finalize Cynara");
262 bool CheckAccess(const std::vector<std::string>& privileges) {
264 for (const auto& privilege : privileges) {
265 if (CYNARA_API_ACCESS_ALLOWED != cynara_check(cynara_, // p_cynara
266 smack_label_.c_str(), // client
267 "", // client_session
268 uid_.c_str(), // user
269 privilege.c_str()) // privilege
283 std::string smack_label_;
288 class AccessControlImpl {
290 AccessControlImpl() {
291 LoggerD("Privilege access - deny all.");
294 bool CheckAccess(const std::vector<std::string>& privileges) {
301 class AccessControl {
303 static AccessControl& GetInstance() {
304 thread_local AccessControl instance;
308 bool CheckAccess(const std::string& privilege) {
309 return CheckAccess(std::vector<std::string>{privilege});
312 bool CheckAccess(const std::vector<std::string>& privileges) {
313 return impl_.CheckAccess(privileges);
321 AccessControlImpl impl_;
326 PlatformResult CheckAccess(const std::string& privilege) {
327 return CheckAccess(std::vector<std::string>{privilege});
330 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
334 * This lock is crucial to avoid concurrent access of static variables in this function and
335 * in AccessControlImpl class.
336 * Concurrent access is likely in web service apps. If multiple are running, each of them is
338 * a thread of the same process.
340 static std::mutex check_access_mutex;
341 const std::lock_guard<std::mutex> lock{check_access_mutex};
343 // Local cache of mapped privilege strings. This routine can be called many times, especially
344 // during application launch, generating a high overhead of retrieving mapped privileges from
345 // the underlying databases. This is especially the case since the same mappings can end up
346 // being retrieved several times.
347 using MappedPrivilegeCache = std::map<std::string, std::vector<std::string>>;
348 thread_local MappedPrivilegeCache mapped_privilege_cache;
350 std::string api_version;
351 PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
355 LoggerD("Application api version: %s", api_version.c_str());
357 for (auto input_priv : privileges) {
358 LoggerD("Input privilege: %s", input_priv.c_str());
359 GList* input_glist = nullptr;
360 GList* mapped_glist = nullptr;
363 g_list_free(input_glist);
364 g_list_free(mapped_glist);
367 std::vector<std::string>* mapped_vector_ptr = nullptr;
369 // Check if mapped privilege is in local cache first
370 MappedPrivilegeCache::const_iterator it = mapped_privilege_cache.find(input_priv);
371 if (mapped_privilege_cache.end() == it) {
372 LoggerD("Mapped privileges - need to be fetched from database");
373 // Not in cache - retrieve from underlying databases.
374 input_glist = g_list_append(input_glist, (void*)input_priv.c_str());
375 int ret = privilege_manager_get_mapped_privilege_list(
376 api_version.c_str(), PRVMGR_PACKAGE_TYPE_WRT, input_glist, &mapped_glist);
377 if (PRVMGR_ERR_NONE != ret) {
378 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get mapped privilege list");
381 // initialize empty cache vector
382 mapped_privilege_cache[input_priv] = std::vector<std::string>();
383 LoggerD("Mapped privileges:");
384 auto push_elem = [](gpointer data, gpointer user_data) -> void {
385 if (data && user_data) {
386 std::vector<std::string>* mapped_vector =
387 static_cast<std::vector<std::string>*>(user_data);
388 char* char_data = static_cast<char*>(data);
389 mapped_vector->push_back(char_data);
390 LoggerD("mapped to: %s", char_data);
393 // fill the vector with data
394 g_list_foreach(mapped_glist, push_elem, &mapped_privilege_cache[input_priv]);
395 mapped_vector_ptr = &mapped_privilege_cache[input_priv];
397 // Retrieve from local cache
398 LoggerD("Mapped privileges already in cache");
399 mapped_vector_ptr = (std::vector<std::string>*)&(it->second);
402 if (!AccessControl::GetInstance().CheckAccess(*mapped_vector_ptr)) {
403 for (const auto& mapped_priv : *mapped_vector_ptr) {
404 LoggerD("Access to privilege: %s has been denied.", mapped_priv.c_str());
406 return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
409 return PlatformResult(ErrorCode::NO_ERROR);
412 PlatformResult GetPkgApiVersion(std::string* api_version) {
415 // Local cache of API version string. This can be expensive to retrieve from
416 // underlying databases and this routine can be called many times during
417 // application launch.
419 // TODO: don't use PID to identify the application when getting
420 // API version (since Tizen 6.5 web service apps share PID)
421 static std::string cached_api_version;
422 static int cached_pid = -1;
424 char* app_id = nullptr;
425 char* pkgid = nullptr;
426 char* api_ver = nullptr;
427 app_info_h app_handle = nullptr;
428 pkgmgrinfo_pkginfo_h pkginfo_handle = nullptr;
438 app_info_destroy(app_handle);
440 if (pkginfo_handle) {
441 pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
445 pid_t pid = getpid();
446 if (cached_pid == pid) {
447 *api_version = cached_api_version; // Retrieve from local cache
449 int ret = app_manager_get_app_id(pid, &app_id);
450 if (ret != APP_MANAGER_ERROR_NONE) {
451 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app id");
454 ret = app_info_create(app_id, &app_handle);
455 if (ret != APP_MANAGER_ERROR_NONE) {
456 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app info");
459 ret = app_info_get_package(app_handle, &pkgid);
460 if ((ret != APP_MANAGER_ERROR_NONE) || (pkgid == nullptr)) {
461 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
464 ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid, getuid(), &pkginfo_handle);
465 if (ret != PMINFO_R_OK) {
466 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkginfo_h");
469 ret = pkgmgrinfo_pkginfo_get_api_version(pkginfo_handle, &api_ver);
470 if (ret != PMINFO_R_OK) {
471 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get api version");
474 cached_api_version = api_ver; // Save in local cache
477 *api_version = api_ver;
480 return PlatformResult(ErrorCode::NO_ERROR);
483 std::vector<std::string> SplitString(const std::string& str, const std::string& delim) {
484 std::vector<std::string> tokens;
485 size_t prev = 0, pos = 0;
487 pos = str.find(delim, prev);
488 if (pos == std::string::npos) pos = str.length();
489 std::string token = str.substr(prev, pos - prev);
490 if (!token.empty()) tokens.push_back(token);
491 prev = pos + delim.length();
492 } while (pos < str.length() && prev < str.length());
496 bool IsAppVersionEarlierThan(const std::string& ver) {
499 auto res = GetPkgApiVersion(&app_ver);
502 "Failed to get application version, "
503 "assuming that application version is same as platform");
506 std::vector<std::string> vec_app = SplitString(app_ver, ".");
507 std::vector<std::string> vec_ref = SplitString(ver, ".");
509 size_t length = std::min(vec_app.size(), vec_ref.size());
510 for (size_t i = 0; i < length; ++i) {
511 int num_ver = std::stoi(vec_ref[i]);
512 int num_app = std::stoi(vec_app[i]);
513 if (num_app < num_ver) {
515 } else if (num_app > num_ver) {
523 * RequestStoragePrivilegeChecker structure is used to check whether the file stating is needed.
524 * File stating is done to ensure that the application has given access to files on
525 * internal/external memory. The checking is done since Tizen 5.0 for applications, which
526 * require at least 5.0 version of Tizen.
528 struct RequestStoragePrivilegeChecker {
529 bool isStorageAccessCheckNeeded;
530 RequestStoragePrivilegeChecker() : isStorageAccessCheckNeeded(!IsAppVersionEarlierThan("5.0")) {
534 bool IsStoragePrivilegeCheckNeeded() {
535 thread_local RequestStoragePrivilegeChecker checker{};
536 return checker.isStorageAccessCheckNeeded;
539 std::string GetErrorString(int error_code) {
540 static const size_t kSize = 1024;
541 char msg[kSize] = {0};
542 strerror_r(error_code, msg, kSize);
546 int HexToInt(char c) {
547 if (c >= '0' && c <= '9') {
549 } else if (c >= 'A' && c <= 'Z') {
556 unsigned char* HexToBin(const char* hex, int size, unsigned char* bin, int bin_size) {
557 for (int i = 0; i < size - 1 && i / 2 < bin_size; i += 2) {
558 bin[i / 2] = HexToInt(hex[i]) << 4;
559 bin[i / 2] |= HexToInt(hex[i + 1]);
564 char* BinToHex(const unsigned char* bin, int size, char* hex, int hex_size) {
565 static const char* const digits = "0123456789ABCDEF";
566 for (int i = 0; i < size && i < hex_size / 2; i++) {
567 hex[i * 2] = digits[bin[i] >> 4];
568 hex[i * 2 + 1] = digits[bin[i] & 0xF];
573 bool IsPathValid(const std::string& path) {
577 * Directory dot-referencing is not allowed
579 return std::string::npos == path.find("/../") && std::string::npos == path.find("/./") &&
580 0 != path.find("./") && 0 != path.find("../") && path.length() - 2 != path.rfind("/.") &&
581 path.length() - 3 != path.rfind("/..");
584 PlatformResult CheckStorageAccess(const std::string& path) {
585 ScopeLogger("path: [%s]", path.c_str());
587 char* real_path = realpath(path.c_str(), nullptr);
591 if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
592 LoggerE("The application does not have necessary privilege to access given file: [%s]",
594 return PlatformResult(ErrorCode::SECURITY_ERR,
595 "Permission denied: accessing files requires proper storage privilege");
597 return PlatformResult(ErrorCode::NO_ERROR);
600 TizenResult CheckStorageAccessAndReturn(const std::string& path) {
601 ScopeLogger("path: [%s]", path.c_str());
603 char* real_path = realpath(path.c_str(), nullptr);
607 if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
608 LoggerE("The application does not have necessary privilege to access given file: [%s]",
610 return SecurityError("Permission denied: accessing files requires proper storage privilege");
612 return TizenSuccess();
615 PlatformResult CheckFileStatus(const std::string& path) {
620 if (stat(path.c_str(), &buf)) {
621 LoggerD("Failed to stat path: %s", path.c_str());
623 if (ENOENT == errno) {
624 return PlatformResult(ErrorCode::NOT_FOUND_ERR, "File does not exist: " + path);
625 } else if (EACCES == errno) {
626 return PlatformResult(ErrorCode::IO_ERR, "The user cannot access the file: " + path);
629 LoggerD("stat() error: %s", common::tools::GetErrorString(errno).c_str());
630 return PlatformResult(ErrorCode::UNKNOWN_ERR, "Cannot get status of the file: " + path);
633 if (!S_ISREG(buf.st_mode)) {
634 return PlatformResult(ErrorCode::NOT_FOUND_ERR,
635 "Path does not point to a regular file: " + path);
638 if (!(S_IRUSR & buf.st_mode)) {
639 return PlatformResult(ErrorCode::IO_ERR, "The user cannot read the file: " + path);
642 return PlatformResult(ErrorCode::NO_ERROR);
645 PlatformResult CheckFileAvailability(const std::string& path) {
648 if (!IsPathValid(path)) {
649 return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid path: " + path);
652 return CheckFileStatus(path);
655 void PrintDeprecationWarningFor(const char* className, const char* replacement) {
656 if (nullptr == replacement) {
657 LoggerW("DEPRECATION WARNING: %s is deprecated and using it is not recommended.", className);
660 "DEPRECATION WARNING: %s is deprecated and using it is not recommended. Try using %s "
662 className, replacement);
666 std::string ConvertToLowerCase(const std::string& input_string) {
667 std::string output_string = input_string;
668 std::transform(output_string.begin(), output_string.end(), output_string.begin(), ::tolower);
669 return output_string;
672 std::string ltrim(const std::string& s) {
675 auto pos = str.find_first_not_of(" ");
680 std::string ConvertUriToPath(const std::string& str) {
681 const std::string URI_PREFIX = "file://";
683 std::string path = ltrim(str);
684 std::string prefix = path.substr(0, URI_PREFIX.size());
686 if (prefix == URI_PREFIX) {
687 return path.substr(URI_PREFIX.size());
694 } // namespace common