[Commmon] Fixed passing a pkgid
[platform/core/api/webapi-plugins.git] / src / common / tools.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "common/tools.h"
18
19 #include <app_manager.h>
20 #include <pkgmgr-info.h>
21 #include <privilegemgr/privilege_manager.h>
22 #include <sys/stat.h>
23 #include <mutex>
24
25 #ifdef PRIVILEGE_USE_DB
26 #include <sqlite3.h>
27 #include "common/current_application.h"
28 #elif PRIVILEGE_USE_ACE
29 //#include <privilege_checker.h>
30 #elif PRIVILEGE_USE_CYNARA
31 #include <unistd.h>
32
33 #include <cynara/cynara-client.h>
34 #include <sys/smack.h>
35 #endif
36
37 #include "common/logger.h"
38 #include "common/scope_exit.h"
39 #include "common/current_application.h"
40
41 namespace common {
42 namespace tools {
43
44 void ReportSuccess(picojson::object& out) {
45   ScopeLogger();
46   out.insert(std::make_pair("status", picojson::value("success")));
47 }
48
49 void ReportSuccess(const picojson::value& result, picojson::object& out) {
50   ScopeLogger();
51   out.insert(std::make_pair("status", picojson::value("success")));
52   out.insert(std::make_pair("result", result));
53 }
54
55 void ReportError(picojson::object& out) {
56   LoggerE("Error without error code");
57   out.insert(std::make_pair("status", picojson::value("error")));
58 }
59
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()));
64 }
65
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()));
71 }
72
73 void PushJSONToBinary(const picojson::value& in, std::vector<uint8_t>* out) {
74   ScopeLogger();
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());
85   }
86
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());
93 }
94
95 void ReportSuccessToBinary(const picojson::value& result, std::vector<uint8_t>* out) {
96   ScopeLogger();
97   picojson::value wrapped_result{picojson::object{}};
98   picojson::object& obj = wrapped_result.get<picojson::object>();
99
100   ReportSuccess(result, obj);
101   PushJSONToBinary(wrapped_result, out);
102 }
103
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>();
109
110   ReportError(error, &obj);
111   PushJSONToBinary(wrapped_result, out);
112 }
113
114 void ReportDataToBinary(const std::vector<uint8_t>& in, std::vector<uint8_t>* out) {
115   ScopeLogger();
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());
124 }
125
126 namespace {
127
128 #ifdef PRIVILEGE_USE_DB
129
130 class AccessControlImpl {
131  public:
132   AccessControlImpl() : initialized_(false) {
133     ScopeLogger("Privilege access checked using DB.");
134
135     const char* kWrtDBPath = "/opt/dbspace/.wrt.db";
136     sqlite3* db = nullptr;
137
138     int ret = sqlite3_open(kWrtDBPath, &db);
139     if (SQLITE_OK != ret) {
140       LoggerE("Failed to access WRT database");
141       return;
142     }
143
144     const char* kQuery =
145         "select name from WidgetFeature where app_id = "
146         "(select app_id from WidgetInfo where tizen_appid = ?)"
147         " and rejected = 0";
148     const std::string app_id = common::CurrentApplication::GetInstance().GetApplicationId();
149     sqlite3_stmt* stmt = nullptr;
150
151     ret = sqlite3_prepare_v2(db, kQuery, -1, &stmt, nullptr);
152     ret |= sqlite3_bind_text(stmt, 1, app_id.c_str(), -1, SQLITE_TRANSIENT);
153
154     SCOPE_EXIT {
155       sqlite3_finalize(stmt);
156       sqlite3_close(db);
157     };
158
159     if (SQLITE_OK != ret) {
160       LoggerE("Failed to query WRT database");
161       return;
162     }
163
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);
168     }
169
170     initialized_ = true;
171   }
172
173   ~AccessControlImpl() {
174   }
175
176   bool CheckAccess(const std::vector<std::string>& privileges) {
177     ScopeLogger();
178     if (!initialized_) {
179       return false;
180     }
181
182     for (const auto& privilege : privileges) {
183       if (std::find(granted_privileges_.begin(), granted_privileges_.end(), privilege) ==
184           granted_privileges_.end()) {
185         return false;
186       }
187     }
188
189     return true;
190   }
191
192  private:
193   bool initialized_;
194   std::vector<std::string> granted_privileges_;
195 };
196
197 #elif PRIVILEGE_USE_ACE
198
199 class AccessControlImpl {
200  public:
201   AccessControlImpl() {
202     LoggerD("Privilege access checked using ACE.");
203   }
204
205   ~AccessControlImpl() {
206   }
207
208   bool CheckAccess(const std::vector<std::string>& privileges) {
209     ScopeLogger();
210
211     return true;
212   }
213 };
214
215 #elif PRIVILEGE_USE_CYNARA
216
217 class AccessControlImpl {
218  public:
219   AccessControlImpl() : cynara_(nullptr) {
220     ScopeLogger("Privilege access checked using Cynara.");
221
222     char* smack_label = nullptr;
223     SCOPE_EXIT {
224       free(smack_label);
225     };
226
227     char path[1024] = {
228         0,
229     };
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);
232
233     if (0 == ret && nullptr != smack_label && 0 < strlen(smack_label)) {
234       auto uid = getuid();
235
236       SLoggerD("uid: [%u]", uid);
237       SLoggerD("smack label: [%s]", smack_label);
238
239       uid_ = std::to_string(uid);
240       smack_label_ = smack_label;
241     } else {
242       LoggerE("Failed to get smack label");
243       return;
244     }
245
246     ret = cynara_initialize(&cynara_, nullptr);
247     if (CYNARA_API_SUCCESS != ret) {
248       LoggerE("Failed to initialize Cynara");
249       cynara_ = nullptr;
250     }
251   }
252
253   ~AccessControlImpl() {
254     if (cynara_) {
255       auto ret = cynara_finish(cynara_);
256       if (CYNARA_API_SUCCESS != ret) {
257         LoggerE("Failed to finalize Cynara");
258       }
259       cynara_ = nullptr;
260     }
261   }
262
263   bool CheckAccess(const std::vector<std::string>& privileges) {
264     if (cynara_) {
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
271             ) {
272           return false;
273         }
274       }
275       return true;
276     } else {
277       return false;
278     }
279   }
280
281  private:
282   cynara* cynara_;
283   std::string uid_;
284   std::string smack_label_;
285 };
286
287 #else
288
289 class AccessControlImpl {
290  public:
291   AccessControlImpl() {
292     LoggerD("Privilege access - deny all.");
293   }
294
295   bool CheckAccess(const std::vector<std::string>& privileges) {
296     return false;
297   }
298 };
299
300 #endif
301
302 class AccessControl {
303  public:
304   static AccessControl& GetInstance() {
305     thread_local AccessControl instance;
306     return instance;
307   }
308
309   bool CheckAccess(const std::string& privilege) {
310     return CheckAccess(std::vector<std::string>{privilege});
311   }
312
313   bool CheckAccess(const std::vector<std::string>& privileges) {
314     return impl_.CheckAccess(privileges);
315   }
316
317  private:
318   AccessControl() {
319   }
320   ~AccessControl() {
321   }
322   AccessControlImpl impl_;
323 };
324
325 }  // namespace
326
327 PlatformResult CheckAccess(const std::string& privilege) {
328   return CheckAccess(std::vector<std::string>{privilege});
329 }
330
331 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
332   ScopeLogger();
333
334   /*
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
338    * executed in
339    * a thread of the same process.
340    */
341   static std::mutex check_access_mutex;
342   const std::lock_guard<std::mutex> lock{check_access_mutex};
343
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;
350
351   std::string api_version;
352   PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
353   if (res.IsError()) {
354     return res;
355   }
356   LoggerD("Application api version: %s", api_version.c_str());
357
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;
362
363     SCOPE_EXIT {
364       g_list_free(input_glist);
365       g_list_free(mapped_glist);
366     };
367
368     std::vector<std::string>* mapped_vector_ptr = nullptr;
369
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");
380       }
381
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);
392         }
393       };
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];
397     } else {
398       // Retrieve from local cache
399       LoggerD("Mapped privileges already in cache");
400       mapped_vector_ptr = (std::vector<std::string>*)&(it->second);
401     }
402
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());
406       }
407       return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
408     }
409   }
410   return PlatformResult(ErrorCode::NO_ERROR);
411 }
412
413 PlatformResult GetPkgApiVersion(std::string* api_version) {
414   ScopeLogger();
415
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.
419
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;
424
425   char* api_ver = nullptr;
426   pkgmgrinfo_pkginfo_h pkginfo_handle = nullptr;
427
428   SCOPE_EXIT {
429     if (pkginfo_handle) {
430       pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
431     }
432   };
433
434   pid_t pid = getpid();
435   if (cached_pid == pid) {
436     *api_version = cached_api_version;  // Retrieve from local cache
437   } else {
438     std::string pkgid =common::CurrentApplication::GetInstance().GetPackageId();
439     if (pkgid.empty()) {
440       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
441     }
442
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");
446     }
447
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");
451     }
452
453     cached_api_version = api_ver;  // Save in local cache
454     cached_pid = pid;
455
456     *api_version = api_ver;
457   }
458
459   return PlatformResult(ErrorCode::NO_ERROR);
460 }
461
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;
465   do {
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());
472   return tokens;
473 }
474
475 bool IsAppVersionEarlierThan(const std::string& ver) {
476   ScopeLogger();
477   std::string app_ver;
478   auto res = GetPkgApiVersion(&app_ver);
479   if (!res) {
480     LoggerE(
481         "Failed to get application version, "
482         "assuming that application version is same as platform");
483     return false;
484   }
485   std::vector<std::string> vec_app = SplitString(app_ver, ".");
486   std::vector<std::string> vec_ref = SplitString(ver, ".");
487
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) {
493       return true;
494     } else if (num_app > num_ver) {
495       return false;
496     }
497   }
498   return false;
499 }
500
501 /*
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.
506  * */
507 struct RequestStoragePrivilegeChecker {
508   bool isStorageAccessCheckNeeded;
509   RequestStoragePrivilegeChecker() : isStorageAccessCheckNeeded(!IsAppVersionEarlierThan("5.0")) {
510   }
511 };
512
513 bool IsStoragePrivilegeCheckNeeded() {
514   thread_local RequestStoragePrivilegeChecker checker{};
515   return checker.isStorageAccessCheckNeeded;
516 }
517
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);
522   return msg;
523 }
524
525 int HexToInt(char c) {
526   if (c >= '0' && c <= '9') {
527     return c - '0';
528   } else if (c >= 'A' && c <= 'Z') {
529     return c - 'A' + 10;
530   } else {
531     return c - 'a' + 10;
532   }
533 }
534
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]);
539   }
540   return bin;
541 }
542
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];
548   }
549   return hex;
550 }
551
552 bool IsPathValid(const std::string& path) {
553   ScopeLogger();
554
555   /*
556    * Directory dot-referencing is not allowed
557    */
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("/..");
561 }
562
563 PlatformResult CheckStorageAccess(const std::string& path) {
564   ScopeLogger("path: [%s]", path.c_str());
565   struct stat buf {};
566   char* real_path = realpath(path.c_str(), nullptr);
567   SCOPE_EXIT {
568     free(real_path);
569   };
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]",
572             path.c_str());
573     return PlatformResult(ErrorCode::SECURITY_ERR,
574                           "Permission denied: accessing files requires proper storage privilege");
575   }
576   return PlatformResult(ErrorCode::NO_ERROR);
577 }
578
579 TizenResult CheckStorageAccessAndReturn(const std::string& path) {
580   ScopeLogger("path: [%s]", path.c_str());
581   struct stat buf {};
582   char* real_path = realpath(path.c_str(), nullptr);
583   SCOPE_EXIT {
584     free(real_path);
585   };
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]",
588             path.c_str());
589     return SecurityError("Permission denied: accessing files requires proper storage privilege");
590   }
591   return TizenSuccess();
592 }
593
594 PlatformResult CheckFileStatus(const std::string& path) {
595   ScopeLogger();
596
597   struct stat buf;
598
599   if (stat(path.c_str(), &buf)) {
600     LoggerD("Failed to stat path: %s", path.c_str());
601
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);
606     }
607
608     LoggerD("stat() error: %s", common::tools::GetErrorString(errno).c_str());
609     return PlatformResult(ErrorCode::UNKNOWN_ERR, "Cannot get status of the file: " + path);
610   }
611
612   if (!S_ISREG(buf.st_mode)) {
613     return PlatformResult(ErrorCode::NOT_FOUND_ERR,
614                           "Path does not point to a regular file: " + path);
615   }
616
617   if (!(S_IRUSR & buf.st_mode)) {
618     return PlatformResult(ErrorCode::IO_ERR, "The user cannot read the file: " + path);
619   }
620
621   return PlatformResult(ErrorCode::NO_ERROR);
622 }
623
624 PlatformResult CheckFileAvailability(const std::string& path) {
625   ScopeLogger();
626
627   if (!IsPathValid(path)) {
628     return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid path: " + path);
629   }
630
631   return CheckFileStatus(path);
632 }
633
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);
637   } else {
638     LoggerW(
639         "DEPRECATION WARNING: %s is deprecated and using it is not recommended. Try using %s "
640         "instead",
641         className, replacement);
642   }
643 }
644
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;
649 }
650
651 std::string ltrim(const std::string& s) {
652   ScopeLogger();
653   std::string str = s;
654   auto pos = str.find_first_not_of(" ");
655   str.erase(0, pos);
656   return str;
657 }
658
659 std::string ConvertUriToPath(const std::string& str) {
660   const std::string URI_PREFIX = "file://";
661   ScopeLogger();
662   std::string path = ltrim(str);
663   std::string prefix = path.substr(0, URI_PREFIX.size());
664
665   if (prefix == URI_PREFIX) {
666     return path.substr(URI_PREFIX.size());
667   } else {
668     return path;
669   }
670 }
671
672 }  // namespace tools
673 }  // namespace common