Check thread smack label for thread-based app model
[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
24 #ifdef PRIVILEGE_USE_DB
25 #include <sqlite3.h>
26 #include "common/current_application.h"
27 #elif PRIVILEGE_USE_ACE
28 //#include <privilege_checker.h>
29 #elif PRIVILEGE_USE_CYNARA
30 #include <unistd.h>
31
32 #include <cynara/cynara-client.h>
33 #include <sys/smack.h>
34 #endif
35
36 #include "common/logger.h"
37 #include "common/scope_exit.h"
38
39 namespace common {
40 namespace tools {
41
42 void ReportSuccess(picojson::object& out) {
43   ScopeLogger();
44   out.insert(std::make_pair("status", picojson::value("success")));
45 }
46
47 void ReportSuccess(const picojson::value& result, picojson::object& out) {
48   ScopeLogger();
49   out.insert(std::make_pair("status", picojson::value("success")));
50   out.insert(std::make_pair("result", result));
51 }
52
53 void ReportError(picojson::object& out) {
54   LoggerE("Error without error code");
55   out.insert(std::make_pair("status", picojson::value("error")));
56 }
57
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()));
62 }
63
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()));
69 }
70
71 namespace {
72
73 #ifdef PRIVILEGE_USE_DB
74
75 class AccessControlImpl {
76  public:
77   AccessControlImpl() : initialized_(false) {
78     ScopeLogger("Privilege access checked using DB.");
79
80     const char* kWrtDBPath = "/opt/dbspace/.wrt.db";
81     sqlite3* db = nullptr;
82
83     int ret = sqlite3_open(kWrtDBPath, &db);
84     if (SQLITE_OK != ret) {
85       LoggerE("Failed to access WRT database");
86       return;
87     }
88
89     const char* kQuery =
90         "select name from WidgetFeature where app_id = "
91         "(select app_id from WidgetInfo where tizen_appid = ?)"
92         " and rejected = 0";
93     const std::string app_id = common::CurrentApplication::GetInstance().GetApplicationId();
94     sqlite3_stmt* stmt = nullptr;
95
96     ret = sqlite3_prepare_v2(db, kQuery, -1, &stmt, nullptr);
97     ret |= sqlite3_bind_text(stmt, 1, app_id.c_str(), -1, SQLITE_TRANSIENT);
98
99     SCOPE_EXIT {
100       sqlite3_finalize(stmt);
101       sqlite3_close(db);
102     };
103
104     if (SQLITE_OK != ret) {
105       LoggerE("Failed to query WRT database");
106       return;
107     }
108
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);
113     }
114
115     initialized_ = true;
116   }
117
118   ~AccessControlImpl() {
119   }
120
121   bool CheckAccess(const std::vector<std::string>& privileges) {
122     ScopeLogger();
123     if (!initialized_) {
124       return false;
125     }
126
127     for (const auto& privilege : privileges) {
128       if (std::find(granted_privileges_.begin(), granted_privileges_.end(), privilege) ==
129           granted_privileges_.end()) {
130         return false;
131       }
132     }
133
134     return true;
135   }
136
137  private:
138   bool initialized_;
139   std::vector<std::string> granted_privileges_;
140 };
141
142 #elif PRIVILEGE_USE_ACE
143
144 class AccessControlImpl {
145  public:
146   AccessControlImpl() {
147     LoggerD("Privilege access checked using ACE.");
148   }
149
150   ~AccessControlImpl() {
151   }
152
153   bool CheckAccess(const std::vector<std::string>& privileges) {
154     ScopeLogger();
155
156     return true;
157   }
158 };
159
160 #elif PRIVILEGE_USE_CYNARA
161
162 class AccessControlImpl {
163  public:
164   AccessControlImpl() : cynara_(nullptr) {
165     ScopeLogger("Privilege access checked using Cynara.");
166
167     char* smack_label = nullptr;
168     char path[1024] = {
169         0,
170     };
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);
173
174     if (0 == ret && nullptr != smack_label && 0 < strlen(smack_label)) {
175       auto uid = getuid();
176
177       SLoggerD("uid: [%u]", uid);
178       SLoggerD("smack label: [%s]", smack_label);
179
180       uid_ = std::to_string(uid);
181       smack_label_ = smack_label;
182
183       free(smack_label);
184     } else {
185       LoggerE("Failed to get smack label");
186       return;
187     }
188
189     ret = cynara_initialize(&cynara_, nullptr);
190     if (CYNARA_API_SUCCESS != ret) {
191       LoggerE("Failed to initialize Cynara");
192       cynara_ = nullptr;
193     }
194   }
195
196   ~AccessControlImpl() {
197     if (cynara_) {
198       auto ret = cynara_finish(cynara_);
199       if (CYNARA_API_SUCCESS != ret) {
200         LoggerE("Failed to finalize Cynara");
201       }
202       cynara_ = nullptr;
203     }
204   }
205
206   bool CheckAccess(const std::vector<std::string>& privileges) {
207     if (cynara_) {
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
214             ) {
215           return false;
216         }
217       }
218       return true;
219     } else {
220       return false;
221     }
222   }
223
224  private:
225   cynara* cynara_;
226   std::string uid_;
227   std::string smack_label_;
228 };
229
230 #else
231
232 class AccessControlImpl {
233  public:
234   AccessControlImpl() {
235     LoggerD("Privilege access - deny all.");
236   }
237
238   bool CheckAccess(const std::vector<std::string>& privileges) {
239     return false;
240   }
241 };
242
243 #endif
244
245 class AccessControl {
246  public:
247   static AccessControl& GetInstance() {
248     static AccessControl instance;
249     return instance;
250   }
251
252   bool CheckAccess(const std::string& privilege) {
253     return CheckAccess(std::vector<std::string>{privilege});
254   }
255
256   bool CheckAccess(const std::vector<std::string>& privileges) {
257     return impl_.CheckAccess(privileges);
258   }
259
260  private:
261   AccessControl() {
262   }
263   ~AccessControl() {
264   }
265   AccessControlImpl impl_;
266 };
267
268 }  // namespace
269
270 PlatformResult CheckAccess(const std::string& privilege) {
271   return CheckAccess(std::vector<std::string>{privilege});
272 }
273
274 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
275   ScopeLogger();
276
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;
283
284   std::string api_version;
285   PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
286   if (res.IsError()) {
287     return res;
288   }
289   LoggerD("Application api version: %s", api_version.c_str());
290
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;
295
296     SCOPE_EXIT {
297       g_list_free(input_glist);
298       g_list_free(mapped_glist);
299     };
300
301     std::vector<std::string>* mapped_vector_ptr = nullptr;
302
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");
313       }
314
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);
325         }
326       };
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];
330     } else {
331       // Retrieve from local cache
332       LoggerD("Mapped privileges already in cache");
333       mapped_vector_ptr = (std::vector<std::string>*)&(it->second);
334     }
335
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());
339       }
340       return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
341     }
342   }
343   return PlatformResult(ErrorCode::NO_ERROR);
344 }
345
346 PlatformResult GetPkgApiVersion(std::string* api_version) {
347   ScopeLogger();
348
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;
354
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;
360
361   SCOPE_EXIT {
362     if (app_id) {
363       free(app_id);
364     }
365     if (pkgid) {
366       free(pkgid);
367     }
368     if (app_handle) {
369       app_info_destroy(app_handle);
370     }
371     if (pkginfo_handle) {
372       pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
373     }
374   };
375
376   pid_t pid = getpid();
377   if (cached_pid == pid) {
378     *api_version = cached_api_version;  // Retrieve from local cache
379   } else {
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");
383     }
384
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");
388     }
389
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");
393     }
394
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");
398     }
399
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");
403     }
404
405     cached_api_version = api_ver;  // Save in local cache
406     cached_pid = pid;
407
408     *api_version = api_ver;
409   }
410
411   return PlatformResult(ErrorCode::NO_ERROR);
412 }
413
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;
417   do {
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());
424   return tokens;
425 }
426
427 bool IsAppVersionEarlierThan(const std::string& ver) {
428   ScopeLogger();
429   std::string app_ver;
430   auto res = GetPkgApiVersion(&app_ver);
431   if (!res) {
432     LoggerE(
433         "Failed to get application version, "
434         "assuming that application version is same as platform");
435     return false;
436   }
437   std::vector<std::string> vec_app = SplitString(app_ver, ".");
438   std::vector<std::string> vec_ref = SplitString(ver, ".");
439
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) {
445       return true;
446     } else if (num_app > num_ver) {
447       return false;
448     }
449   }
450   return false;
451 }
452
453 /*
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.
458  * */
459 struct RequestStoragePrivilegeChecker {
460   bool isStorageAccessCheckNeeded;
461   RequestStoragePrivilegeChecker() : isStorageAccessCheckNeeded(!IsAppVersionEarlierThan("5.0")) {
462   }
463 };
464
465 bool IsStoragePrivilegeCheckNeeded() {
466   static RequestStoragePrivilegeChecker checker{};
467   return checker.isStorageAccessCheckNeeded;
468 }
469
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);
474   return msg;
475 }
476
477 int HexToInt(char c) {
478   if (c >= '0' && c <= '9') {
479     return c - '0';
480   } else if (c >= 'A' && c <= 'Z') {
481     return c - 'A' + 10;
482   } else {
483     return c - 'a' + 10;
484   }
485 }
486
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]);
491   }
492   return bin;
493 }
494
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];
500   }
501   return hex;
502 }
503
504 bool IsPathValid(const std::string& path) {
505   ScopeLogger();
506
507   /*
508    * Directory dot-referencing is not allowed
509    */
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("/..");
513 }
514
515 PlatformResult CheckStorageAccess(const std::string& path) {
516   ScopeLogger("path: [%s]", path.c_str());
517   struct stat buf {};
518   char* real_path = realpath(path.c_str(), nullptr);
519   SCOPE_EXIT {
520     free(real_path);
521   };
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]",
524             path.c_str());
525     return PlatformResult(ErrorCode::SECURITY_ERR,
526                           "Permission denied: accessing files requires proper storage privilege");
527   }
528   return PlatformResult(ErrorCode::NO_ERROR);
529 }
530
531 TizenResult CheckStorageAccessAndReturn(const std::string& path) {
532   ScopeLogger("path: [%s]", path.c_str());
533   struct stat buf {};
534   char* real_path = realpath(path.c_str(), nullptr);
535   SCOPE_EXIT {
536     free(real_path);
537   };
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]",
540             path.c_str());
541     return SecurityError("Permission denied: accessing files requires proper storage privilege");
542   }
543   return TizenSuccess();
544 }
545
546 PlatformResult CheckFileStatus(const std::string& path) {
547   ScopeLogger();
548
549   struct stat buf;
550
551   if (stat(path.c_str(), &buf)) {
552     LoggerD("Failed to stat path: %s", path.c_str());
553
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);
558     }
559
560     LoggerD("stat() error: %s", common::tools::GetErrorString(errno).c_str());
561     return PlatformResult(ErrorCode::UNKNOWN_ERR, "Cannot get status of the file: " + path);
562   }
563
564   if (!S_ISREG(buf.st_mode)) {
565     return PlatformResult(ErrorCode::NOT_FOUND_ERR,
566                           "Path does not point to a regular file: " + path);
567   }
568
569   if (!(S_IRUSR & buf.st_mode)) {
570     return PlatformResult(ErrorCode::IO_ERR, "The user cannot read the file: " + path);
571   }
572
573   return PlatformResult(ErrorCode::NO_ERROR);
574 }
575
576 PlatformResult CheckFileAvailability(const std::string& path) {
577   ScopeLogger();
578
579   if (!IsPathValid(path)) {
580     return PlatformResult(ErrorCode::INVALID_VALUES_ERR, "Invalid path: " + path);
581   }
582
583   return CheckFileStatus(path);
584 }
585
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);
589   } else {
590     LoggerW(
591         "DEPRECATION WARNING: %s is deprecated and using it is not recommended. Try using %s "
592         "instead",
593         className, replacement);
594   }
595 }
596
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;
601 }
602
603 std::string ltrim(const std::string& s) {
604   ScopeLogger();
605   std::string str = s;
606   auto pos = str.find_first_not_of(" ");
607   str.erase(0, pos);
608   return str;
609 }
610
611 std::string ConvertUriToPath(const std::string& str) {
612   const std::string URI_PREFIX = "file://";
613   ScopeLogger();
614   std::string path = ltrim(str);
615   std::string prefix = path.substr(0, URI_PREFIX.size());
616
617   if (prefix == URI_PREFIX) {
618     return path.substr(URI_PREFIX.size());
619   } else {
620     return path;
621   }
622 }
623
624 }  // namespace tools
625 }  // namespace common