360196f60bd539fb7382dc46e9c7132d61e12aa1
[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 paramter: " << 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 paramter: " << 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 paramter: " << 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) && (req.uid))
131         uid = req.uid;
132     checkGlobalUser(uid, uidstr);
133
134     if (!installRequestAuthCheck(req, uid)) {
135         LogError("Request from uid " << uid << " for app installation denied");
136         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
137     }
138
139     std::string smackLabel;
140     if (!generateAppLabel(req.pkgId, smackLabel)) {
141         LogError("Cannot generate Smack label for package: " << req.pkgId);
142         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
143     }
144
145     LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
146             << ", generated smack label: " << smackLabel);
147
148     // create null terminated array of strings for permissions
149     std::unique_ptr<const char *[]> pp_permissions(new const char* [req.privileges.size() + 1]);
150     for (size_t i = 0; i < req.privileges.size(); ++i) {
151         LogDebug("  Permission = " << req.privileges[i]);
152         pp_permissions[i] = req.privileges[i].c_str();
153     }
154     pp_permissions[req.privileges.size()] = nullptr;
155
156     try {
157         std::vector<std::string> oldPkgPrivileges, newPkgPrivileges;
158
159         LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
160                  << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
161
162         PrivilegeDb::getInstance().BeginTransaction();
163         PrivilegeDb::getInstance().GetPkgPrivileges(req.pkgId, uid, oldPkgPrivileges);
164         PrivilegeDb::getInstance().AddApplication(req.appId, req.pkgId, uid, pkgIdIsNew);
165         PrivilegeDb::getInstance().UpdateAppPrivileges(req.appId, uid, req.privileges);
166         PrivilegeDb::getInstance().GetPkgPrivileges(req.pkgId, uid, newPkgPrivileges);
167         CynaraAdmin::UpdatePackagePolicy(smackLabel, uidstr, oldPkgPrivileges,
168                                          newPkgPrivileges);
169         PrivilegeDb::getInstance().CommitTransaction();
170         LogDebug("Application installation commited to database");
171     } catch (const PrivilegeDb::Exception::IOError &e) {
172         LogError("Cannot access application database: " << e.DumpToString());
173         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
174     } catch (const PrivilegeDb::Exception::InternalError &e) {
175         PrivilegeDb::getInstance().RollbackTransaction();
176         LogError("Error while saving application info to database: " << e.DumpToString());
177         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
178     } catch (const CynaraException::Base &e) {
179         PrivilegeDb::getInstance().RollbackTransaction();
180         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
181         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
182     } catch (const std::bad_alloc &e) {
183         PrivilegeDb::getInstance().RollbackTransaction();
184         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
185         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
186     }
187
188     // register paths
189     for (const auto &appPath : req.appPaths) {
190         const std::string &path = appPath.first;
191         app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
192         int result = setupPath(req.pkgId, path, pathType);
193
194         if (!result) {
195             LogError("setupPath() failed");
196             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
197         }
198     }
199
200     if (pkgIdIsNew) {
201         LogDebug("Adding Smack rules for new pkgId " << req.pkgId);
202         if (!SmackRules::installPackageRules(req.pkgId)) {
203             LogError("Failed to apply package-specific smack rules");
204             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
205         }
206     }
207
208     return SECURITY_MANAGER_API_SUCCESS;
209 }
210
211 int appUninstall(const std::string &appId, uid_t uid)
212 {
213     std::string pkgId;
214     std::string smackLabel;
215     bool appExists = true;
216     bool removePkg = false;
217     std::string uidstr;
218     checkGlobalUser(uid, uidstr);
219
220     try {
221         std::vector<std::string> oldPkgPrivileges, newPkgPrivileges;
222
223         PrivilegeDb::getInstance().BeginTransaction();
224         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
225             LogWarning("Application " << appId <<
226                 " not found in database while uninstalling");
227             PrivilegeDb::getInstance().RollbackTransaction();
228             appExists = false;
229         } else {
230
231             LogDebug("Uninstall parameters: appId: " << appId << ", pkgId: " << pkgId
232                      << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
233
234             if (!generateAppLabel(pkgId, smackLabel)) {
235                 LogError("Cannot generate Smack label for package: " << pkgId);
236                 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
237             }
238
239             PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, oldPkgPrivileges);
240             PrivilegeDb::getInstance().UpdateAppPrivileges(appId, uid, std::vector<std::string>());
241             PrivilegeDb::getInstance().RemoveApplication(appId, uid, removePkg);
242             PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, newPkgPrivileges);
243             CynaraAdmin::UpdatePackagePolicy(smackLabel, uidstr, oldPkgPrivileges,
244                                              newPkgPrivileges);
245             PrivilegeDb::getInstance().CommitTransaction();
246             LogDebug("Application uninstallation commited to database");
247         }
248     } catch (const PrivilegeDb::Exception::IOError &e) {
249         LogError("Cannot access application database: " << e.DumpToString());
250         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
251     } catch (const PrivilegeDb::Exception::InternalError &e) {
252         PrivilegeDb::getInstance().RollbackTransaction();
253         LogError("Error while removing application info from database: " << e.DumpToString());
254         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
255     } catch (const CynaraException::Base &e) {
256         PrivilegeDb::getInstance().RollbackTransaction();
257         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
258         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
259     } catch (const std::bad_alloc &e) {
260         PrivilegeDb::getInstance().RollbackTransaction();
261         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
262         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
263     }
264
265     if (appExists) {
266
267         if (removePkg) {
268             LogDebug("Removing Smack rules for deleted pkgId " << pkgId);
269             if (!SmackRules::uninstallPackageRules(pkgId)) {
270                 LogError("Error on uninstallation of package-specific smack rules");
271                 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
272             }
273         }
274     }
275
276     return SECURITY_MANAGER_API_SUCCESS;
277 }
278
279 int getPkgId(const std::string &appId, std::string &pkgId)
280 {
281     LogDebug("appId: " << appId);
282
283     try {
284         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
285             LogWarning("Application " << appId << " not found in database");
286             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
287         } else {
288             LogDebug("pkgId: " << pkgId);
289         }
290     } catch (const PrivilegeDb::Exception::Base &e) {
291         LogError("Error while getting pkgId from database: " << e.DumpToString());
292         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
293     }
294
295     return SECURITY_MANAGER_API_SUCCESS;
296 }
297
298 int getAppGroups(const std::string &appId, uid_t uid, pid_t pid, std::unordered_set<gid_t> &gids)
299 {
300     try {
301         std::string pkgId;
302         std::string smackLabel;
303         std::string uidStr = std::to_string(uid);
304         std::string pidStr = std::to_string(pid);
305
306         LogDebug("appId: " << appId);
307
308         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
309             LogWarning("Application " << appId << " not found in database");
310             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
311         }
312         LogDebug("pkgId: " << pkgId);
313
314         if (!generateAppLabel(pkgId, smackLabel)) {
315              LogError("Cannot generate Smack label for package: " << pkgId);
316             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
317         }
318         LogDebug("smack label: " << smackLabel);
319
320         std::vector<std::string> privileges;
321         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, privileges);
322         /*there is also a need of checking, if privilege is granted to all users*/
323         size_t tmp = privileges.size();
324         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, getGlobalUserId(), privileges);
325         /*privileges needs to be sorted and with no duplications - for cynara sake*/
326         std::inplace_merge(privileges.begin(), privileges.begin() + tmp, privileges.end());
327         privileges.erase( unique( privileges.begin(), privileges.end() ), privileges.end() );
328
329         for (const auto &privilege : privileges) {
330             std::vector<std::string> gidsTmp;
331             PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, gidsTmp);
332             if (!gidsTmp.empty()) {
333                 LogDebug("Considering privilege " << privilege << " with " <<
334                     gidsTmp.size() << " groups assigned");
335                 if (Cynara::getInstance().check(smackLabel, privilege, uidStr, pidStr)) {
336                     for_each(gidsTmp.begin(), gidsTmp.end(), [&] (std::string group)
337                     {
338                         struct group *grp = getgrnam(group.c_str());
339                         if (grp == NULL) {
340                                 LogError("No such group: " << group.c_str());
341                                 return;
342                         }
343                         gids.insert(grp->gr_gid);
344                     });
345                     LogDebug("Cynara allowed, adding groups");
346                 } else
347                     LogDebug("Cynara denied, not adding groups");
348             }
349         }
350     } catch (const PrivilegeDb::Exception::Base &e) {
351         LogError("Database error: " << e.DumpToString());
352         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
353     } catch (const CynaraException::Base &e) {
354         LogError("Error while querying Cynara for permissions: " << e.DumpToString());
355         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
356     } catch (const std::bad_alloc &e) {
357         LogError("Memory allocation failed: " << e.what());
358         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
359     }
360
361     return SECURITY_MANAGER_API_SUCCESS;
362 }
363
364 } /* namespace ServiceImpl */
365 } /* namespace SecurityManager */