Added support for offline applications installations mode
[platform/core/security/security-manager.git] / src / common / service_impl.cpp
1 /*
2  *  Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Rafal Krypa <r.krypa@samsung.com>
5  *
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *  Unless required by applicable law or agreed to in writing, software
13  *  distributed under the License is distributed on an "AS IS" BASIS,
14  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *  See the License for the specific language governing permissions and
16  *  limitations under the License
17  */
18 /*
19  * @file        service_impl.cpp
20  * @author      Michal Witanowski <m.witanowski@samsung.com>
21  * @author      Jacek Bukarewicz <j.bukarewicz@samsung.com>
22  * @author      Rafal Krypa <r.krypa@samsung.com>
23  * @brief       Implementation of the service methods
24  */
25
26 #include <grp.h>
27 #include <limits.h>
28 #include <pwd.h>
29
30 #include <cstring>
31 #include <algorithm>
32
33 #include <dpl/log/log.h>
34 #include <tzplatform_config.h>
35
36 #include "protocols.h"
37 #include "privilege_db.h"
38 #include "cynara.h"
39 #include "smack-common.h"
40 #include "smack-rules.h"
41 #include "smack-labels.h"
42
43 #include "service_impl.h"
44
45 namespace SecurityManager {
46 namespace ServiceImpl {
47
48 static uid_t getGlobalUserId(void)
49 {
50     static uid_t globaluid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
51     return globaluid;
52 }
53
54 /**
55  * Unifies user data of apps installed for all users
56  * @param  uid            peer's uid - may be changed during process
57  * @param  cynaraUserStr  string to which cynara user parameter will be put
58  */
59 static void checkGlobalUser(uid_t &uid, std::string &cynaraUserStr)
60 {
61     static uid_t globaluid = getGlobalUserId();
62     if (uid == 0 || uid == globaluid) {
63         uid = globaluid;
64         cynaraUserStr = CYNARA_ADMIN_WILDCARD;
65     } else {
66         cynaraUserStr = std::to_string(static_cast<unsigned int>(uid));
67     }
68 }
69 static inline bool isSubDir(const char *parent, const char *subdir)
70 {
71     while (*parent && *subdir)
72         if (*parent++ != *subdir++)
73             return false;
74
75     return (*subdir == '/');
76 }
77
78 static inline bool installRequestAuthCheck(const app_inst_req &req, uid_t uid)
79 {
80     struct passwd *pwd;
81     do {
82         errno = 0;
83         pwd = getpwuid(uid);
84         if (!pwd && errno != EINTR) {
85             LogError("getpwuid failed with '" << uid
86                     << "' as parameter: " << strerror(errno));
87             return false;
88         }
89     } while (!pwd);
90
91     std::unique_ptr<char, std::function<void(void*)>> home(
92         realpath(pwd->pw_dir, NULL), free);
93     if (!home.get()) {
94             LogError("realpath failed with '" << pwd->pw_dir
95                     << "' as parameter: " << strerror(errno));
96             return false;
97     }
98
99     for (const auto &appPath : req.appPaths) {
100         std::unique_ptr<char, std::function<void(void*)>> real_path(
101             realpath(appPath.first.c_str(), NULL), free);
102         if (!real_path.get()) {
103             LogError("realpath failed with '" << appPath.first.c_str()
104                     << "' as parameter: " << strerror(errno));
105             return false;
106         }
107         LogDebug("Requested path is '" << appPath.first.c_str()
108                 << "'. User's HOME is '" << pwd->pw_dir << "'");
109         if (!isSubDir(home.get(), real_path.get())) {
110             LogWarning("User's apps may have registered folders only in user's home dir");
111             return false;
112         }
113
114         app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
115         if (pathType == SECURITY_MANAGER_PATH_PUBLIC) {
116             LogWarning("Only root can register SECURITY_MANAGER_PATH_PUBLIC path");
117             return false;
118         }
119     }
120     return true;
121 }
122
123 int appInstall(const app_inst_req &req, uid_t uid)
124 {
125     bool pkgIdIsNew = false;
126     std::vector<std::string> addedPermissions;
127     std::vector<std::string> removedPermissions;
128
129     std::string uidstr;
130     if (uid) {
131         if (uid != req.uid) {
132             LogError("User " << uid <<
133                      " is denied to install application for user " << req.uid);
134             return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
135         }
136     } else {
137         if (req.uid)
138             uid = req.uid;
139     }
140     checkGlobalUser(uid, uidstr);
141
142     if (!installRequestAuthCheck(req, uid)) {
143         LogError("Request from uid " << uid << " for app installation denied");
144         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
145     }
146
147     std::string smackLabel;
148     if (!generateAppLabel(req.pkgId, smackLabel)) {
149         LogError("Cannot generate Smack label for package: " << req.pkgId);
150         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
151     }
152
153     LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
154             << ", generated smack label: " << smackLabel);
155
156     // create null terminated array of strings for permissions
157     std::unique_ptr<const char *[]> pp_permissions(new const char* [req.privileges.size() + 1]);
158     for (size_t i = 0; i < req.privileges.size(); ++i) {
159         LogDebug("  Permission = " << req.privileges[i]);
160         pp_permissions[i] = req.privileges[i].c_str();
161     }
162     pp_permissions[req.privileges.size()] = nullptr;
163
164     try {
165         std::vector<std::string> oldPkgPrivileges, newPkgPrivileges;
166
167         LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
168                  << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
169
170         PrivilegeDb::getInstance().BeginTransaction();
171         PrivilegeDb::getInstance().GetPkgPrivileges(req.pkgId, uid, oldPkgPrivileges);
172         PrivilegeDb::getInstance().AddApplication(req.appId, req.pkgId, uid, pkgIdIsNew);
173         PrivilegeDb::getInstance().UpdateAppPrivileges(req.appId, uid, req.privileges);
174         PrivilegeDb::getInstance().GetPkgPrivileges(req.pkgId, uid, newPkgPrivileges);
175         CynaraAdmin::UpdatePackagePolicy(smackLabel, uidstr, oldPkgPrivileges,
176                                          newPkgPrivileges);
177         PrivilegeDb::getInstance().CommitTransaction();
178         LogDebug("Application installation commited to database");
179     } catch (const PrivilegeDb::Exception::IOError &e) {
180         LogError("Cannot access application database: " << e.DumpToString());
181         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
182     } catch (const PrivilegeDb::Exception::InternalError &e) {
183         PrivilegeDb::getInstance().RollbackTransaction();
184         LogError("Error while saving application info to database: " << e.DumpToString());
185         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
186     } catch (const CynaraException::Base &e) {
187         PrivilegeDb::getInstance().RollbackTransaction();
188         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
189         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
190     } catch (const std::bad_alloc &e) {
191         PrivilegeDb::getInstance().RollbackTransaction();
192         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
193         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
194     }
195
196     // register paths
197     for (const auto &appPath : req.appPaths) {
198         const std::string &path = appPath.first;
199         app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
200         int result = setupPath(req.pkgId, path, pathType);
201
202         if (!result) {
203             LogError("setupPath() failed");
204             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
205         }
206     }
207
208     if (pkgIdIsNew) {
209         LogDebug("Adding Smack rules for new pkgId " << req.pkgId);
210         if (!SmackRules::installPackageRules(req.pkgId)) {
211             LogError("Failed to apply package-specific smack rules");
212             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
213         }
214     }
215
216     return SECURITY_MANAGER_API_SUCCESS;
217 }
218
219 int appUninstall(const std::string &appId, uid_t uid)
220 {
221     std::string pkgId;
222     std::string smackLabel;
223     bool appExists = true;
224     bool removePkg = false;
225     std::string uidstr;
226     checkGlobalUser(uid, uidstr);
227
228     try {
229         std::vector<std::string> oldPkgPrivileges, newPkgPrivileges;
230
231         PrivilegeDb::getInstance().BeginTransaction();
232         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
233             LogWarning("Application " << appId <<
234                 " not found in database while uninstalling");
235             PrivilegeDb::getInstance().RollbackTransaction();
236             appExists = false;
237         } else {
238
239             LogDebug("Uninstall parameters: appId: " << appId << ", pkgId: " << pkgId
240                      << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
241
242             if (!generateAppLabel(pkgId, smackLabel)) {
243                 LogError("Cannot generate Smack label for package: " << pkgId);
244                 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
245             }
246
247             PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, oldPkgPrivileges);
248             PrivilegeDb::getInstance().UpdateAppPrivileges(appId, uid, std::vector<std::string>());
249             PrivilegeDb::getInstance().RemoveApplication(appId, uid, removePkg);
250             PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, newPkgPrivileges);
251             CynaraAdmin::UpdatePackagePolicy(smackLabel, uidstr, oldPkgPrivileges,
252                                              newPkgPrivileges);
253             PrivilegeDb::getInstance().CommitTransaction();
254             LogDebug("Application uninstallation commited to database");
255         }
256     } catch (const PrivilegeDb::Exception::IOError &e) {
257         LogError("Cannot access application database: " << e.DumpToString());
258         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
259     } catch (const PrivilegeDb::Exception::InternalError &e) {
260         PrivilegeDb::getInstance().RollbackTransaction();
261         LogError("Error while removing application info from database: " << e.DumpToString());
262         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
263     } catch (const CynaraException::Base &e) {
264         PrivilegeDb::getInstance().RollbackTransaction();
265         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
266         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
267     } catch (const std::bad_alloc &e) {
268         PrivilegeDb::getInstance().RollbackTransaction();
269         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
270         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
271     }
272
273     if (appExists) {
274
275         if (removePkg) {
276             LogDebug("Removing Smack rules for deleted pkgId " << pkgId);
277             if (!SmackRules::uninstallPackageRules(pkgId)) {
278                 LogError("Error on uninstallation of package-specific smack rules");
279                 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
280             }
281         }
282     }
283
284     return SECURITY_MANAGER_API_SUCCESS;
285 }
286
287 int getPkgId(const std::string &appId, std::string &pkgId)
288 {
289     LogDebug("appId: " << appId);
290
291     try {
292         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
293             LogWarning("Application " << appId << " not found in database");
294             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
295         } else {
296             LogDebug("pkgId: " << pkgId);
297         }
298     } catch (const PrivilegeDb::Exception::Base &e) {
299         LogError("Error while getting pkgId from database: " << e.DumpToString());
300         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
301     }
302
303     return SECURITY_MANAGER_API_SUCCESS;
304 }
305
306 int getAppGroups(const std::string &appId, uid_t uid, pid_t pid, std::unordered_set<gid_t> &gids)
307 {
308     try {
309         std::string pkgId;
310         std::string smackLabel;
311         std::string uidStr = std::to_string(uid);
312         std::string pidStr = std::to_string(pid);
313
314         LogDebug("appId: " << appId);
315
316         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
317             LogWarning("Application " << appId << " not found in database");
318             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
319         }
320         LogDebug("pkgId: " << pkgId);
321
322         if (!generateAppLabel(pkgId, smackLabel)) {
323              LogError("Cannot generate Smack label for package: " << pkgId);
324             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
325         }
326         LogDebug("smack label: " << smackLabel);
327
328         std::vector<std::string> privileges;
329         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, privileges);
330         /*there is also a need of checking, if privilege is granted to all users*/
331         size_t tmp = privileges.size();
332         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, getGlobalUserId(), privileges);
333         /*privileges needs to be sorted and with no duplications - for cynara sake*/
334         std::inplace_merge(privileges.begin(), privileges.begin() + tmp, privileges.end());
335         privileges.erase( unique( privileges.begin(), privileges.end() ), privileges.end() );
336
337         for (const auto &privilege : privileges) {
338             std::vector<std::string> gidsTmp;
339             PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, gidsTmp);
340             if (!gidsTmp.empty()) {
341                 LogDebug("Considering privilege " << privilege << " with " <<
342                     gidsTmp.size() << " groups assigned");
343                 if (Cynara::getInstance().check(smackLabel, privilege, uidStr, pidStr)) {
344                     for_each(gidsTmp.begin(), gidsTmp.end(), [&] (std::string group)
345                     {
346                         struct group *grp = getgrnam(group.c_str());
347                         if (grp == NULL) {
348                                 LogError("No such group: " << group.c_str());
349                                 return;
350                         }
351                         gids.insert(grp->gr_gid);
352                     });
353                     LogDebug("Cynara allowed, adding groups");
354                 } else
355                     LogDebug("Cynara denied, not adding groups");
356             }
357         }
358     } catch (const PrivilegeDb::Exception::Base &e) {
359         LogError("Database error: " << e.DumpToString());
360         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
361     } catch (const CynaraException::Base &e) {
362         LogError("Error while querying Cynara for permissions: " << e.DumpToString());
363         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
364     } catch (const std::bad_alloc &e) {
365         LogError("Memory allocation failed: " << e.what());
366         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
367     }
368
369     return SECURITY_MANAGER_API_SUCCESS;
370 }
371
372 } /* namespace ServiceImpl */
373 } /* namespace SecurityManager */