2 * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Rafal Krypa <r.krypa@samsung.com>
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
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
33 #include <dpl/log/log.h>
34 #include <tzplatform_config.h>
36 #include "protocols.h"
37 #include "privilege_db.h"
39 #include "smack-rules.h"
40 #include "smack-labels.h"
42 #include "service_impl.h"
44 namespace SecurityManager {
45 namespace ServiceImpl {
47 static uid_t getGlobalUserId(void)
49 static uid_t globaluid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER);
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
58 static void checkGlobalUser(uid_t &uid, std::string &cynaraUserStr)
60 static uid_t globaluid = getGlobalUserId();
61 if (uid == 0 || uid == globaluid) {
63 cynaraUserStr = CYNARA_ADMIN_WILDCARD;
65 cynaraUserStr = std::to_string(static_cast<unsigned int>(uid));
68 static inline bool isSubDir(const char *parent, const char *subdir)
70 while (*parent && *subdir)
71 if (*parent++ != *subdir++)
74 return (*subdir == '/');
77 static inline bool installRequestAuthCheck(const app_inst_req &req, uid_t uid)
83 if (!pwd && errno != EINTR) {
84 LogError("getpwuid failed with '" << uid
85 << "' as parameter: " << strerror(errno));
90 std::unique_ptr<char, std::function<void(void*)>> home(
91 realpath(pwd->pw_dir, NULL), free);
93 LogError("realpath failed with '" << pwd->pw_dir
94 << "' as parameter: " << strerror(errno));
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));
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");
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");
122 int appInstall(const app_inst_req &req, uid_t uid)
124 std::vector<std::string> addedPermissions;
125 std::vector<std::string> removedPermissions;
126 std::vector<std::string> pkgContents;
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;
138 checkGlobalUser(uid, uidstr);
140 if (!installRequestAuthCheck(req, uid)) {
141 LogError("Request from uid " << uid << " for app installation denied");
142 return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
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;
151 LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
152 << ", generated smack label: " << smackLabel);
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();
160 pp_permissions[req.privileges.size()] = nullptr;
163 std::vector<std::string> oldAppPrivileges;
164 LogDebug("Install parameters: appId: " << req.appId << ", pkgId: " << req.pkgId
165 << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
167 PrivilegeDb::getInstance().BeginTransaction();
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;
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,
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;
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);
208 LogError("setupPath() failed");
209 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
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;
220 return SECURITY_MANAGER_API_SUCCESS;
223 int appUninstall(const std::string &appId, uid_t uid)
226 std::string smackLabel;
227 std::vector<std::string> pkgContents;
228 bool appExists = true;
229 bool removePkg = false;
231 checkGlobalUser(uid, uidstr);
234 std::vector<std::string> oldAppPrivileges;
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();
244 LogDebug("Uninstall parameters: appId: " << appId << ", pkgId: " << pkgId
245 << ", uidstr " << uidstr << ", generated smack label: " << smackLabel);
247 if (!generateAppLabel(appId, smackLabel)) {
248 LogError("Cannot generate Smack label for package: " << pkgId);
249 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
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");
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;
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;
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;
297 return SECURITY_MANAGER_API_SUCCESS;
300 int getPkgId(const std::string &appId, std::string &pkgId)
302 LogDebug("appId: " << appId);
305 if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
306 LogWarning("Application " << appId << " not found in database");
307 return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
309 LogDebug("pkgId: " << pkgId);
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;
316 return SECURITY_MANAGER_API_SUCCESS;
319 int getAppGroups(const std::string &appId, uid_t uid, pid_t pid, std::unordered_set<gid_t> &gids)
323 std::string smackLabel;
324 std::string uidStr = std::to_string(uid);
325 std::string pidStr = std::to_string(pid);
327 LogDebug("appId: " << appId);
329 if (!PrivilegeDb::getInstance().GetAppPkgId(appId, pkgId)) {
330 LogWarning("Application " << appId << " not found in database");
331 return SECURITY_MANAGER_API_ERROR_NO_SUCH_OBJECT;
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;
338 LogDebug("smack label: " << smackLabel);
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() );
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)
358 struct group *grp = getgrnam(group.c_str());
360 LogError("No such group: " << group.c_str());
363 gids.insert(grp->gr_gid);
365 LogDebug("Cynara allowed, adding groups");
367 LogDebug("Cynara denied, not adding groups");
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;
381 return SECURITY_MANAGER_API_SUCCESS;
384 int userAdd(uid_t uidAdded, int userType, uid_t uid)
387 return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
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;
394 return SECURITY_MANAGER_API_SUCCESS;
397 int userDelete(uid_t uidDeleted, uid_t uid)
399 int ret = SECURITY_MANAGER_API_SUCCESS;
401 return SECURITY_MANAGER_API_ERROR_AUTHENTICATION_FAILED;
403 /*Uninstall all user apps*/
404 std::vector<std::string> userApps;
406 PrivilegeDb::getInstance().GetUserApps(uidDeleted, userApps);
407 } catch (const PrivilegeDb::Exception::Base &e) {
408 LogError("Error while getting user apps from database: " << e.DumpToString());
409 return SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
412 for (auto &app: userApps) {
413 if (appUninstall(app, uidDeleted) != SECURITY_MANAGER_API_SUCCESS) {
414 /*if uninstallation of this app fails, just go on trying to uninstall another ones.
415 we do not have anything special to do about that matter - user will be deleted anyway.*/
416 ret = SECURITY_MANAGER_API_ERROR_SERVER_ERROR;
420 CynaraAdmin::getInstance().UserRemove(uidDeleted);
425 } /* namespace ServiceImpl */
426 } /* namespace SecurityManager */