[Common] Added cache for mapping privileges
[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 <privilegemgr/privilege_manager.h>
20 #include <app_manager.h>
21 #include <pkgmgr-info.h>
22
23 #ifdef PRIVILEGE_USE_DB
24 #include <sqlite3.h>
25 #include "common/current_application.h"
26 #elif PRIVILEGE_USE_ACE
27 //#include <privilege_checker.h>
28 #elif PRIVILEGE_USE_CYNARA
29 #include <unistd.h>
30
31 #include <cynara/cynara-client.h>
32 #include <sys/smack.h>
33 #endif
34
35 #include "common/logger.h"
36 #include "common/scope_exit.h"
37
38 namespace common {
39 namespace tools {
40
41 void ReportSuccess(picojson::object& out) {
42   LoggerD("Enter");
43   out.insert(std::make_pair("status", picojson::value("success")));
44 }
45
46 void ReportSuccess(const picojson::value& result, picojson::object& out) {
47   LoggerD("Enter");
48   out.insert(std::make_pair("status", picojson::value("success")));
49   out.insert(std::make_pair("result", result));
50 }
51
52 void ReportError(picojson::object& out) {
53   LoggerE("Error without error code");
54   out.insert(std::make_pair("status", picojson::value("error")));
55 }
56
57 void ReportError(const PlatformException& ex, picojson::object& out) {
58   LoggerE("PlatformException: %s, message: %s", ex.name().c_str(), ex.message().c_str());
59   out.insert(std::make_pair("status", picojson::value("error")));
60   out.insert(std::make_pair("error", ex.ToJSON()));
61 }
62
63 void ReportError(const PlatformResult& error, picojson::object* out) {
64   LoggerE("PlatformResult: %d, message: %s", error.error_code(), error.message().c_str());
65   out->insert(std::make_pair("status", picojson::value("error")));
66   out->insert(std::make_pair("error", error.ToJSON()));
67 }
68
69 namespace {
70
71 #ifdef PRIVILEGE_USE_DB
72
73 class AccessControlImpl {
74  public:
75   AccessControlImpl()
76       : initialized_(false) {
77     LoggerD("Privilege access checked using DB.");
78
79     const char* kWrtDBPath = "/opt/dbspace/.wrt.db";
80     sqlite3* db = nullptr;
81
82     int ret = sqlite3_open(kWrtDBPath, &db);
83     if (SQLITE_OK != ret) {
84       LoggerE("Failed to access WRT database");
85       return;
86     }
87
88     const char* kQuery = "select name from WidgetFeature where app_id = "
89                          "(select app_id from WidgetInfo where tizen_appid = ?)"
90                          " and rejected = 0";
91     const std::string app_id = common::CurrentApplication::GetInstance().GetApplicationId();
92     sqlite3_stmt* stmt = nullptr;
93
94     ret = sqlite3_prepare_v2(db, kQuery, -1, &stmt, nullptr);
95     ret |= sqlite3_bind_text(stmt, 1, app_id.c_str(), -1, SQLITE_TRANSIENT);
96
97     SCOPE_EXIT {
98       sqlite3_finalize(stmt);
99       sqlite3_close(db);
100     };
101
102     if (SQLITE_OK != ret) {
103       LoggerE("Failed to query WRT database");
104       return;
105     }
106
107     while (sqlite3_step(stmt) == SQLITE_ROW) {
108       const char* privilege = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
109       SLoggerD("Granted: %s", privilege);
110       granted_privileges_.push_back(privilege);
111     }
112
113     initialized_ = true;
114   }
115
116   ~AccessControlImpl() {}
117
118   bool CheckAccess(const std::vector<std::string>& privileges) {
119     LoggerD("Enter");
120     if (!initialized_) {
121       return false;
122     }
123
124     for (const auto& privilege : privileges) {
125       if (std::find(granted_privileges_.begin(), granted_privileges_.end(), privilege) == granted_privileges_.end()) {
126         return false;
127       }
128     }
129
130     return true;
131   }
132
133  private:
134   bool initialized_;
135   std::vector<std::string> granted_privileges_;
136 };
137
138 #elif PRIVILEGE_USE_ACE
139
140 class AccessControlImpl {
141  public:
142   AccessControlImpl() {
143     LoggerD("Privilege access checked using ACE.");
144   }
145
146   ~AccessControlImpl() {
147   }
148
149   bool CheckAccess(const std::vector<std::string>& privileges) {
150     LoggerD("Enter");
151
152     return true;
153   }
154 };
155
156 #elif PRIVILEGE_USE_CYNARA
157
158 class AccessControlImpl {
159  public:
160   AccessControlImpl() : cynara_(nullptr) {
161     LoggerD("Privilege access checked using Cynara.");
162
163     char* smack_label = nullptr;
164     int len= smack_new_label_from_self(&smack_label);
165
166     if (0 < len && nullptr != smack_label) {
167       auto uid = getuid();
168
169       SLoggerD("uid: [%u]", uid);
170       SLoggerD("smack label: [%s]", smack_label);
171
172       uid_ = std::to_string(uid);
173       smack_label_ = smack_label;
174
175       free(smack_label);
176     } else {
177       LoggerE("Failed to get smack label");
178       return;
179     }
180
181     int ret = cynara_initialize(&cynara_, nullptr);
182     if (CYNARA_API_SUCCESS != ret) {
183       LoggerE("Failed to initialize Cynara");
184       cynara_ = nullptr;
185     }
186   }
187
188   ~AccessControlImpl() {
189     if (cynara_) {
190       auto ret = cynara_finish(cynara_);
191       if (CYNARA_API_SUCCESS != ret) {
192         LoggerE("Failed to finalize Cynara");
193       }
194       cynara_ = nullptr;
195     }
196   }
197
198   bool CheckAccess(const std::vector<std::string>& privileges) {
199
200     if (cynara_) {
201       for (const auto& privilege : privileges) {
202         if (CYNARA_API_ACCESS_ALLOWED != cynara_check(cynara_,  // p_cynara
203                                                       smack_label_.c_str(),  // client
204                                                       "", // client_session
205                                                       uid_.c_str(),  // user
206                                                       privilege.c_str()  // privilege
207                                                       )) {
208           return false;
209         }
210       }
211       return true;
212     } else {
213       return false;
214     }
215   }
216
217  private:
218   cynara* cynara_;
219   std::string uid_;
220   std::string smack_label_;
221 };
222
223 #else
224
225 class AccessControlImpl {
226  public:
227   AccessControlImpl() {
228     LoggerD("Privilege access - deny all.");
229   }
230
231   bool CheckAccess(const std::vector<std::string>& privileges) {
232     return false;
233   }
234 };
235
236 #endif
237
238 class AccessControl {
239  public:
240   static AccessControl& GetInstance() {
241     static AccessControl instance;
242     return instance;
243   }
244
245   bool CheckAccess(const std::string& privilege) {
246     return CheckAccess(std::vector<std::string>{privilege});
247   }
248
249   bool CheckAccess(const std::vector<std::string>& privileges) {
250     return impl_.CheckAccess(privileges);
251   }
252
253  private:
254   AccessControl() {}
255   ~AccessControl() {}
256   AccessControlImpl impl_;
257 };
258
259 } // namespace
260
261
262 PlatformResult CheckAccess(const std::string& privilege) {
263   return CheckAccess(std::vector<std::string>{privilege});
264 }
265
266 PlatformResult CheckAccess(const std::vector<std::string>& privileges) {
267   LoggerD("Enter");
268
269   // Local cache of mapped privilege strings. This routine can be called many times, especially
270   // during application launch, generating a high overhead of retrieving mapped privileges from
271   // the underlying databases. This is especially the case since the same mappings can end up
272   // being retrieved several times.
273   using MappedPrivilegeCache = std::map<std::string, std::vector<std::string>>;
274   static MappedPrivilegeCache mapped_privilege_cache;
275
276   std::string api_version;
277   PlatformResult res = common::tools::GetPkgApiVersion(&api_version);
278   if (res.IsError()) {
279     return res;
280   }
281   LoggerD("Application api version: %s", api_version.c_str());
282
283   for (auto input_priv : privileges) {
284     LoggerD("Input privilege: %s", input_priv.c_str());
285     GList *input_glist = nullptr;
286     GList *mapped_glist = nullptr;
287
288     SCOPE_EXIT {
289       g_list_free(input_glist);
290       g_list_free(mapped_glist);
291     };
292
293     std::vector<std::string>* mapped_vector_ptr = nullptr;
294
295     // Check if mapped privilege is in local cache first
296     MappedPrivilegeCache::const_iterator it = mapped_privilege_cache.find(input_priv);
297     if (mapped_privilege_cache.end() == it) {
298       LoggerD("Mapped privileges - need to be fetched from database");
299       // Not in cache - retrieve from underlying databases.
300       input_glist = g_list_append(input_glist, (void*)input_priv.c_str());
301       int ret = privilege_manager_get_mapped_privilege_list(api_version.c_str(),
302                                                             PRVMGR_PACKAGE_TYPE_WRT,
303                                                             input_glist,
304                                                             &mapped_glist);
305       if (PRVMGR_ERR_NONE != ret) {
306         return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get mapped privilege list");
307       }
308
309       // initialize empty cache vector
310       mapped_privilege_cache[input_priv] = std::vector<std::string>();
311       LoggerD("Mapped privileges:");
312       auto push_elem = [](gpointer data, gpointer user_data) -> void {
313         if (data && user_data) {
314           std::vector<std::string>* mapped_vector =
315               static_cast<std::vector<std::string>*>(user_data);
316           char* char_data = static_cast<char*>(data);
317           mapped_vector->push_back(char_data);
318           LoggerD("mapped to: %s", char_data);
319         }
320       };
321       // fill the vector with data
322       g_list_foreach (mapped_glist, push_elem, &mapped_privilege_cache[input_priv]);
323       mapped_vector_ptr = &mapped_privilege_cache[input_priv];
324     } else {
325       // Retrieve from local cache
326       LoggerD("Mapped privileges already in cache");
327       mapped_vector_ptr = (std::vector<std::string>*) &(it->second);
328     }
329
330     if (!AccessControl::GetInstance().CheckAccess(*mapped_vector_ptr)){
331       for (const auto& mapped_priv : *mapped_vector_ptr) {
332         LoggerD("Access to privilege: %s has been denied.", mapped_priv.c_str());
333       }
334       return PlatformResult(ErrorCode::SECURITY_ERR, "Permission denied");
335     }
336   }
337   return PlatformResult(ErrorCode::NO_ERROR);
338 }
339
340 PlatformResult GetPkgApiVersion(std::string* api_version) {
341   LoggerD("Entered");
342
343   // Local cache of API version string.  This can be expensive to retrieve from
344   // underlying databases and this routine can be called many times during
345   // application launch.
346   static std::string cached_api_version;
347   static int cached_pid = -1;
348
349   char* app_id = nullptr;
350   char* pkgid = nullptr;
351   char* api_ver = nullptr;
352   app_info_h app_handle = nullptr;
353   pkgmgrinfo_pkginfo_h pkginfo_handle = nullptr;
354
355   SCOPE_EXIT {
356     if (app_id) {
357       free(app_id);
358     }
359     if (pkgid) {
360       free(pkgid);
361     }
362     if (app_handle) {
363       app_info_destroy(app_handle);
364     }
365     if (pkginfo_handle) {
366       pkgmgrinfo_pkginfo_destroy_pkginfo(pkginfo_handle);
367     }
368   };
369
370   pid_t pid = getpid();
371   if (cached_pid == pid) {
372     *api_version = cached_api_version;  // Retrieve from local cache
373   } else {
374     int ret = app_manager_get_app_id(pid, &app_id);
375     if (ret != APP_MANAGER_ERROR_NONE) {
376       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app id");
377     }
378
379     ret = app_info_create(app_id, &app_handle);
380     if (ret != APP_MANAGER_ERROR_NONE) {
381       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get app info");
382     }
383
384     ret = app_info_get_package(app_handle, &pkgid);
385     if ((ret != APP_MANAGER_ERROR_NONE) || (pkgid == nullptr)) {
386       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkg id");
387     }
388
389     ret = pkgmgrinfo_pkginfo_get_usr_pkginfo(pkgid, getuid(), &pkginfo_handle);
390     if (ret != PMINFO_R_OK) {
391       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get pkginfo_h");
392     }
393
394     ret = pkgmgrinfo_pkginfo_get_api_version(pkginfo_handle, &api_ver);
395     if (ret != PMINFO_R_OK) {
396       return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Fail to get api version");
397     }
398
399     cached_api_version = api_ver;  // Save in local cache
400     cached_pid = pid;
401
402     *api_version = api_ver;
403   }
404
405   return PlatformResult(ErrorCode::NO_ERROR);
406 }
407
408 std::vector<std::string> SplitString(const std::string& str, const std::string& delim) {
409   std::vector<std::string> tokens;
410   size_t prev = 0, pos = 0;
411   do
412   {
413     pos = str.find(delim, prev);
414     if (pos == std::string::npos) pos = str.length();
415     std::string token = str.substr(prev, pos-prev);
416     if (!token.empty()) tokens.push_back(token);
417     prev = pos + delim.length();
418   }
419   while (pos < str.length() && prev < str.length());
420   return tokens;
421 }
422
423 bool IsAppVersionEarlierThan(const std::string& ver) {
424   LoggerD("Enter");
425   std::string app_ver;
426   auto res = GetPkgApiVersion(&app_ver);
427   if (!res) {
428     LoggerE("Failed to get application version, "
429         "assuming that application version is same as platform");
430     return false;
431   }
432   std::vector<std::string> vec_app = SplitString(app_ver, ".");
433   std::vector<std::string> vec_ref = SplitString(ver, ".");
434
435   size_t length = std::min(vec_app.size(), vec_ref.size());
436   for (size_t i = 0; i < length; ++i) {
437     int num_ver = std::stoi(vec_ref[i]);
438     int num_app = std::stoi(vec_app[i]);
439     if (num_app < num_ver) {
440       return true;
441     } else if (num_app > num_ver) {
442       return false;
443     }
444   }
445   return false;
446 }
447
448 std::string GetErrorString(int error_code) {
449   static const size_t kSize = 1024;
450   char msg[kSize] = {0};
451   strerror_r(error_code, msg, kSize);
452   return msg;
453 }
454
455
456 int HexToInt(char c) {
457   if (c >= '0' && c <= '9') {
458     return c - '0';
459   } else if (c >= 'A' && c <= 'Z') {
460     return c - 'A' + 10;
461   } else {
462     return c - 'a' + 10;
463   }
464 }
465
466 unsigned char* HexToBin(const char* hex, int size, unsigned char* bin, int bin_size) {
467   for (int i = 0; i < size - 1 && i / 2 < bin_size; i += 2) {
468     bin[i / 2] = HexToInt(hex[i]) << 4;
469     bin[i / 2] |= HexToInt(hex[i + 1]);
470   }
471   return bin;
472 }
473
474 char* BinToHex(const unsigned char* bin, int size, char* hex, int hex_size) {
475   static const char * const digits = "0123456789ABCDEF";
476   for (int i = 0; i < size && i < hex_size / 2; i++) {
477     hex[i * 2] = digits[bin[i] >> 4];
478     hex[i * 2 + 1] = digits[bin[i] & 0xF];
479   }
480   return hex;
481 }
482
483 }  // namespace tools
484 }  // namespace common