7eddd502a8f8db525f08d14c84f17b8b6870d45b
[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-rules.h"
40 #include "smack-labels.h"
41
42 #include "service_impl.h"
43
44 namespace SecurityManager {
45 namespace ServiceImpl {
46
47 static uid_t getGlobalUserId(void)
48 {
49     static uid_t globaluid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
50     return globaluid;
51 }
52
53 /**
54  * Unifies user data of apps installed for all users
55  * @param  uid            peer's uid - may be changed during process
56  * @param  cynaraUserStr  string to which cynara user parameter will be put
57  */
58 static void checkGlobalUser(uid_t &uid, std::string &cynaraUserStr)
59 {
60     static uid_t globaluid = getGlobalUserId();
61     if (uid == 0 || uid == globaluid) {
62         uid = globaluid;
63         cynaraUserStr = CYNARA_ADMIN_WILDCARD;
64     } else {
65         cynaraUserStr = std::to_string(static_cast<unsigned int>(uid));
66     }
67 }
68 static inline bool isSubDir(const char *parent, const char *subdir)
69 {
70     while (*parent && *subdir)
71         if (*parent++ != *subdir++)
72             return false;
73
74     return (*subdir == '/');
75 }
76
77 static inline bool installRequestAuthCheck(const app_inst_req &req, uid_t uid)
78 {
79     struct passwd *pwd;
80     do {
81         errno = 0;
82         pwd = getpwuid(uid);
83         if (!pwd && errno != EINTR) {
84             LogError("getpwuid failed with '" << uid
85                     << "' as parameter: " << strerror(errno));
86             return false;
87         }
88     } while (!pwd);
89
90     std::unique_ptr<char, std::function<void(void*)>> home(
91         realpath(pwd->pw_dir, NULL), free);
92     if (!home.get()) {
93             LogError("realpath failed with '" << pwd->pw_dir
94                     << "' as parameter: " << strerror(errno));
95             return false;
96     }
97
98     for (const auto &appPath : req.appPaths) {
99         std::unique_ptr<char, std::function<void(void*)>> real_path(
100             realpath(appPath.first.c_str(), NULL), free);
101         if (!real_path.get()) {
102             LogError("realpath failed with '" << appPath.first.c_str()
103                     << "' as parameter: " << strerror(errno));
104             return false;
105         }
106         LogDebug("Requested path is '" << appPath.first.c_str()
107                 << "'. User's HOME is '" << pwd->pw_dir << "'");
108         if (!isSubDir(home.get(), real_path.get())) {
109             LogWarning("User's apps may have registered folders only in user's home dir");
110             return false;
111         }
112
113         app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
114         if (pathType == SECURITY_MANAGER_PATH_PUBLIC) {
115             LogWarning("Only root can register SECURITY_MANAGER_PATH_PUBLIC path");
116             return false;
117         }
118     }
119     return true;
120 }
121
122 int appInstall(const app_inst_req &req, uid_t uid)
123 {
124     std::vector<std::string> addedPermissions;
125     std::vector<std::string> removedPermissions;
126     std::vector<std::string> pkgContents;
127     std::string uidstr;
128     if (uid) {
129         if (uid != req.uid) {
130             LogError("User " << uid <<
131                      " is denied to install application for user " << req.uid);
132             return SECURITY_MANAGER_API_ERROR_ACCESS_DENIED;
133         }
134     } else {
135         if (req.uid)
136             uid = req.uid;
137     }
138     checkGlobalUser(uid, uidstr);
139
140     if (!installRequestAuthCheck(req, uid)) {
141         LogError("Request from uid " << uid << " for app installation denied");
142         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
143     }
144
145     std::string smackLabel;
146     if (!generateAppLabel(req.appId, smackLabel)) {
147         LogError("Cannot generate Smack label for application: " << req.appId);
148         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
149     }
150
151     LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
152             << ", generated smack label: " << smackLabel);
153
154     // create null terminated array of strings for permissions
155     std::unique_ptr<const char *[]> pp_permissions(new const char* [req.privileges.size() + 1]);
156     for (size_t i = 0; i < req.privileges.size(); ++i) {
157         LogDebug("  Permission = " << req.privileges[i]);
158         pp_permissions[i] = req.privileges[i].c_str();
159     }
160     pp_permissions[req.privileges.size()] = nullptr;
161
162     try {
163         std::vector<std::string> oldAppPrivileges;
164         LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
165                  << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
166
167         PrivilegeDb::getInstance().BeginTransaction();
168         std::string pkg;
169         bool ret = PrivilegeDb::getInstance().GetAppPkgId(req.appId, pkg);
170         if (ret == true && pkg != req.pkgId) {
171             LogError("Application already installed with different package id");
172             PrivilegeDb::getInstance().RollbackTransaction();
173             return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
174         }
175         PrivilegeDb::getInstance().GetAppPrivileges(req.appId, uid, oldAppPrivileges);
176         PrivilegeDb::getInstance().AddApplication(req.appId, req.pkgId, uid);
177         PrivilegeDb::getInstance().UpdateAppPrivileges(req.appId, uid, req.privileges);
178         /* Get all application ids in the package to generate rules withing the package */
179         PrivilegeDb::getInstance().GetAppIdsForPkgId(req.pkgId, pkgContents);
180         CynaraAdmin::getInstance().UpdateAppPolicy(smackLabel, uidstr, oldAppPrivileges,
181                                          req.privileges);
182         PrivilegeDb::getInstance().CommitTransaction();
183         LogDebug("Application installation commited to database");
184     } catch (const PrivilegeDb::Exception::IOError &e) {
185         LogError("Cannot access application database: " << e.DumpToString());
186         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
187     } catch (const PrivilegeDb::Exception::InternalError &e) {
188         PrivilegeDb::getInstance().RollbackTransaction();
189         LogError("Error while saving application info to database: " << e.DumpToString());
190         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
191     } catch (const CynaraException::Base &e) {
192         PrivilegeDb::getInstance().RollbackTransaction();
193         LogError("Error while setting Cynara rules for application: " << e.DumpToString());
194         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
195     } catch (const std::bad_alloc &e) {
196         PrivilegeDb::getInstance().RollbackTransaction();
197         LogError("Memory allocation while setting Cynara rules for application: " << e.what());
198         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
199     }
200
201     // register paths
202     for (const auto &appPath : req.appPaths) {
203         const std::string &path = appPath.first;
204         app_install_path_type pathType = static_cast<app_install_path_type>(appPath.second);
205         int result = setupPath(req.pkgId, path, pathType);
206
207         if (!result) {
208             LogError("setupPath() failed");
209             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
210         }
211     }
212
213     LogDebug("Adding Smack rules for new appId: " << req.appId << " with pkgId: "
214             << req.pkgId << ". Applications in package: " << pkgContents.size());
215     if (!SmackRules::installApplicationRules(req.appId, req.pkgId, pkgContents)) {
216         LogError("Failed to apply package-specific smack rules");
217         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
218     }
219
220     return SECURITY_MANAGER_API_SUCCESS;
221 }
222
223 int appUninstall(const std::string &appId, uid_t uid)
224 {
225     std::string pkgId;
226     std::string smackLabel;
227     std::vector<std::string> pkgContents;
228     bool appExists = true;
229     bool removePkg = false;
230     std::string uidstr;
231     checkGlobalUser(uid, uidstr);
232
233     try {
234         std::vector<std::string> oldAppPrivileges;
235
236         PrivilegeDb::getInstance().BeginTransaction();
237         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
238             LogWarning("Application " << appId <<
239                 " not found in database while uninstalling");
240             PrivilegeDb::getInstance().RollbackTransaction();
241             appExists = false;
242         } else {
243
244             LogDebug("Uninstall parameters: appId: " << appId << ", pkgId: " << pkgId
245                      << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
246
247             if (!generateAppLabel(appId, smackLabel)) {
248                 LogError("Cannot generate Smack label for package: " << pkgId);
249                 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
250             }
251
252             /* Before we remove the app from the database, let's fetch all apps in the package
253                 that this app belongs to, this will allow us to remove all rules withing the
254                 package that the app appears in */
255             PrivilegeDb::getInstance().GetAppIdsForPkgId(pkgId, pkgContents);
256             PrivilegeDb::getInstance().GetAppPrivileges(appId, uid, oldAppPrivileges);
257             PrivilegeDb::getInstance().UpdateAppPrivileges(appId, uid, std::vector<std::string>());
258             PrivilegeDb::getInstance().RemoveApplication(appId, uid, removePkg);
259             CynaraAdmin::getInstance().UpdateAppPolicy(smackLabel, uidstr, oldAppPrivileges,
260                                              std::vector<std::string>());
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         LogDebug ("Removing smack rules for deleted appId " << appId);
291         if (!SmackRules::uninstallApplicationRules(appId, pkgId, pkgContents)) {
292             LogError("Error during uninstallation of application-specific smack rules");
293             return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
294         }
295     }
296
297     return SECURITY_MANAGER_API_SUCCESS;
298 }
299
300 int getPkgId(const std::string &appId, std::string &pkgId)
301 {
302     LogDebug("appId: " << appId);
303
304     try {
305         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
306             LogWarning("Application " << appId << " not found in database");
307             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
308         } else {
309             LogDebug("pkgId: " << pkgId);
310         }
311     } catch (const PrivilegeDb::Exception::Base &e) {
312         LogError("Error while getting pkgId from database: " << e.DumpToString());
313         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
314     }
315
316     return SECURITY_MANAGER_API_SUCCESS;
317 }
318
319 int getAppGroups(const std::string &appId, uid_t uid, pid_t pid, std::unordered_set<gid_t> &gids)
320 {
321     try {
322         std::string pkgId;
323         std::string smackLabel;
324         std::string uidStr = std::to_string(uid);
325         std::string pidStr = std::to_string(pid);
326
327         LogDebug("appId: " << appId);
328
329         if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
330             LogWarning("Application " << appId << " not found in database");
331             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
332         }
333         LogDebug("pkgId: " << pkgId);
334         if (!generatePkgLabel(pkgId, smackLabel)) {
335             LogError("Cannot generate Smack label for pkgId: " << pkgId);
336             return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
337         }
338         LogDebug("smack label: " << smackLabel);
339
340         std::vector<std::string> privileges;
341         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, uid, privileges);
342         /*there is also a need of checking, if privilege is granted to all users*/
343         size_t tmp = privileges.size();
344         PrivilegeDb::getInstance().GetPkgPrivileges(pkgId, getGlobalUserId(), privileges);
345         /*privileges needs to be sorted and with no duplications - for cynara sake*/
346         std::inplace_merge(privileges.begin(), privileges.begin() + tmp, privileges.end());
347         privileges.erase( unique( privileges.begin(), privileges.end() ), privileges.end() );
348
349         for (const auto &privilege : privileges) {
350             std::vector<std::string> gidsTmp;
351             PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, gidsTmp);
352             if (!gidsTmp.empty()) {
353                 LogDebug("Considering privilege " << privilege << " with " <<
354                     gidsTmp.size() << " groups assigned");
355                 if (Cynara::getInstance().check(smackLabel, privilege, uidStr, pidStr)) {
356                     for_each(gidsTmp.begin(), gidsTmp.end(), [&] (std::string group)
357                     {
358                         struct group *grp = getgrnam(group.c_str());
359                         if (grp == NULL) {
360                                 LogError("No such group: " << group.c_str());
361                                 return;
362                         }
363                         gids.insert(grp->gr_gid);
364                     });
365                     LogDebug("Cynara allowed, adding groups");
366                 } else
367                     LogDebug("Cynara denied, not adding groups");
368             }
369         }
370     } catch (const PrivilegeDb::Exception::Base &e) {
371         LogError("Database error: " << e.DumpToString());
372         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
373     } catch (const CynaraException::Base &e) {
374         LogError("Error while querying Cynara for permissions: " << e.DumpToString());
375         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
376     } catch (const std::bad_alloc &e) {
377         LogError("Memory allocation failed: " << e.what());
378         return SECURITY_MANAGER_API_ERROR_OUT_OF_MEMORY;
379     }
380
381     return SECURITY_MANAGER_API_SUCCESS;
382 }
383
384 int userAdd(uid_t uidAdded, int userType, uid_t uid)
385 {
386     if (uid != 0)
387         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
388
389     try {
390         CynaraAdmin::getInstance().UserInit(uidAdded, static_cast<security_manager_user_type>(userType));
391     } catch (CynaraException::InvalidParam &e) {
392         return SECURITY_MANAGER_API_ERROR_INPUT_PARAM;
393     }
394     return SECURITY_MANAGER_API_SUCCESS;
395 }
396
397 int userDelete(uid_t uidDeleted, uid_t uid)
398 {
399     int ret = SECURITY_MANAGER_API_SUCCESS;
400     if (uid != 0)
401         return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
402
403     //TODO remove policy information from cynara
404
405     /*Uninstall all user apps*/
406     std::vector<std::string> userApps;
407     try {
408         PrivilegeDb::getInstance().GetUserApps(uidDeleted, userApps);
409     } catch (const PrivilegeDb::Exception::Base &e) {
410         LogError("Error while getting user apps from database: " << e.DumpToString());
411         return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
412     }
413
414     for (auto &app: userApps) {
415         if (appUninstall(app, uidDeleted) != SECURITY_MANAGER_API_SUCCESS) {
416         /*if uninstallation of this app fails, just go on trying to uninstall another ones.
417         we do not have anything special to do about that matter - user will be deleted anyway.*/
418             ret = SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
419         }
420     }
421
422     return ret;
423 }
424
425 } /* namespace ServiceImpl */
426 } /* namespace SecurityManager */