6660561177d8886944a8d167dc72b3f5afe8c7af
[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
172         std::string pkg;
173         bool ret = PrivilegeDb::getInstance().GetAppPkgId(req.appId, pkg);
174         if (ret == true && pkg != req.pkgId) {
175             LogError("Application already installed with different package id");
176             PrivilegeDb::getInstance().RollbackTransaction();
177             return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
178         }
179         PrivilegeDb::getInstance().GetPkgPrivileges(req.pkgId, uid, oldPkgPrivileges);
180         PrivilegeDb::getInstance().AddApplication(req.appId, req.pkgId, uid, pkgIdIsNew);
181         PrivilegeDb::getInstance().UpdateAppPrivileges(req.appId, uid, req.privileges);
182         PrivilegeDb::getInstance().GetPkgPrivileges(req.pkgId, uid, newPkgPrivileges);
183         CynaraAdmin::UpdatePackagePolicy(smackLabel, uidstr, oldPkgPrivileges,
184                                          newPkgPrivileges);
185         PrivilegeDb::getInstance().CommitTransaction();
186         LogDebug("Application installation commited to database");
187     } catch (const PrivilegeDb::Exception::IOError &e) {
188         LogError("Cannot access application database: " << e.DumpToString());
189         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
190     } catch (const PrivilegeDb::Exception::InternalError &e) {
191         PrivilegeDb::getInstance().RollbackTransaction();
192         LogError("Error while saving application info to database: " << e.DumpToString());
193         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
194     } catch (const CynaraException::Base &e) {
195         PrivilegeDb::getInstance().RollbackTransaction();
196         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
197         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
198     } catch (const std::bad_alloc &e) {
199         PrivilegeDb::getInstance().RollbackTransaction();
200         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
201         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
202     }
203
204     // register paths
205     for (const auto &appPath : req.appPaths) {
206         const std::string &path = appPath.first;
207         app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
208         int result = setupPath(req.pkgId, path, pathType);
209
210         if (!result) {
211             LogError("setupPath() failed");
212             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
213         }
214     }
215
216     if (pkgIdIsNew) {
217         LogDebug("Adding Smack rules for new pkgId " << req.pkgId);
218         if (!SmackRules::installPackageRules(req.pkgId)) {
219             LogError("Failed to apply package-specific smack rules");
220             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
221         }
222     }
223
224     return SECURITY_MANAGER_API_SUCCESS;
225 }
226
227 int appUninstall(const std::string &appId, uid_t uid)
228 {
229     std::string pkgId;
230     std::string smackLabel;
231     bool appExists = true;
232     bool removePkg = false;
233     std::string uidstr;
234     checkGlobalUser(uid, uidstr);
235
236     try {
237         std::vector<std::string> oldPkgPrivileges, newPkgPrivileges;
238
239         PrivilegeDb::getInstance().BeginTransaction();
240         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
241             LogWarning("Application " << appId <<
242                 " not found in database while uninstalling");
243             PrivilegeDb::getInstance().RollbackTransaction();
244             appExists = false;
245         } else {
246
247             LogDebug("Uninstall parameters: appId: " << appId << ", pkgId: " << pkgId
248                      << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
249
250             if (!generateAppLabel(pkgId, smackLabel)) {
251                 LogError("Cannot generate Smack label for package: " << pkgId);
252                 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
253             }
254
255             PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, oldPkgPrivileges);
256             PrivilegeDb::getInstance().UpdateAppPrivileges(appId, uid, std::vector<std::string>());
257             PrivilegeDb::getInstance().RemoveApplication(appId, uid, removePkg);
258             PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, newPkgPrivileges);
259             CynaraAdmin::UpdatePackagePolicy(smackLabel, uidstr, oldPkgPrivileges,
260                                              newPkgPrivileges);
261             PrivilegeDb::getInstance().CommitTransaction();
262             LogDebug("Application uninstallation commited to database");
263         }
264     } catch (const PrivilegeDb::Exception::IOError &e) {
265         LogError("Cannot access application database: " << e.DumpToString());
266         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
267     } catch (const PrivilegeDb::Exception::InternalError &e) {
268         PrivilegeDb::getInstance().RollbackTransaction();
269         LogError("Error while removing application info from database: " << e.DumpToString());
270         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
271     } catch (const CynaraException::Base &e) {
272         PrivilegeDb::getInstance().RollbackTransaction();
273         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
274         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
275     } catch (const std::bad_alloc &e) {
276         PrivilegeDb::getInstance().RollbackTransaction();
277         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
278         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
279     }
280
281     if (appExists) {
282
283         if (removePkg) {
284             LogDebug("Removing Smack rules for deleted pkgId " << pkgId);
285             if (!SmackRules::uninstallPackageRules(pkgId)) {
286                 LogError("Error on uninstallation of package-specific smack rules");
287                 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
288             }
289         }
290     }
291
292     return SECURITY_MANAGER_API_SUCCESS;
293 }
294
295 int getPkgId(const std::string &appId, std::string &pkgId)
296 {
297     LogDebug("appId: " << appId);
298
299     try {
300         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
301             LogWarning("Application " << appId << " not found in database");
302             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
303         } else {
304             LogDebug("pkgId: " << pkgId);
305         }
306     } catch (const PrivilegeDb::Exception::Base &e) {
307         LogError("Error while getting pkgId from database: " << e.DumpToString());
308         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
309     }
310
311     return SECURITY_MANAGER_API_SUCCESS;
312 }
313
314 int getAppGroups(const std::string &appId, uid_t uid, pid_t pid, std::unordered_set<gid_t> &gids)
315 {
316     try {
317         std::string pkgId;
318         std::string smackLabel;
319         std::string uidStr = std::to_string(uid);
320         std::string pidStr = std::to_string(pid);
321
322         LogDebug("appId: " << appId);
323
324         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
325             LogWarning("Application " << appId << " not found in database");
326             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
327         }
328         LogDebug("pkgId: " << pkgId);
329
330         if (!generateAppLabel(pkgId, smackLabel)) {
331              LogError("Cannot generate Smack label for package: " << pkgId);
332             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
333         }
334         LogDebug("smack label: " << smackLabel);
335
336         std::vector<std::string> privileges;
337         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, privileges);
338         /*there is also a need of checking, if privilege is granted to all users*/
339         size_t tmp = privileges.size();
340         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, getGlobalUserId(), privileges);
341         /*privileges needs to be sorted and with no duplications - for cynara sake*/
342         std::inplace_merge(privileges.begin(), privileges.begin() + tmp, privileges.end());
343         privileges.erase( unique( privileges.begin(), privileges.end() ), privileges.end() );
344
345         for (const auto &privilege : privileges) {
346             std::vector<std::string> gidsTmp;
347             PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, gidsTmp);
348             if (!gidsTmp.empty()) {
349                 LogDebug("Considering privilege " << privilege << " with " <<
350                     gidsTmp.size() << " groups assigned");
351                 if (Cynara::getInstance().check(smackLabel, privilege, uidStr, pidStr)) {
352                     for_each(gidsTmp.begin(), gidsTmp.end(), [&] (std::string group)
353                     {
354                         struct group *grp = getgrnam(group.c_str());
355                         if (grp == NULL) {
356                                 LogError("No such group: " << group.c_str());
357                                 return;
358                         }
359                         gids.insert(grp->gr_gid);
360                     });
361                     LogDebug("Cynara allowed, adding groups");
362                 } else
363                     LogDebug("Cynara denied, not adding groups");
364             }
365         }
366     } catch (const PrivilegeDb::Exception::Base &e) {
367         LogError("Database error: " << e.DumpToString());
368         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
369     } catch (const CynaraException::Base &e) {
370         LogError("Error while querying Cynara for permissions: " << e.DumpToString());
371         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
372     } catch (const std::bad_alloc &e) {
373         LogError("Memory allocation failed: " << e.what());
374         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
375     }
376
377     return SECURITY_MANAGER_API_SUCCESS;
378 }
379
380 int userAdd(uid_t uidAdded, int userType, uid_t uid)
381 {
382     if (uid != 0)
383         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
384
385     switch (userType) {
386     case SM_USER_TYPE_SYSTEM:
387     case SM_USER_TYPE_ADMIN:
388     case SM_USER_TYPE_GUEST:
389     case SM_USER_TYPE_NORMAL:
390         break;
391     default:
392         return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
393     }
394
395     //TODO add policy information to cynara regarding user default privileges based on user_type
396     (void) uidAdded;
397     (void) userType;
398
399     return SECURITY_MANAGER_API_SUCCESS;
400 }
401
402 int userDelete(uid_t uidDeleted, uid_t uid)
403 {
404     int ret = SECURITY_MANAGER_API_SUCCESS;
405     if (uid != 0)
406         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
407
408     //TODO remove policy information from cynara
409
410     /*Uninstall all user apps*/
411     std::vector<std::string> userApps;
412     try {
413         PrivilegeDb::getInstance().GetUserApps(uidDeleted, userApps);
414     } catch (const PrivilegeDb::Exception::Base &e) {
415         LogError("Error while getting user apps from database: " << e.DumpToString());
416         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
417     }
418
419     for (auto &app: userApps) {
420         if (appUninstall(app, uidDeleted) != SECURITY_MANAGER_API_SUCCESS) {
421         /*if uninstallation of this app fails, just go on trying to uninstall another ones.
422         we do not have anything special to do about that matter - user will be deleted anyway.*/
423             ret = SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
424         }
425     }
426
427     return ret;
428 }
429
430 } /* namespace ServiceImpl */
431 } /* namespace SecurityManager */