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>
24 #ifdef PRIVILEGE_USE_DB
26 #include "common/current_application.h"
27 #elif PRIVILEGE_USE_ACE
28 //#include <privilege_checker.h>
29 #elif PRIVILEGE_USE_CYNARA
32 #include <cynara/cynara-client.h>
33 #include <sys/smack.h>
36 #include "common/logger.h"
37 #include "common/scope_exit.h"
42 void ReportSuccess(picojson::object& out) {
44 out.insert(std::make_pair("status", picojson::value("success")));
47 void ReportSuccess(const picojson::value& result, picojson::object& out) {
49 out.insert(std::make_pair("status", picojson::value("success")));
50 out.insert(std::make_pair("result", result));
53 void ReportError(picojson::object& out) {
54 LoggerE("Error without error code");
55 out.insert(std::make_pair("status", picojson::value("error")));
58 void ReportError(const PlatformException& ex, picojson::object& out) {
59 LoggerE("PlatformException: %s, message: %s", ex.name().c_str(), ex.message().c_str());
60 out.insert(std::make_pair("status", picojson::value("error")));
61 out.insert(std::make_pair("error", ex.ToJSON()));
64 void ReportError(const PlatformResult& error, picojson::object* out) {
65 LoggerE("PlatformResult: %d, message: %s", static_cast<int>(error.error_code()),
66 error.message().c_str());
67 out->insert(std::make_pair("status", picojson::value("error")));
68 out->insert(std::make_pair("error", error.ToJSON()));
73 #ifdef PRIVILEGE_USE_DB
75 class AccessControlImpl {
77 AccessControlImpl() : initialized_(false) {
78 ScopeLogger("Privilege access checked using DB.");
80 const char* kWrtDBPath = "/opt/dbspace/.wrt.db";
81 sqlite3* db = nullptr;
83 int ret = sqlite3_open(kWrtDBPath, &db);
84 if (SQLITE_OK != ret) {
85 LoggerE("Failed to access WRT database");
90 "select name from WidgetFeature where app_id = "
91 "(select app_id from WidgetInfo where tizen_appid = ?)"
93 const std::string app_id = common::CurrentApplication::GetInstance().GetApplicationId();
94 sqlite3_stmt* stmt = nullptr;
96 ret = sqlite3_prepare_v2(db, kQuery, -1, &stmt, nullptr);
97 ret |= sqlite3_bind_text(stmt, 1, app_id.c_str(), -1, SQLITE_TRANSIENT);
100 sqlite3_finalize(stmt);
104 if (SQLITE_OK != ret) {
105 LoggerE("Failed to query WRT database");
109 while (sqlite3_step(stmt) == SQLITE_ROW) {
110 const char* privilege = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
111 SLoggerD("Granted: %s", privilege);
112 granted_privileges_.push_back(privilege);
118 ~AccessControlImpl() {
121 bool CheckAccess(const std::vector<std::string>& privileges) {
127 for (const auto& privilege : privileges) {
128 if (std::find(granted_privileges_.begin(), granted_privileges_.end(), privilege) ==
129 granted_privileges_.end()) {
139 std::vector<std::string> granted_privileges_;
142 #elif PRIVILEGE_USE_ACE
144 class AccessControlImpl {
146 AccessControlImpl() {
147 LoggerD("Privilege access checked using ACE.");
150 ~AccessControlImpl() {
153 bool CheckAccess(const std::vector<std::string>& privileges) {
160 #elif PRIVILEGE_USE_CYNARA
162 class AccessControlImpl {
164 AccessControlImpl() : cynara_(nullptr) {
165 ScopeLogger("Privilege access checked using Cynara.");
167 char* smack_label = nullptr;
171 snprintf(path, sizeof(path), "/proc/%lu/attr/current", static_cast<unsigned long>(gettid()));
172 int ret = smack_getlabel(path, &smack_label, SMACK_LABEL_ACCESS);
174 if (0 == ret && nullptr != smack_label && 0 < strlen(smack_label)) {
177 SLoggerD("uid: [%u]", uid);
178 SLoggerD("smack label: [%s]", smack_label);
180 uid_ = std::to_string(uid);
181 smack_label_ = smack_label;
185 LoggerE("Failed to get smack label");
189 ret = cynara_initialize(&cynara_, nullptr);
190 if (CYNARA_API_SUCCESS != ret) {
191 LoggerE("Failed to initialize Cynara");
196 ~AccessControlImpl() {
198 auto ret = cynara_finish(cynara_);
199 if (CYNARA_API_SUCCESS != ret) {
200 LoggerE("Failed to finalize Cynara");
206 bool CheckAccess(const std::vector<std::string>& privileges) {
208 for (const auto& privilege : privileges) {
209 if (CYNARA_API_ACCESS_ALLOWED != cynara_check(cynara_, // p_cynara
210 smack_label_.c_str(), // client
211 "", // client_session
212 uid_.c_str(), // user
213 privilege.c_str()) // privilege
227 std::string smack_label_;
232 class AccessControlImpl {
234 AccessControlImpl() {
235 LoggerD("Privilege access - deny all.");
238 bool CheckAccess(const std::vector<std::string>& privileges) {
245 class AccessControl {
247 static AccessControl& GetInstance() {
248 static AccessControl instance;
252 bool CheckAccess(const std::string& privilege) {
253 return CheckAccess(std::vector<std::string>{privilege});
256 bool CheckAccess(const std::vector<std::string>& privileges) {
257 return impl_.CheckAccess(privileges);
265 AccessControlImpl impl_;
270 PlatformResult CheckAccess(const std::string& privilege) {
271 return CheckAccess(std::vector<std::string>{privilege});
274 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
277 // Local cache of mapped privilege strings. This routine can be called many times, especially
278 // during application launch, generating a high overhead of retrieving mapped privileges from
279 // the underlying databases. This is especially the case since the same mappings can end up
280 // being retrieved several times.
281 using MappedPrivilegeCache = std::map<std::string, std::vector<std::string>>;
282 static MappedPrivilegeCache mapped_privilege_cache;
284 std::string api_version;
285 PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
289 LoggerD("Application api version: %s", api_version.c_str());
291 for (auto input_priv : privileges) {
292 LoggerD("Input privilege: %s", input_priv.c_str());
293 GList* input_glist = nullptr;
294 GList* mapped_glist = nullptr;
297 g_list_free(input_glist);
298 g_list_free(mapped_glist);
301 std::vector<std::string>* mapped_vector_ptr = nullptr;
303 // Check if mapped privilege is in local cache first
304 MappedPrivilegeCache::const_iterator it = mapped_privilege_cache.find(input_priv);
305 if (mapped_privilege_cache.end() == it) {
306 LoggerD("Mapped privileges - need to be fetched from database");
307 // Not in cache - retrieve from underlying databases.
308 input_glist = g_list_append(input_glist, (void*)input_priv.c_str());
309 int ret = privilege_manager_get_mapped_privilege_list(
310 api_version.c_str(), PRVMGR_PACKAGE_TYPE_WRT, input_glist, &mapped_glist);
311 if (PRVMGR_ERR_NONE != ret) {
312 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get mapped privilege list");
315 // initialize empty cache vector
316 mapped_privilege_cache[input_priv] = std::vector<std::string>();
317 LoggerD("Mapped privileges:");
318 auto push_elem = [](gpointer data, gpointer user_data) -> void {
319 if (data && user_data) {
320 std::vector<std::string>* mapped_vector =
321 static_cast<std::vector<std::string>*>(user_data);
322 char* char_data = static_cast<char*>(data);
323 mapped_vector->push_back(char_data);
324 LoggerD("mapped to: %s", char_data);
327 // fill the vector with data
328 g_list_foreach(mapped_glist, push_elem, &mapped_privilege_cache[input_priv]);
329 mapped_vector_ptr = &mapped_privilege_cache[input_priv];
331 // Retrieve from local cache
332 LoggerD("Mapped privileges already in cache");
333 mapped_vector_ptr = (std::vector<std::string>*)&(it->second);
336 if (!AccessControl::GetInstance().CheckAccess(*mapped_vector_ptr)) {
337 for (const auto& mapped_priv : *mapped_vector_ptr) {
338 LoggerD("Access to privilege: %s has been denied.", mapped_priv.c_str());
340 return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
343 return PlatformResult(ErrorCode::NO_ERROR);
346 PlatformResult GetPkgApiVersion(std::string* api_version) {
349 // Local cache of API version string. This can be expensive to retrieve from
350 // underlying databases and this routine can be called many times during
351 // application launch.
352 static std::string cached_api_version;
353 static int cached_pid = -1;
355 char* app_id = nullptr;
356 char* pkgid = nullptr;
357 char* api_ver = nullptr;
358 app_info_h app_handle = nullptr;
359 pkgmgrinfo_pkginfo_h pkginfo_handle = nullptr;
369 app_info_destroy(app_handle);
371 if (pkginfo_handle) {
372 pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
376 pid_t pid = getpid();
377 if (cached_pid == pid) {
378 *api_version = cached_api_version; // Retrieve from local cache
380 int ret = app_manager_get_app_id(pid, &app_id);
381 if (ret != APP_MANAGER_ERROR_NONE) {
382 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app id");
385 ret = app_info_create(app_id, &app_handle);
386 if (ret != APP_MANAGER_ERROR_NONE) {
387 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app info");
390 ret = app_info_get_package(app_handle, &pkgid);
391 if ((ret != APP_MANAGER_ERROR_NONE) || (pkgid == nullptr)) {
392 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
395 ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid, getuid(), &pkginfo_handle);
396 if (ret != PMINFO_R_OK) {
397 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkginfo_h");
400 ret = pkgmgrinfo_pkginfo_get_api_version(pkginfo_handle, &api_ver);
401 if (ret != PMINFO_R_OK) {
402 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get api version");
405 cached_api_version = api_ver; // Save in local cache
408 *api_version = api_ver;
411 return PlatformResult(ErrorCode::NO_ERROR);
414 std::vector<std::string> SplitString(const std::string& str, const std::string& delim) {
415 std::vector<std::string> tokens;
416 size_t prev = 0, pos = 0;
418 pos = str.find(delim, prev);
419 if (pos == std::string::npos) pos = str.length();
420 std::string token = str.substr(prev, pos - prev);
421 if (!token.empty()) tokens.push_back(token);
422 prev = pos + delim.length();
423 } while (pos < str.length() && prev < str.length());
427 bool IsAppVersionEarlierThan(const std::string& ver) {
430 auto res = GetPkgApiVersion(&app_ver);
433 "Failed to get application version, "
434 "assuming that application version is same as platform");
437 std::vector<std::string> vec_app = SplitString(app_ver, ".");
438 std::vector<std::string> vec_ref = SplitString(ver, ".");
440 size_t length = std::min(vec_app.size(), vec_ref.size());
441 for (size_t i = 0; i < length; ++i) {
442 int num_ver = std::stoi(vec_ref[i]);
443 int num_app = std::stoi(vec_app[i]);
444 if (num_app < num_ver) {
446 } else if (num_app > num_ver) {
454 * RequestStoragePrivilegeChecker structure is used to check whether the file stating is needed.
455 * File stating is done to ensure that the application has given access to files on
456 * internal/external memory. The checking is done since Tizen 5.0 for applications, which
457 * require at least 5.0 version of Tizen.
459 struct RequestStoragePrivilegeChecker {
460 bool isStorageAccessCheckNeeded;
461 RequestStoragePrivilegeChecker() : isStorageAccessCheckNeeded(!IsAppVersionEarlierThan("5.0")) {
465 bool IsStoragePrivilegeCheckNeeded() {
466 static RequestStoragePrivilegeChecker checker{};
467 return checker.isStorageAccessCheckNeeded;
470 std::string GetErrorString(int error_code) {
471 static const size_t kSize = 1024;
472 char msg[kSize] = {0};
473 strerror_r(error_code, msg, kSize);
477 int HexToInt(char c) {
478 if (c >= '0' && c <= '9') {
480 } else if (c >= 'A' && c <= 'Z') {
487 unsigned char* HexToBin(const char* hex, int size, unsigned char* bin, int bin_size) {
488 for (int i = 0; i < size - 1 && i / 2 < bin_size; i += 2) {
489 bin[i / 2] = HexToInt(hex[i]) << 4;
490 bin[i / 2] |= HexToInt(hex[i + 1]);
495 char* BinToHex(const unsigned char* bin, int size, char* hex, int hex_size) {
496 static const char* const digits = "0123456789ABCDEF";
497 for (int i = 0; i < size && i < hex_size / 2; i++) {
498 hex[i * 2] = digits[bin[i] >> 4];
499 hex[i * 2 + 1] = digits[bin[i] & 0xF];
504 bool IsPathValid(const std::string& path) {
508 * Directory dot-referencing is not allowed
510 return std::string::npos == path.find("/../") && std::string::npos == path.find("/./") &&
511 0 != path.find("./") && 0 != path.find("../") && path.length() - 2 != path.rfind("/.") &&
512 path.length() - 3 != path.rfind("/..");
515 PlatformResult CheckStorageAccess(const std::string& path) {
516 ScopeLogger("path: [%s]", path.c_str());
518 char* real_path = realpath(path.c_str(), nullptr);
522 if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
523 LoggerE("The application does not have necessary privilege to access given file: [%s]",
525 return PlatformResult(ErrorCode::SECURITY_ERR,
526 "Permission denied: accessing files requires proper storage privilege");
528 return PlatformResult(ErrorCode::NO_ERROR);
531 TizenResult CheckStorageAccessAndReturn(const std::string& path) {
532 ScopeLogger("path: [%s]", path.c_str());
534 char* real_path = realpath(path.c_str(), nullptr);
538 if ((nullptr == real_path || -1 == stat(real_path, &buf)) && (EACCES == errno)) {
539 LoggerE("The application does not have necessary privilege to access given file: [%s]",
541 return SecurityError("Permission denied: accessing files requires proper storage privilege");
543 return TizenSuccess();
546 PlatformResult CheckFileStatus(const std::string& path) {
551 if (stat(path.c_str(), &buf)) {
552 LoggerD("Failed to stat path: %s", path.c_str());
554 if (ENOENT == errno) {
555 return PlatformResult(ErrorCode::NOT_FOUND_ERR, "File does not exist: " + path);
556 } else if (EACCES == errno) {
557 return PlatformResult(ErrorCode::IO_ERR, "The user cannot access the file: " + path);
560 LoggerD("stat() error: %s", common::tools::GetErrorString(errno).c_str());
561 return PlatformResult(ErrorCode::UNKNOWN_ERR, "Cannot get status of the file: " + path);
564 if (!S_ISREG(buf.st_mode)) {
565 return PlatformResult(ErrorCode::NOT_FOUND_ERR,
566 "Path does not point to a regular file: " + path);
569 if (!(S_IRUSR & buf.st_mode)) {
570 return PlatformResult(ErrorCode::IO_ERR, "The user cannot read the file: " + path);
573 return PlatformResult(ErrorCode::NO_ERROR);
576 PlatformResult CheckFileAvailability(const std::string& path) {
579 if (!IsPathValid(path)) {
580 return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid path: " + path);
583 return CheckFileStatus(path);
586 void PrintDeprecationWarningFor(const char* className, const char* replacement) {
587 if (nullptr == replacement) {
588 LoggerW("DEPRECATION WARNING: %s is deprecated and using it is not recommended.", className);
591 "DEPRECATION WARNING: %s is deprecated and using it is not recommended. Try using %s "
593 className, replacement);
597 std::string ConvertToLowerCase(const std::string& input_string) {
598 std::string output_string = input_string;
599 std::transform(output_string.begin(), output_string.end(), output_string.begin(), ::tolower);
600 return output_string;
603 std::string ltrim(const std::string& s) {
606 auto pos = str.find_first_not_of(" ");
611 std::string ConvertUriToPath(const std::string& str) {
612 const std::string URI_PREFIX = "file://";
614 std::string path = ltrim(str);
615 std::string prefix = path.substr(0, URI_PREFIX.size());
617 if (prefix == URI_PREFIX) {
618 return path.substr(URI_PREFIX.size());
625 } // namespace common