From 04a23aabac88fd9351d8971f7e14bb4594906953 Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Fri, 8 Apr 2016 15:23:38 +0200 Subject: [PATCH 01/16] Add constraint error in database logic. [Problem] Constraint errors can't be distinguished from othes. [Solution] Introduce constraint error, update documentation and add fix exception handling in service_impl.cpp. [Verification] Run tests Change-Id: Ie16e02bdf7028fc28df0e4981d77879cb65eb3bf --- src/common/include/privilege_db.h | 26 ++++++++++++++++++++++++++ src/common/privilege_db.cpp | 4 ++++ src/common/service_impl.cpp | 18 +++++++++++++++++- src/dpl/db/include/dpl/db/sql_connection.h | 4 ++++ src/dpl/db/src/sql_connection.cpp | 3 +++ 5 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/common/include/privilege_db.h b/src/common/include/privilege_db.h index a7972e5..84d595c 100644 --- a/src/common/include/privilege_db.h +++ b/src/common/include/privilege_db.h @@ -159,6 +159,7 @@ public: DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base) DECLARE_EXCEPTION_TYPE(Base, IOError) DECLARE_EXCEPTION_TYPE(Base, InternalError) + DECLARE_EXCEPTION_TYPE(Base, ConstraintError) }; ~PrivilegeDb(void); @@ -191,6 +192,7 @@ public: * * @param appName - package identifier * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation * */ bool AppNameExists(const std::string &appName); @@ -200,6 +202,7 @@ public: * * @param pkgName - package identifier * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation * @return true if pkgName exists in the database * */ @@ -210,6 +213,7 @@ public: * * @param authorId numerical author identifier * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation * @return true if authorId exists in the database * */ @@ -221,6 +225,7 @@ public: * @param appName - application identifier * @param[out] pkgName - return application's package identifier * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetAppPkgName(const std::string &appName, std::string &pkgName); @@ -230,6 +235,7 @@ public: * @param appName - application identifier * @param[out] tizenVer - return application's target Tizen version * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetAppVersion(const std::string &appName, std::string &tizenVer); @@ -240,6 +246,7 @@ public: * @param uid - user identifier for whom privileges will be retrieved * @param[out] currentPrivileges - list of current privileges assigned to the package * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetPkgPrivileges(const std::string &pkgName, uid_t uid, std::vector ¤tPrivilege); @@ -251,6 +258,7 @@ public: * @param uid - user identifier for whom privileges will be retrieved * @param[out] currentPrivileges - list of current privileges assigned to appName * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetAppPrivileges(const std::string &appName, uid_t uid, std::vector ¤tPrivileges); @@ -264,6 +272,7 @@ public: * @param targetTizenVer - target tizen version for application * @param author - author identifier * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void AddApplication( const std::string &appName, @@ -281,6 +290,7 @@ public: * @param[out] pkgNameIsNoMore - return info if pkgName is in the database * @param[out] authorNameIsNoMore - return info if authorName is in the database * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void RemoveApplication( const std::string &appName, @@ -295,6 +305,7 @@ public: * @param appName - application identifier * @param uid - user identifier for whom privileges will be removed * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void RemoveAppPrivileges(const std::string &appName, uid_t uid); @@ -306,6 +317,7 @@ public: * @param uid - user identifier for whom privileges will be updated * @param privileges - list of privileges to assign * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void UpdateAppPrivileges(const std::string &appName, uid_t uid, const std::vector &privileges); @@ -316,6 +328,7 @@ public: * @param path - path name * @param[out] count - count of sharing * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetPathSharingCount(const std::string &path, int &count); @@ -326,6 +339,7 @@ public: * @param targetAppName - application identifier * @param[out] count - count of sharing * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetOwnerTargetSharingCount(const std::string &ownerAppName, const std::string &targetAppName, int &count); @@ -337,6 +351,7 @@ public: * @param path - user identifier for whom privileges will be updated * @param[out] count - count of sharing * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetTargetPathSharingCount(const std::string &targetAppName, const std::string &path, @@ -350,6 +365,7 @@ public: * @param path - path name * @param pathLabel - label of path * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void ApplyPrivateSharing(const std::string &ownerAppName, const std::string &targetAppName, const std::string &path, const std::string &pathLabel); @@ -361,6 +377,7 @@ public: * @param targetAppName - application identifier * @param path - path name * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void DropPrivateSharing(const std::string &ownerAppName, const std::string &targetAppName, const std::string &path); @@ -370,6 +387,7 @@ public: * * @param appPathMap - map containing vectors of paths shared by applications mapped by name * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetAllPrivateSharing(std::map> &appPathMap); @@ -377,6 +395,7 @@ public: * Clear information about private sharing. * * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void ClearPrivateSharing(); @@ -386,6 +405,7 @@ public: * @param privilege - privilege identifier * @param[out] grp_names - list of group names assigned to the privilege * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetPrivilegeGroups(const std::string &privilege, std::vector &grp_names); @@ -398,6 +418,7 @@ public: * this parameter do not need to be empty, but * it is being overwritten during function call. * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetUserApps(uid_t uid, std::vector &apps); @@ -407,6 +428,7 @@ public: * @param pkgName - package identifier * @param[out] appNames - list of application identifiers for the package * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetPkgApps(const std::string &pkgName, std::vector &appNames); @@ -418,6 +440,7 @@ public: * this parameter do not need to be empty, but * it is being overwritten during function call. * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetTizen2XApps(const std::string &origApp, std::vector &apps); @@ -432,6 +455,7 @@ public: * this parameter do not need to be empty, but * it is being overwritten during function call. * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetTizen2XAppsAndPackages(const std::string& origApp, std::vector &apps, std::vector &packages); @@ -442,6 +466,7 @@ public: * @param authorId[out] author id associated with the application, or -1 if no * author was assigned during installation * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetAppAuthorId(const std::string &appName, int &authorId); @@ -450,6 +475,7 @@ public: * * @param[out] grp_names - list of group names * @exception DB::SqlConnection::Exception::InternalError on internal error + * @exception DB::SqlConnection::Exception::ConstraintError on constraint violation */ void GetGroups(std::vector &grp_names); }; diff --git a/src/common/privilege_db.cpp b/src/common/privilege_db.cpp index 88b1abc..d0d7df2 100644 --- a/src/common/privilege_db.cpp +++ b/src/common/privilege_db.cpp @@ -47,6 +47,10 @@ T try_catch(const std::function &func) LogError("Syntax error in command: " << e.DumpToString()); ThrowMsg(PrivilegeDb::Exception::InternalError, "Syntax error in command: " << e.DumpToString()); + } catch (DB::SqlConnection::Exception::ConstraintError &e) { + LogError("Constraints violated by command: " << e.DumpToString()); + ThrowMsg(PrivilegeDb::Exception::ConstraintError, + "Constraints violated by command: " << e.DumpToString()); } catch (DB::SqlConnection::Exception::InternalError &e) { LogError("Mysterious internal error in SqlConnection class" << e.DumpToString()); ThrowMsg(PrivilegeDb::Exception::InternalError, diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 9d3da26..0186323 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -348,6 +348,10 @@ int ServiceImpl::appInstall(const app_inst_req &req, uid_t uid) } catch (const PrivilegeDb::Exception::IOError &e) { LogError("Cannot access application database: " << e.DumpToString()); return SECURITY_MANAGER_ERROR_SERVER_ERROR; + } catch (const PrivilegeDb::Exception::ConstraintError &e) { + PrivilegeDb::getInstance().RollbackTransaction(); + LogError("Application conflicts with existing one: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_INPUT_PARAM; } catch (const PrivilegeDb::Exception::InternalError &e) { PrivilegeDb::getInstance().RollbackTransaction(); LogError("Error while saving application info to database: " << e.DumpToString()); @@ -446,7 +450,7 @@ int ServiceImpl::appUninstall(const std::string &appName, uid_t uid) } catch (const PrivilegeDb::Exception::IOError &e) { LogError("Cannot access application database: " << e.DumpToString()); return SECURITY_MANAGER_ERROR_SERVER_ERROR; - } catch (const PrivilegeDb::Exception::InternalError &e) { + } catch (const PrivilegeDb::Exception::Base &e) { PrivilegeDb::getInstance().RollbackTransaction(); LogError("Error while removing application info from database: " << e.DumpToString()); return SECURITY_MANAGER_ERROR_SERVER_ERROR; @@ -897,6 +901,9 @@ int ServiceImpl::getPolicy(const policy_entry &filter, uid_t uid, pid_t pid, con }; }; + } catch (const PrivilegeDb::Exception::Base &e) { + LogError("Error while getting application privileges from database: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_SERVER_ERROR; } catch (const CynaraException::Base &e) { LogError("Error while listing Cynara rules: " << e.DumpToString()); return SECURITY_MANAGER_ERROR_SERVER_ERROR; @@ -1000,6 +1007,9 @@ int ServiceImpl::dropOnePrivateSharing( SmackRules::dropPrivateSharingRules(ownerPkgName, ownerPkgContents, targetAppName, pathLabel, pathCount < 1, ownerTargetCount < 1); return SECURITY_MANAGER_SUCCESS; + } catch (const PrivilegeDb::Exception::Base &e) { + LogError("Error while dropping private sharing in database: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_SERVER_ERROR; } catch (const SmackException::Base &e) { LogError("Error performing smack operation: " << e.GetMessage()); errorRet = SECURITY_MANAGER_ERROR_SERVER_ERROR; @@ -1083,6 +1093,9 @@ int ServiceImpl::applyPrivatePathSharing( } trans.commit(); return SECURITY_MANAGER_SUCCESS; + } catch (const PrivilegeDb::Exception::Base &e) { + LogError("Error while applying private sharing in database: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_SERVER_ERROR; } catch (const SmackException::Base &e) { LogError("Error performing smack operation: " << e.GetMessage()); errorRet = SECURITY_MANAGER_ERROR_SERVER_ERROR; @@ -1159,6 +1172,9 @@ int ServiceImpl::dropPrivatePathSharing( } trans.commit(); return SECURITY_MANAGER_SUCCESS; + } catch (const PrivilegeDb::Exception::Base &e) { + LogError("Error while dropping private sharing in database: " << e.DumpToString()); + return SECURITY_MANAGER_ERROR_SERVER_ERROR; } catch (const SmackException::Base &e) { LogError("Error performing smack operation: " << e.GetMessage()); errorRet = SECURITY_MANAGER_ERROR_SERVER_ERROR; diff --git a/src/dpl/db/include/dpl/db/sql_connection.h b/src/dpl/db/include/dpl/db/sql_connection.h index 185af59..85f1456 100644 --- a/src/dpl/db/include/dpl/db/sql_connection.h +++ b/src/dpl/db/include/dpl/db/sql_connection.h @@ -54,6 +54,7 @@ class SqlConnection DECLARE_EXCEPTION_TYPE(Base, ConnectionBroken) DECLARE_EXCEPTION_TYPE(Base, InternalError) DECLARE_EXCEPTION_TYPE(Base, InvalidColumn) + DECLARE_EXCEPTION_TYPE(Base, ConstraintError) }; typedef int ColumnIndex; @@ -242,6 +243,9 @@ class SqlConnection * Execute the prepared statement and/or move * to the next row of the result * + * @throw Exception::ConstraintError in case of constraint violation + * @throw Exception::InternalError in case of other sqlite error + * * @return True when there was a row returned */ bool Step(); diff --git a/src/dpl/db/src/sql_connection.cpp b/src/dpl/db/src/sql_connection.cpp index ccea00b..997df19 100644 --- a/src/dpl/db/src/sql_connection.cpp +++ b/src/dpl/db/src/sql_connection.cpp @@ -369,6 +369,9 @@ bool SqlConnection::DataCommand::Step() LogPedantic("SQL step data command failed"); LogPedantic(" Error: " << error); + if (ret == SQLITE_CONSTRAINT) { + ThrowMsg(Exception::ConstraintError, error); + } ThrowMsg(Exception::InternalError, error); } } -- 2.7.4 From 20a1fc20ecbcd86066c75687dec3247b6e06bdf3 Mon Sep 17 00:00:00 2001 From: Lukasz Kostyra Date: Wed, 16 Mar 2016 16:36:47 +0100 Subject: [PATCH 02/16] db: Add update script The script performs update of current security-manager database by using intermediate schema updaters (located in db/updates) and by applying views from main db schema (db/db.sql). Verification: Build with higher package version, upgrade package with rpm -Uh. The script will activate and update DB version on target to 1. Change-Id: I4d185f7e47d4ae9df53349627b8f97be22ef2642 Signed-off-by: Lukasz Kostyra Signed-off-by: Zbigniew Jasinski Signed-off-by: Rafal Krypa --- db/240.security-manager.db-update.sh | 3 +++ db/CMakeLists.txt | 18 ++++++------- db/db.sql | 2 +- db/update.sh | 52 ++++++++++++++++++++++++++++++++++++ db/updates/update-db-to-v1.sql | 5 ++++ packaging/security-manager.spec | 11 ++++++++ 6 files changed, 80 insertions(+), 11 deletions(-) create mode 100755 db/240.security-manager.db-update.sh create mode 100755 db/update.sh create mode 100644 db/updates/update-db-to-v1.sql diff --git a/db/240.security-manager.db-update.sh b/db/240.security-manager.db-update.sh new file mode 100755 index 0000000..f3d8786 --- /dev/null +++ b/db/240.security-manager.db-update.sh @@ -0,0 +1,3 @@ +#!/bin/sh -e + +exec /usr/share/security-manager/db/update.sh diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt index 9e8ffcc..d2dbdcf 100644 --- a/db/CMakeLists.txt +++ b/db/CMakeLists.txt @@ -1,12 +1,10 @@ -SET(TARGET_DB ".security-manager.db") +SET(DB_SCRIPT_DIR "${SHARE_INSTALL_PREFIX}/${PROJECT_NAME}/db") +SET(FOTA_DIR "${SYSCONF_INSTALL_DIR}/opt/upgrade") -ADD_CUSTOM_COMMAND( - OUTPUT ${TARGET_DB} ${TARGET_DB}-journal - COMMAND sqlite3 ${TARGET_DB} Date: Thu, 14 Apr 2016 11:03:16 +0200 Subject: [PATCH 03/16] db: update schema to version 2 Since last release database schema was modified. We now have proper tools for handling such changes. The update to version 2 covers all schema differences since last release. Change-Id: I5bbc3297065468f17f28d15c28c5232c34d3507f Signed-off-by: Rafal Krypa --- db/db.sql | 2 +- db/updates/update-db-to-v2.sql | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 db/updates/update-db-to-v2.sql diff --git a/db/db.sql b/db/db.sql index 54e8143..8d85ba2 100644 --- a/db/db.sql +++ b/db/db.sql @@ -4,7 +4,7 @@ PRAGMA auto_vacuum = NONE; BEGIN EXCLUSIVE TRANSACTION; -PRAGMA user_version = 1; +PRAGMA user_version = 2; CREATE TABLE IF NOT EXISTS pkg ( pkg_id INTEGER PRIMARY KEY, diff --git a/db/updates/update-db-to-v2.sql b/db/updates/update-db-to-v2.sql new file mode 100644 index 0000000..00a4dfc --- /dev/null +++ b/db/updates/update-db-to-v2.sql @@ -0,0 +1,24 @@ +BEGIN EXCLUSIVE TRANSACTION; + +PRAGMA user_version = 2; + +DROP TABLE version; + +CREATE TABLE app_private_sharing_new ( + owner_app_name TEXT NOT NULL, + target_app_name TEXT NOT NULL, + path_id INTEGER NOT NULL, + counter INTEGER NOT NULL, + PRIMARY KEY (owner_app_name, target_app_name, path_id) + FOREIGN KEY (path_id) REFERENCES shared_path (path_id) +); + +INSERT INTO app_private_sharing_new + SELECT owner_app_name, target_app_name, path_id, counter + FROM app_private_sharing_view + LEFT JOIN shared_path USING (path); + +DROP TABLE app_private_sharing; +ALTER TABLE app_private_sharing_new RENAME TO app_private_sharing; + +COMMIT TRANSACTION; -- 2.7.4 From 675972c373489c107e5a5dbf2efa3e545a91dc9c Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Wed, 13 Apr 2016 16:55:51 +0200 Subject: [PATCH 04/16] Integrate with Cynara, clients must be privileged Several API functions now require the caller to hold appropriate privilege. Ultimately new internal privileges will be created and used by security-manager. For now, when appropriate privilege is missing, use "notexist" privilege placeholder. Privileges required per API: - security_manager_app_install * http://tizen.org/privilege/notexist (private installation) * http://tizen.org/privilege/notexist (global installation) - security_manager_app_uninstall * http://tizen.org/privilege/notexist (private uninstallation) * http://tizen.org/privilege/notexist (global uninstallation) - security_manager_private_sharing_apply * http://tizen.org/privilege/notexist - security_manager_private_sharing_drop * http://tizen.org/privilege/notexist - security_manager_policy_update_send * http://tizen.org/privilege/notexist (for setting own policy) * http://tizen.org/privilege/internal/usermanagement (for setting policy for other or all) - security_manager_get_configured_policy_for_admin * http://tizen.org/privilege/internal/usermanagement - security_manager_get_configured_policy_for_self * http://tizen.org/privilege/notexist - security_manager_get_policy * http://tizen.org/privilege/notexist (for fetching own policy) * http://tizen.org/privilege/internal/usermanagement (for fetching policy for other or all) - security_manager_user_add * http://tizen.org/privilege/internal/usermanagement - security_manager_user_delete * http://tizen.org/privilege/internal/usermanagement Change-Id: Id67473db434b13d977fbd2fa704db3ac1bd1c32b Signed-off-by: Rafal Krypa --- src/client/client-security-manager.cpp | 7 +- src/common/CMakeLists.txt | 1 + src/common/config.cpp | 10 +- src/common/credentials.cpp | 47 +++++ src/common/include/config.h | 10 +- src/common/include/credentials.h | 56 ++++++ src/common/include/service_impl.h | 59 +++--- src/common/include/smack-labels.h | 10 +- src/common/service_impl.cpp | 322 +++++++++++++++++------------- src/common/smack-labels.cpp | 13 +- src/include/app-manager.h | 8 + src/include/app-sharing.h | 6 + src/include/policy-manager.h | 24 ++- src/include/user-manager.h | 6 + src/server/service/base-service.cpp | 22 +- src/server/service/include/base-service.h | 14 +- src/server/service/include/service.h | 51 +++-- src/server/service/service.cpp | 79 ++++---- 18 files changed, 454 insertions(+), 291 deletions(-) create mode 100644 src/common/credentials.cpp create mode 100644 src/common/include/credentials.h diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 1e0fbcf..3420e9a 100755 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -47,6 +47,7 @@ #include #include #include +#include #include #include @@ -192,7 +193,8 @@ int security_manager_app_install(const app_inst_req *p_req) int retval; ClientOffline offlineMode; if (offlineMode.isOffline()) { - retval = SecurityManager::ServiceImpl().appInstall(*p_req, geteuid()); + Credentials creds = SecurityManager::Credentials::getCredentialsFromSelf(); + retval = SecurityManager::ServiceImpl().appInstall(creds, app_inst_req(*p_req)); } else { MessageBuffer send, recv; @@ -578,7 +580,8 @@ int security_manager_user_add(const user_req *p_req) int retval; ClientOffline offlineMode; if (offlineMode.isOffline()) { - retval = SecurityManager::ServiceImpl().userAdd(p_req->uid, p_req->utype, geteuid()); + Credentials creds = SecurityManager::Credentials::getCredentialsFromSelf(); + retval = SecurityManager::ServiceImpl().userAdd(creds, p_req->uid, p_req->utype); } else { MessageBuffer send, recv; //server is working diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 3a9d3c9..582f7db 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -44,6 +44,7 @@ SET(COMMON_SOURCES ${DPL_PATH}/db/src/sql_connection.cpp ${COMMON_PATH}/config.cpp ${COMMON_PATH}/connection.cpp + ${COMMON_PATH}/credentials.cpp ${COMMON_PATH}/cynara.cpp ${COMMON_PATH}/file-lock.cpp ${COMMON_PATH}/protocols.cpp diff --git a/src/common/config.cpp b/src/common/config.cpp index 445662b..8b15c21 100644 --- a/src/common/config.cpp +++ b/src/common/config.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2015-2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -35,6 +35,14 @@ const std::string PRIVILEGE_VERSION = "3.0" #endif ; + +const std::string PRIVILEGE_APPINST_USER = "http://tizen.org/privilege/notexist"; +const std::string PRIVILEGE_APPINST_ADMIN = "http://tizen.org/privilege/notexist"; +const std::string PRIVILEGE_USER_ADMIN = "http://tizen.org/privilege/internal/usermanagement"; +const std::string PRIVILEGE_POLICY_USER = "http://tizen.org/privilege/notexist"; +const std::string PRIVILEGE_POLICY_ADMIN = "http://tizen.org/privilege/internal/usermanagement"; +const std::string PRIVILEGE_APPSHARING_ADMIN = "http://tizen.org/privilege/notexist"; + }; } /* namespace SecurityManager */ diff --git a/src/common/credentials.cpp b/src/common/credentials.cpp new file mode 100644 index 0000000..171326e --- /dev/null +++ b/src/common/credentials.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/* + * @file credentials.cpp + * @author Rafal Krypa + * @version 1.0 + */ + +#include +#include + +#include "smack-labels.h" +#include "credentials.h" + +namespace SecurityManager { + +Credentials Credentials::getCredentialsFromSelf(void) +{ + return Credentials(getpid(), geteuid(), getegid(), + SmackLabels::getSmackLabelFromSelf()); +} + +Credentials Credentials::getCredentialsFromSocket(int sock) +{ + struct ucred cr; + socklen_t len = sizeof(cr); + + if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &len) == -1) + ThrowMsg(Exception::SocketError, "Failed to read peer credentials for sockfd " << sock); + + return Credentials(cr.pid, cr.uid, cr.gid, SmackLabels::getSmackLabelFromSocket(sock)); +} + +} // namespace SecurityManager diff --git a/src/common/include/config.h b/src/common/include/config.h index 742b092..4bb655f 100644 --- a/src/common/include/config.h +++ b/src/common/include/config.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2015-2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -33,6 +33,14 @@ namespace Config { extern const std::string PRIVILEGE_VERSION; +/* Privileges required from users of our API */ +extern const std::string PRIVILEGE_APPINST_USER; +extern const std::string PRIVILEGE_APPINST_ADMIN; +extern const std::string PRIVILEGE_USER_ADMIN; +extern const std::string PRIVILEGE_POLICY_USER; +extern const std::string PRIVILEGE_POLICY_ADMIN; +extern const std::string PRIVILEGE_APPSHARING_ADMIN; + }; } /* namespace SecurityManager */ diff --git a/src/common/include/credentials.h b/src/common/include/credentials.h new file mode 100644 index 0000000..003a37e --- /dev/null +++ b/src/common/include/credentials.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/* + * @file credentials.h + * @author Rafal Krypa + * @version 1.0 + */ + +#ifndef SECURITY_MANAGER_CREDENTIALS_ +#define SECURITY_MANAGER_CREDENTIALS_ + +#include +#include + +#include + +namespace SecurityManager { + +class Credentials { +public: + pid_t pid; /* process ID of the sending process */ + uid_t uid; /* user ID of the sending process */ + gid_t gid; /* group ID of the sending process */ + std::string label; /* security context of the sending process */ + + Credentials() = delete; + static Credentials getCredentialsFromSelf(void); + static Credentials getCredentialsFromSocket(int socket); + + class Exception { + public: + DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, SocketError) + }; + +private: + Credentials(pid_t pid, uid_t uid, gid_t gid, std::string &&label) : + pid(pid), uid(uid), gid(gid), label(std::move(label)) {} +}; + +} // namespace SecurityManager + +#endif /* SECURITY_MANAGER_CREDENTIALS_ */ diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index 3eb25ae..b6ffc9b 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -29,6 +29,7 @@ #include +#include "credentials.h" #include "security-manager.h" namespace SecurityManager { @@ -37,13 +38,15 @@ class ServiceImpl { private: static uid_t getGlobalUserId(void); - static void checkGlobalUser(uid_t &uid, std::string &cynaraUserStr); - static bool isSubDir(const char *parent, const char *subdir); static bool getUserAppDir(const uid_t &uid, std::string &userAppDir); - static bool installRequestAuthCheck(const app_inst_req &req, uid_t uid, std::string &appPath); + static void installRequestMangle(app_inst_req &req, std::string &cynaraUserStr); + + static bool installRequestAuthCheck(const Credentials &creds, const app_inst_req &req); + + static bool installRequestPathsCheck(const app_inst_req &req, std::string &appPath); static bool getZoneId(std::string &zoneId); @@ -60,22 +63,23 @@ public: /** * Process application installation request. * + * @param[in] creds credentials of the requesting process * @param[in] req installation request - * @param[in] uid id of the requesting user * * @return API return code, as defined in protocols.h */ - int appInstall(const app_inst_req &req, uid_t uid); + int appInstall(const Credentials &creds, app_inst_req &&req); /** * Process application uninstallation request. * - * @param[in] appName application identifier - * @param[in] uid id of the requesting user + * @param[in] creds credentials of the requesting process + * @param[in] req uninstallation request + * @param[in] authenticated whether the caller has been already checked against Cynara policy * * @return API return code, as defined in protocols.h */ - int appUninstall(const std::string &appName, uid_t uid); + int appUninstall(const Credentials &creds, app_inst_req &&req, bool authenticated = false); /** * Process package id query. @@ -95,49 +99,46 @@ public: * queried. * Returns set of group ids that are permitted. * + * @param[in] creds credentials of the requesting process * @param[in] appName application identifier - * @param[in] uid id of the requesting user - * @param[in] pid id of the requesting process (to construct Cynara session id) * @param[out] gids returned set of allowed group ids * * @return API return code, as defined in protocols.h */ - int getAppGroups(const std::string &appName, uid_t uid, pid_t pid, std::unordered_set &gids); + int getAppGroups(const Credentials &creds, const std::string &appName, std::unordered_set &gids); /** * Process user adding request. * + * @param[in] creds credentials of the requesting process * @param[in] uidAdded uid of newly created user * @param[in] userType type of newly created user - * @param[in] uid uid of requesting user * * @return API return code, as defined in protocols.h */ - int userAdd(uid_t uidAdded, int userType, uid_t uid); + int userAdd(const Credentials &creds, uid_t uidAdded, int userType); /** * Process user deletion request. * + * @param[in] creds credentials of the requesting process * @param[in] uidDeleted uid of removed user - * @param[in] uid uid of requesting user * * @return API return code, as defined in protocols.h */ - int userDelete(uid_t uidDeleted, uid_t uid); + int userDelete(const Credentials &creds, uid_t uidDeleted); /** * Update policy in Cynara - proper privilege: http://tizen.org/privilege/internal/usermanagement * is needed for this to succeed * + * @param[in] creds credentials of the requesting process * @param[in] policyEntries vector of policy chunks with instructions - * @param[in] uid identifier of requesting user - * @param[in] pid PID of requesting process - * @param[in] smackLabel smack label of requesting app * * @return API return code, as defined in protocols.h */ + int policyUpdate(const Credentials &creds, const std::vector &policyEntries); - int policyUpdate(const std::vector &policyEntries, uid_t uid, pid_t pid, const std::string &smackLabel); /** * Fetch all configured privileges from user configurable bucket. * Depending on forAdmin value: personal user policies or admin enforced @@ -145,26 +146,22 @@ public: * * @param[in] forAdmin determines if user is asking as ADMIN or not * @param[in] filter filter for limiting the query - * @param[in] uid identifier of queried user - * @param[in] pid PID of requesting process * @param[out] policyEntries vector of policy entries with result * * @return API return code, as defined in protocols.h */ - int getConfiguredPolicy(bool forAdmin, const policy_entry &filter, uid_t uid, pid_t pid, const std::string &smackLabel, std::vector &policyEntries); + int getConfiguredPolicy(const Credentials &creds, bool forAdmin, const policy_entry &filter, std::vector &policyEntries); /** * Fetch all privileges for all apps installed for specific user. * - * @param[in] forAdmin determines if user is asking as ADMIN or not + * @param[in] creds credentials of the requesting process * @param[in] filter filter for limiting the query - * @param[in] uid identifier of queried user - * @param[in] pid PID of requesting process * @param[out] policyEntries vector of policy entries with result * * @return API return code, as defined in protocols.h */ - int getPolicy(const policy_entry &filter, uid_t uid, pid_t pid, const std::string &smackLabel, std::vector &policyEntries); + int getPolicy(const Credentials &creds, const policy_entry &filter, std::vector &policyEntries); /** * Process getting policy descriptions list. @@ -199,25 +196,29 @@ public: /** * Process applying private path sharing between applications. * + * @param[in] creds credentials of the requesting process * @param[in] ownerAppName application owning paths * @param[in] targetAppName application which paths will be shared with * @param[in] paths vector of paths to be shared * * @return API return code, as defined in protocols.h */ - int applyPrivatePathSharing(const std::string &ownerAppName, + int applyPrivatePathSharing(const Credentials &creds, + const std::string &ownerAppName, const std::string &targetAppName, const std::vector &paths); /** * Process droping private path sharing between applications. * + * @param[in] creds credentials of the requesting process * @param[in] ownerAppName application owning paths * @param[in] targetAppName application which paths won't be anymore shared with * @param[in] paths vector of paths to be stopped being shared * @return API return code, as defined in protocols.h */ - int dropPrivatePathSharing(const std::string &ownerAppName, + int dropPrivatePathSharing(const Credentials &creds, + const std::string &ownerAppName, const std::string &targetAppName, const std::vector &paths); }; diff --git a/src/common/include/smack-labels.h b/src/common/include/smack-labels.h index d9138d9..fd3921c 100644 --- a/src/common/include/smack-labels.h +++ b/src/common/include/smack-labels.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -147,6 +147,14 @@ std::string getSmackLabelFromPid(pid_t pid); */ std::string getSmackLabelFromPath(const std::string &path); +/** + * Returns smack label for current process + * + * @param[in] sock socket file descriptor + * @return resulting Smack label + */ +std::string getSmackLabelFromSelf(void); + } // namespace SmackLabels } // namespace SecurityManager diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 0186323..66b0b1c 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -48,9 +48,6 @@ namespace SecurityManager { -static const std::string ADMIN_PRIVILEGE = "http://tizen.org/privilege/internal/usermanagement"; -static const std::string SELF_PRIVILEGE = "http://tizen.org/privilege/notexist"; - namespace { static inline int validatePolicy(policy_entry &policyEntry, std::string uidStr, bool &forAdmin, CynaraAdminPolicy &cyap) @@ -183,22 +180,6 @@ uid_t ServiceImpl::getGlobalUserId(void) return globaluid; } -/** - * Unifies user data of apps installed for all users - * @param uid peer's uid - may be changed during process - * @param cynaraUserStr string to which cynara user parameter will be put - */ -void ServiceImpl::checkGlobalUser(uid_t &uid, std::string &cynaraUserStr) -{ - static uid_t globaluid = getGlobalUserId(); - if (uid == 0 || uid == globaluid) { - uid = globaluid; - cynaraUserStr = CYNARA_ADMIN_WILDCARD; - } else { - cynaraUserStr = std::to_string(static_cast(uid)); - } -} - bool ServiceImpl::isSubDir(const char *parent, const char *subdir) { while (*parent && *subdir) @@ -244,26 +225,51 @@ bool ServiceImpl::getUserAppDir(const uid_t &uid, std::string &userAppDir) return true; } -bool ServiceImpl::installRequestAuthCheck(const app_inst_req &req, uid_t uid, std::string &appPath) +void ServiceImpl::installRequestMangle(app_inst_req &req, std::string &cynaraUserStr) { - std::string userHome; - std::string userAppDir; - std::stringstream correctPath; + uid_t globalUid = getGlobalUserId(); - if (uid != getGlobalUserId()) - LogDebug("Installation type: single user"); - else + if (req.uid == 0) + req.uid = globalUid; + + if (req.uid == globalUid) { LogDebug("Installation type: global installation"); + cynaraUserStr = CYNARA_ADMIN_WILDCARD; + } else { + LogDebug("Installation type: local installation"); + cynaraUserStr = std::to_string(static_cast(req.uid)); + } +} + +bool ServiceImpl::installRequestAuthCheck(const Credentials &creds, const app_inst_req &req) +{ + if (req.uid == getGlobalUserId() || req.uid != creds.uid) { + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPINST_ADMIN, + std::to_string(creds.uid), std::to_string(creds.pid))) { + LogError("Caller is not permitted to install applications globally"); + return false; + } + } else { + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPINST_USER, + std::to_string(creds.uid), std::to_string(creds.pid))) { + LogError("Caller is not permitted to install applications"); + return false; + } + } - if (!getUserAppDir(uid, userAppDir)) { - LogError("Failed getting app dir for user uid: " << uid); + return true; +} + +bool ServiceImpl::installRequestPathsCheck(const app_inst_req &req, std::string &appPath) +{ + if (!getUserAppDir(req.uid, appPath)) { + LogError("Failed getting app dir for user uid: " << req.uid); return false; } - appPath = userAppDir; - correctPath.clear(); - correctPath << userAppDir << "/" << req.pkgName; - LogDebug("correctPath: " << correctPath.str()); + std::string correctPath{appPath}; + correctPath.append("/").append(req.pkgName); + LogDebug("correctPath: " << correctPath); for (const auto &path : req.appPaths) { std::unique_ptr> real_path( @@ -274,41 +280,41 @@ bool ServiceImpl::installRequestAuthCheck(const app_inst_req &req, uid_t uid, st return false; } LogDebug("Requested path is '" << path.first.c_str() - << "'. User's APPS_DIR is '" << userAppDir << "'"); - if (!isSubDir(correctPath.str().c_str(), real_path.get())) { - LogWarning("Installation is outside correct path: " << correctPath.str() << "," << real_path.get()); + << "'. User's APPS_DIR is '" << appPath << "'"); + if (!isSubDir(correctPath.c_str(), real_path.get())) { + LogWarning("Installation is outside correct path: " << correctPath << "," << real_path.get()); return false; } } return true; } -int ServiceImpl::appInstall(const app_inst_req &req, uid_t uid) +int ServiceImpl::appInstall(const Credentials &creds, app_inst_req &&req) { std::vector addedPermissions; std::vector removedPermissions; std::vector pkgContents; - std::string uidstr; + std::string cynaraUserStr; std::string appPath; std::string appLabel; std::string pkgLabel; std::vector allTizen2XApps, allTizen2XPackages; int authorId; - if (uid) { - if (uid != req.uid) { - LogError("User " << uid << - " is denied to install application for user " << req.uid); - return SECURITY_MANAGER_ERROR_ACCESS_DENIED; - } - } else { - if (req.uid) - uid = req.uid; + installRequestMangle(req, cynaraUserStr); + + LogDebug("Install parameters: appName: " << req.appName << ", pkgName: " << req.pkgName + << ", uid: " << req.uid << ", target Tizen API ver: " + << (req.tizenVersion.empty() ? "unknown" : req.tizenVersion)); + + if (!installRequestAuthCheck(creds, req)) { + LogError("Request from uid=" << creds.uid << ", Smack=" << creds.label << + " for app installation denied"); + return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; } - checkGlobalUser(uid, uidstr); - if (!installRequestAuthCheck(req, uid, appPath)) { - LogError("Request from uid " << uid << " for app installation denied"); + if (!installRequestPathsCheck(req, appPath)) { + LogError("Installation request with paths outside expected application path"); return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; } @@ -317,10 +323,8 @@ int ServiceImpl::appInstall(const app_inst_req &req, uid_t uid) /* NOTE: we don't use pkgLabel here, but generate it for pkgName validation */ pkgLabel = SmackLabels::generatePkgLabel(req.pkgName); - LogDebug("Install parameters: appName: " << req.appName << ", pkgName: " << req.pkgName - << ", uidstr " << uidstr - << ", app label: " << appLabel << ", pkg label: " << pkgLabel - << ", target Tizen API ver: " << (req.tizenVersion.empty()?"unknown":req.tizenVersion)); + LogDebug("Generated install parameters: " + << "app label: " << appLabel << ", pkg label: " << pkgLabel); PrivilegeDb::getInstance().BeginTransaction(); std::string pkg; @@ -331,12 +335,12 @@ int ServiceImpl::appInstall(const app_inst_req &req, uid_t uid) return SECURITY_MANAGER_ERROR_INPUT_PARAM; } - PrivilegeDb::getInstance().AddApplication(req.appName, req.pkgName, uid, req.tizenVersion, req.authorName); - PrivilegeDb::getInstance().UpdateAppPrivileges(req.appName, uid, req.privileges); + PrivilegeDb::getInstance().AddApplication(req.appName, req.pkgName, req.uid, req.tizenVersion, req.authorName); + PrivilegeDb::getInstance().UpdateAppPrivileges(req.appName, req.uid, req.privileges); /* Get all application ids in the package to generate rules withing the package */ PrivilegeDb::getInstance().GetPkgApps(req.pkgName, pkgContents); PrivilegeDb::getInstance().GetAppAuthorId(req.appName, authorId); - CynaraAdmin::getInstance().UpdateAppPolicy(appLabel, uidstr, req.privileges); + CynaraAdmin::getInstance().UpdateAppPolicy(appLabel, cynaraUserStr, req.privileges); // if app is targetted to Tizen 2.X, give other 2.X apps RO rules to it's shared dir if(isTizen2XVersion(req.tizenVersion)) @@ -401,49 +405,59 @@ int ServiceImpl::appInstall(const app_inst_req &req, uid_t uid) return SECURITY_MANAGER_SUCCESS; } -int ServiceImpl::appUninstall(const std::string &appName, uid_t uid) +int ServiceImpl::appUninstall(const Credentials &creds, app_inst_req &&req, + bool authenticated) { - std::string pkgName; std::string tizenVersion; std::string smackLabel; std::vector pkgContents; bool removeApp = false; bool removePkg = false; bool removeAuthor = false; - std::string uidstr; + std::string cynaraUserStr; std::vector allTizen2XApps; int authorId; - checkGlobalUser(uid, uidstr); + installRequestMangle(req, cynaraUserStr); + + LogDebug("Uninstall parameters: appName=" << req.appName << ", uid=" << req.uid); + + if (!authenticated && !installRequestAuthCheck(creds, req)) { + LogError("Request from uid=" << creds.uid << ", Smack=" << creds.label << + " for app uninstallation denied"); + return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; + } try { PrivilegeDb::getInstance().BeginTransaction(); - PrivilegeDb::getInstance().GetAppPkgName(appName, pkgName); - if (pkgName.empty()) { - LogWarning("Application " << appName << + if (req.pkgName.empty()) + PrivilegeDb::getInstance().GetAppPkgName(req.appName, req.pkgName); + + if (req.pkgName.empty()) { + LogWarning("Application " << req.appName << " not found in database while uninstalling"); PrivilegeDb::getInstance().RollbackTransaction(); return SECURITY_MANAGER_SUCCESS; } - smackLabel = SmackLabels::generateAppLabel(appName); - LogDebug("Uninstall parameters: appName: " << appName << ", pkgName: " << pkgName - << ", uidstr " << uidstr << ", generated smack label: " << smackLabel); + smackLabel = SmackLabels::generateAppLabel(req.appName); + LogDebug("Generated uninstall parameters: pkgName=" << req.pkgName + << " Smack label=" << smackLabel); /* Before we remove the app from the database, let's fetch all apps in the package that this app belongs to, this will allow us to remove all rules withing the package that the app appears in */ - PrivilegeDb::getInstance().GetAppAuthorId(appName, authorId); - PrivilegeDb::getInstance().GetPkgApps(pkgName, pkgContents); - PrivilegeDb::getInstance().UpdateAppPrivileges(appName, uid, std::vector()); - PrivilegeDb::getInstance().RemoveApplication(appName, uid, removeApp, removePkg, removeAuthor); + PrivilegeDb::getInstance().GetAppAuthorId(req.appName, authorId); + PrivilegeDb::getInstance().GetPkgApps(req.pkgName, pkgContents); + PrivilegeDb::getInstance().UpdateAppPrivileges(req.appName, req.uid, std::vector()); + PrivilegeDb::getInstance().RemoveApplication(req.appName, req.uid, removeApp, removePkg, removeAuthor); // if uninstalled app is targetted to Tizen 2.X, remove other 2.X apps RO rules it's shared dir - PrivilegeDb::getInstance().GetAppVersion(appName, tizenVersion); + PrivilegeDb::getInstance().GetAppVersion(req.appName, tizenVersion); if (isTizen2XVersion(tizenVersion)) - PrivilegeDb::getInstance().GetTizen2XApps(appName, allTizen2XApps); + PrivilegeDb::getInstance().GetTizen2XApps(req.appName, allTizen2XApps); - CynaraAdmin::getInstance().UpdateAppPolicy(smackLabel, uidstr, std::vector()); + CynaraAdmin::getInstance().UpdateAppPolicy(smackLabel, cynaraUserStr, std::vector()); PrivilegeDb::getInstance().CommitTransaction(); LogDebug("Application uninstallation commited to database"); @@ -470,13 +484,13 @@ int ServiceImpl::appUninstall(const std::string &appName, uid_t uid) try { if (removeApp) { - LogDebug("Removing smack rules for deleted appName " << appName); - SmackRules::uninstallApplicationRules(appName); - LogDebug("Pkg rules are deprecated. We must uninstall them. pkgName " << pkgName); - SmackRules::uninstallPackageRules(pkgName); + LogDebug("Removing Smack rules for appName " << req.appName); + SmackRules::uninstallApplicationRules(req.appName); + LogDebug("Removing Smack rules for pkgName " << req.pkgName); + SmackRules::uninstallPackageRules(req.pkgName); if (!removePkg) { - LogDebug("Creating new rules for pkgName " << pkgName); - SmackRules::updatePackageRules(pkgName, pkgContents, allTizen2XApps); + LogDebug("Recreating Smack rules for pkgName " << req.pkgName); + SmackRules::updatePackageRules(req.pkgName, pkgContents, allTizen2XApps); } } @@ -515,17 +529,14 @@ int ServiceImpl::getPkgName(const std::string &appName, std::string &pkgName) return SECURITY_MANAGER_SUCCESS; } -int ServiceImpl::getAppGroups( - const std::string &appName, - uid_t uid, - pid_t pid, - std::unordered_set &gids) +int ServiceImpl::getAppGroups(const Credentials &creds, const std::string &appName, + std::unordered_set &gids) { try { std::string pkgName; std::string smackLabel; - std::string uidStr = std::to_string(uid); - std::string pidStr = std::to_string(pid); + std::string uidStr = std::to_string(creds.uid); + std::string pidStr = std::to_string(creds.pid); LogDebug("appName: " << appName); @@ -540,7 +551,7 @@ int ServiceImpl::getAppGroups( LogDebug("smack label: " << smackLabel); std::vector privileges; - PrivilegeDb::getInstance().GetPkgPrivileges(pkgName, uid, privileges); + PrivilegeDb::getInstance().GetPkgPrivileges(pkgName, creds.uid, privileges); /*there is also a need of checking, if privilege is granted to all users*/ size_t tmp = privileges.size(); PrivilegeDb::getInstance().GetPkgPrivileges(pkgName, getGlobalUserId(), privileges); @@ -586,10 +597,14 @@ int ServiceImpl::getAppGroups( return SECURITY_MANAGER_SUCCESS; } -int ServiceImpl::userAdd(uid_t uidAdded, int userType, uid_t uid) +int ServiceImpl::userAdd(const Credentials &creds, uid_t uidAdded, int userType) { - if (uid != 0) + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_USER_ADMIN, + std::to_string(creds.uid), std::to_string(creds.pid))) { + + LogError("Caller is not permitted to manage users"); return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; + } try { CynaraAdmin::getInstance().UserInit(uidAdded, static_cast(userType)); @@ -599,11 +614,16 @@ int ServiceImpl::userAdd(uid_t uidAdded, int userType, uid_t uid) return SECURITY_MANAGER_SUCCESS; } -int ServiceImpl::userDelete(uid_t uidDeleted, uid_t uid) +int ServiceImpl::userDelete(const Credentials &creds, uid_t uidDeleted) { int ret = SECURITY_MANAGER_SUCCESS; - if (uid != 0) + + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_USER_ADMIN, + std::to_string(creds.uid), std::to_string(creds.pid))) { + + LogError("Caller is not permitted to manage users"); return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; + } /*Uninstall all user apps*/ std::vector userApps; @@ -614,8 +634,11 @@ int ServiceImpl::userDelete(uid_t uidDeleted, uid_t uid) return SECURITY_MANAGER_ERROR_SERVER_ERROR; } - for (auto &app: userApps) { - if (appUninstall(app, uidDeleted) != SECURITY_MANAGER_SUCCESS) { + for (const auto &app : userApps) { + app_inst_req req; + req.uid = uidDeleted; + req.appName = app; + if (appUninstall(creds, std::move(req), true) != SECURITY_MANAGER_SUCCESS) { /*if uninstallation of this app fails, just go on trying to uninstall another ones. we do not have anything special to do about that matter - user will be deleted anyway.*/ ret = SECURITY_MANAGER_ERROR_SERVER_ERROR; @@ -627,28 +650,20 @@ int ServiceImpl::userDelete(uid_t uidDeleted, uid_t uid) return ret; } -int ServiceImpl::policyUpdate(const std::vector &policyEntries, uid_t uid, pid_t pid, const std::string &smackLabel) +int ServiceImpl::policyUpdate(const Credentials &creds, const std::vector &policyEntries) { - enum { - NOT_CHECKED, - IS_NOT_ADMIN, - IS_ADMIN - } isAdmin = NOT_CHECKED; + bool permAdminRequired = false; + bool permUserRequired = false; try { - std::string uidStr = std::to_string(uid); - std::string pidStr = std::to_string(pid); + std::string uidStr = std::to_string(creds.uid); + std::string pidStr = std::to_string(creds.pid); if (policyEntries.size() == 0) { LogError("Validation failed: policy update request is empty"); return SECURITY_MANAGER_ERROR_BAD_REQUEST; }; - if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) { - LogError("Not enough permission to call: " << __FUNCTION__); - return SECURITY_MANAGER_ERROR_ACCESS_DENIED; - }; - std::vector validatedPolicies; for (auto &entry : const_cast&>(policyEntries)) { @@ -656,24 +671,31 @@ int ServiceImpl::policyUpdate(const std::vector &policyEntries, ui CynaraAdminPolicy cyap("", "", "", CYNARA_ADMIN_NONE, ""); int ret = validatePolicy(entry, uidStr, forAdmin, cyap); - if (forAdmin && (isAdmin == NOT_CHECKED)) { - isAdmin = Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)?IS_ADMIN:IS_NOT_ADMIN; - }; + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; - if (ret == SECURITY_MANAGER_SUCCESS) { - if (!forAdmin - || (forAdmin && (isAdmin == IS_ADMIN))) { - validatedPolicies.push_back(std::move(cyap)); - } else { - LogError("Not enough privilege to enforce admin policy"); - return SECURITY_MANAGER_ERROR_ACCESS_DENIED; - }; + if (forAdmin) + permAdminRequired = true; + else + permUserRequired = true; - } else - return ret; + validatedPolicies.push_back(std::move(cyap)); }; - // Apply updates + // Check privileges + if (permUserRequired && !Cynara::getInstance().check(creds.label, + Config::PRIVILEGE_POLICY_USER, uidStr, pidStr)) { + LogError("Not enough privilege to enforce user policy"); + return SECURITY_MANAGER_ERROR_ACCESS_DENIED; + } + + if (permAdminRequired && !Cynara::getInstance().check(creds.label, + Config::PRIVILEGE_POLICY_ADMIN, uidStr, pidStr)) { + LogError("Not enough privilege to enforce admin policy"); + return SECURITY_MANAGER_ERROR_ACCESS_DENIED; + } + + // Apply updates CynaraAdmin::getInstance().SetPolicies(validatedPolicies); } catch (const CynaraException::Base &e) { @@ -687,17 +709,12 @@ int ServiceImpl::policyUpdate(const std::vector &policyEntries, ui return SECURITY_MANAGER_SUCCESS; } -int ServiceImpl::getConfiguredPolicy(bool forAdmin, const policy_entry &filter, uid_t uid, pid_t pid, - const std::string &smackLabel, std::vector &policyEntries) +int ServiceImpl::getConfiguredPolicy(const Credentials &creds, bool forAdmin, + const policy_entry &filter, std::vector &policyEntries) { try { - std::string uidStr = std::to_string(uid); - std::string pidStr = std::to_string(pid); - - if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) { - LogError("Not enough permission to call: " << __FUNCTION__); - return SECURITY_MANAGER_ERROR_ACCESS_DENIED; - }; + std::string uidStr = std::to_string(creds.uid); + std::string pidStr = std::to_string(creds.pid); LogDebug("Filter is: C: " << filter.appName << ", U: " << filter.user @@ -716,10 +733,10 @@ int ServiceImpl::getConfiguredPolicy(bool forAdmin, const policy_entry &filter, LogDebug("App: " << filter.appName << ", Label: " << appLabel); if (forAdmin) { - if (!Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) { - LogError("Not enough privilege to access admin enforced policies: " << __FUNCTION__); + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_POLICY_ADMIN, uidStr, pidStr)) { + LogError("Not enough privilege to access admin enforced policies"); return SECURITY_MANAGER_ERROR_ACCESS_DENIED; - }; + } //Fetch privileges from ADMIN bucket CynaraAdmin::getInstance().ListPolicies( @@ -731,8 +748,13 @@ int ServiceImpl::getConfiguredPolicy(bool forAdmin, const policy_entry &filter, ); LogDebug("ADMIN - number of policies matched: " << listOfPolicies.size()); } else { + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_POLICY_USER, uidStr, pidStr)) { + LogError("Not enough privilege to access user enforced policies"); + return SECURITY_MANAGER_ERROR_ACCESS_DENIED; + } + if (uidStr.compare(user)) { - if (!Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) { + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_POLICY_ADMIN, uidStr, pidStr)) { LogWarning("Not enough privilege to access other user's personal policies. Limiting query to personal privileges."); user = uidStr; }; @@ -797,13 +819,14 @@ int ServiceImpl::getConfiguredPolicy(bool forAdmin, const policy_entry &filter, return SECURITY_MANAGER_SUCCESS; } -int ServiceImpl::getPolicy(const policy_entry &filter, uid_t uid, pid_t pid, const std::string &smackLabel, std::vector &policyEntries) +int ServiceImpl::getPolicy(const Credentials &creds, const policy_entry &filter, + std::vector &policyEntries) { try { - std::string uidStr = std::to_string(uid); - std::string pidStr = std::to_string(pid); + std::string uidStr = std::to_string(creds.uid); + std::string pidStr = std::to_string(creds.pid); - if (!Cynara::getInstance().check(smackLabel, SELF_PRIVILEGE, uidStr, pidStr)) { + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_POLICY_USER, uidStr, pidStr)) { LogWarning("Not enough permission to call: " << __FUNCTION__); return SECURITY_MANAGER_ERROR_ACCESS_DENIED; }; @@ -817,7 +840,7 @@ int ServiceImpl::getPolicy(const policy_entry &filter, uid_t uid, pid_t pid, con std::vector listOfUsers; - if (Cynara::getInstance().check(smackLabel, ADMIN_PRIVILEGE, uidStr, pidStr)) { + if (Cynara::getInstance().check(creds.label, Config::PRIVILEGE_POLICY_ADMIN, uidStr, pidStr)) { LogDebug("User is privileged"); if (filter.user.compare(SECURITY_MANAGER_ANY)) { LogDebug("Limitting Cynara query to user: " << filter.user); @@ -829,9 +852,9 @@ int ServiceImpl::getPolicy(const policy_entry &filter, uid_t uid, pid_t pid, con } else CynaraAdmin::getInstance().ListUsers(listOfUsers); } else { - LogWarning("Not enough privilege to fetch user policy for all users by user: " << uid); - LogDebug("Fetching personal policy for user: " << uid); - listOfUsers.push_back(uid); + LogWarning("Not enough privilege to fetch user policy for all users by user: " << creds.uid); + LogDebug("Fetching personal policy for user: " << creds.uid); + listOfUsers.push_back(creds.uid); }; LogDebug("Fetching policy for " << listOfUsers.size() << " users"); @@ -1027,6 +1050,7 @@ int ServiceImpl::dropOnePrivateSharing( } int ServiceImpl::applyPrivatePathSharing( + const Credentials &creds, const std::string &ownerAppName, const std::string &targetAppName, const std::vector &paths) @@ -1038,6 +1062,12 @@ int ServiceImpl::applyPrivatePathSharing( std::vector pkgContents; try { + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPSHARING_ADMIN, + std::to_string(creds.uid), std::to_string(creds.pid))) { + LogError("Caller is not permitted to manage file sharing"); + return SECURITY_MANAGER_ERROR_ACCESS_DENIED; + } + PrivilegeDb::getInstance().GetAppPkgName(ownerAppName, ownerPkgName); if (ownerPkgName.empty()) { LogError(ownerAppName << " is not an installed application"); @@ -1119,12 +1149,19 @@ int ServiceImpl::applyPrivatePathSharing( } int ServiceImpl::dropPrivatePathSharing( + const Credentials &creds, const std::string &ownerAppName, const std::string &targetAppName, const std::vector &paths) { int errorRet; try { + if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPSHARING_ADMIN, + std::to_string(creds.uid), std::to_string(creds.pid))) { + LogError("Caller is not permitted to manage file sharing"); + return SECURITY_MANAGER_ERROR_ACCESS_DENIED; + } + std::string ownerPkgName; PrivilegeDb::getInstance().GetAppPkgName(ownerAppName, ownerPkgName); if (ownerPkgName.empty()) { @@ -1193,4 +1230,3 @@ int ServiceImpl::dropPrivatePathSharing( } } /* namespace SecurityManager */ - diff --git a/src/common/smack-labels.cpp b/src/common/smack-labels.cpp index 4d6dc47..6674589 100755 --- a/src/common/smack-labels.cpp +++ b/src/common/smack-labels.cpp @@ -300,11 +300,22 @@ std::string getSmackLabelFromPid(pid_t pid) "/attr/current file read error for pid: " << pid); if (smack_label_length(result.c_str()) <= 0) - ThrowMsg(SmackException::InvalidLabel, "Invalid Smack label for process " << pid); + ThrowMsg(SmackException::InvalidLabel, "Error while fetching Smack label for process " << pid); return result; } +std::string getSmackLabelFromSelf(void) +{ + char *label = nullptr; + ssize_t labelSize = smack_new_label_from_self(&label); + if (labelSize <= 0) + ThrowMsg(SmackException::InvalidLabel, "Error while fetching Smack label for current process"); + + std::unique_ptr labelPtr(label, free); + return std::string(labelPtr.get(), labelSize); +} + std::string generateAuthorLabel(const int authorId) { if (authorId < 0) { diff --git a/src/include/app-manager.h b/src/include/app-manager.h index 1d90b6a..f58ff65 100644 --- a/src/include/app-manager.h +++ b/src/include/app-manager.h @@ -115,6 +115,10 @@ int security_manager_app_inst_req_set_author_id(app_inst_req *p_req, const char * This function is used to install application based on * using filled up app_inst_req data structure * + * Required privileges: + * - http://tizen.org/privilege/notexist (local installation) + * - http://tizen.org/privilege/notexist (global installation) + * * \param[in] Pointer handling app_inst_req structure * \return API return code or error code: it would be * - SECURITY_MANAGER_SUCCESS on success, @@ -128,6 +132,10 @@ int security_manager_app_install(const app_inst_req *p_req); * This function is used to uninstall application based on * using filled up app_inst_req data structure * + * Required privileges: + * - http://tizen.org/privilege/notexist (private uninstallation) + * - http://tizen.org/privilege/notexist (global uninstallation) + * * \param[in] Pointer handling app_inst_req structure * \return API return code or error code */ diff --git a/src/include/app-sharing.h b/src/include/app-sharing.h index 9528c88..ad510e7 100644 --- a/src/include/app-sharing.h +++ b/src/include/app-sharing.h @@ -85,6 +85,9 @@ int security_manager_private_sharing_req_add_paths(private_sharing_req *p_req, * This function is used to apply private sharing based on given private_sharing_req. * One path can be shared with multiple applications at the same time. * + * Required privileges: + * - http://tizen.org/privilege/notexist + * * \param[in] Pointer handling private_sharing_req structure * \return API return code or error code: it would be * - SECURITY_MANAGER_SUCCESS on success, @@ -96,6 +99,9 @@ int security_manager_private_sharing_apply(const private_sharing_req *p_req); /* * This function is used to drop private sharing based on given private_sharing_req. * + * Required privileges: + * - http://tizen.org/privilege/notexist + * * \param[in] Pointer handling private_sharing_req structure * \return API return code or error code: it would be * - SECURITY_MANAGER_SUCCESS on success, diff --git a/src/include/policy-manager.h b/src/include/policy-manager.h index b703e18..98a0587 100644 --- a/src/include/policy-manager.h +++ b/src/include/policy-manager.h @@ -136,8 +136,6 @@ int security_manager_policy_entry_set_level(policy_entry *p_entry, const char *p * in p_entry structure. * * This function is intended to be used by admin to change level of privilege. - * If it is used by user that has no http://tizen.org/privilege/internal/usermanagement - * privilege, then security_manager_policy_update_send will return error code. * * \param[in] p_entry Pointer handling policy_entry structure * \param[in] policy_level Policy level to be set. This may be one of strings @@ -249,6 +247,10 @@ const char *security_manager_policy_entry_get_max_level(policy_entry *p_entry); * 3. For user's personal policy: wildcards usage in application or privilege field of policy_entry * is not allowed * + * Required privileges: + * - http://tizen.org/privilege/notexist (for setting own policy) + * - http://tizen.org/privilege/internal/usermanagement (for setting policy for other or all) + * * \param[in] p_req Pointer handling allocated policy_update_req structure * \return API return code or error code * @@ -339,13 +341,12 @@ int security_manager_policy_update_send(policy_update_req *p_req); * \brief Function fetches all privileges enforced by admin user. * The result is stored in the policy_entry structures array. * - * \note It should be called by user with http://tizen.org/privilege/internal/usermanagement privilege. - * Normal users may list their personal policy entries using - * security_manager_get_configured_policy_for_self() API function. - * * \attention Developer is responsible for calling security_manager_policy_entries_free() * for freeing allocated resources. * + * Required privileges: + * - http://tizen.org/privilege/internal/usermanagement + * * \param[in] p_filter Pointer to filter struct * \param[out] ppp_privs_policy Pointer handling allocated policy_entry structures array * \param[out] p_size Pointer where the size of allocated array will be stored @@ -363,6 +364,9 @@ int security_manager_get_configured_policy_for_admin( * * \attention Developer is responsible for calling security_manager_policy_entries_free() * for freeing allocated resources. + + * Required privileges: + * - http://tizen.org/privilege/notexist * * \param[in] p_filter Pointer to filter struct * \param[out] ppp_privs_policy Pointer handling allocated policy_entry structures array @@ -378,13 +382,13 @@ int security_manager_get_configured_policy_for_self( * \brief Function gets the whole policy for all users, their applications and privileges * based on the provided filter. The result is stored in the policy_entry array. * - * \note If this call is performed by user with http://tizen.org/privilege/internal/usermanagement - * privilege, then it's possible to list policies for all users. - * Normal users may only list privileges for their own UID. - * * \attention Developer is responsible for calling security_manager_policy_entries_free() * for freeing allocated resources. * + * Required privileges: + * - http://tizen.org/privilege/notexist (for fetching own policy) + * - http://tizen.org/privilege/internal/usermanagement (for fetching policy for other or all) + * * \param[in] p_filter Pointer to filter struct * \param[out] ppp_privs_policy Pointer handling allocated policy_entry structures array * \param[out] p_size Pointer where the size of allocated array will be stored diff --git a/src/include/user-manager.h b/src/include/user-manager.h index e577fd0..69dc38e 100644 --- a/src/include/user-manager.h +++ b/src/include/user-manager.h @@ -67,6 +67,9 @@ int security_manager_user_req_set_user_type(user_req *p_req, security_manager_us * This function succeeds only when is called by privileged user. * Otherwise it just returns SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED and does nothing. * + * Required privileges: + * - http://tizen.org/privilege/internal/usermanagement + * * It adds all required privileges to a newly created user. * User data are passed through pointer 'p_req'. * @param p_req Structure containing user data filled before calling this @@ -81,6 +84,9 @@ int security_manager_user_add(const user_req *p_req); * This function succeeds only when is called by privileged user. * Otherwise it just returns SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED and does nothing. * + * Required privileges: + * - http://tizen.org/privilege/internal/usermanagement + * * It removes all privileges granted to a user that has been granted previously by * security_manager_user_add. * diff --git a/src/server/service/base-service.cpp b/src/server/service/base-service.cpp index 32360cb..865284b 100644 --- a/src/server/service/base-service.cpp +++ b/src/server/service/base-service.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2000 - 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -39,26 +39,6 @@ BaseService::BaseService() { } -bool BaseService::getPeerID(int sock, uid_t &uid, pid_t &pid, std::string &smackLabel) -{ - struct ucred cr; - socklen_t len = sizeof(cr); - - if (!getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cr, &len)) { - char *smk; - ssize_t ret = smack_new_label_from_socket(sock, &smk); - if (ret < 0) - return false; - smackLabel = smk; - uid = cr.uid; - pid = cr.pid; - free(smk); - return true; - } - - return false; -} - void BaseService::accept(const AcceptEvent &event) { LogDebug("Accept event. ConnectionID.sock: " << event.connectionID.sock << diff --git a/src/server/service/include/base-service.h b/src/server/service/include/base-service.h index 5acb212..ca2753e 100644 --- a/src/server/service/include/base-service.h +++ b/src/server/service/include/base-service.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000 - 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2000 - 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -64,18 +64,6 @@ protected: ConnectionInfoMap m_connectionInfoMap; /** - * Retrieves ID (UID and PID) of peer connected to socket - * - * @param[in] sock Socket file descriptor - * @param[out] uid PID of connected peer. - * @param[out] pid PID of connected peer. - * @param[out] smackLabel Smack label of connected peer. - * - * @return True if peer ID was successfully retrieved, false otherwise. - */ - bool getPeerID(int sock, uid_t &uid, pid_t &pid, std::string &smackLabel); - - /** * Handle request from a client * * @param conn Socket connection information diff --git a/src/server/service/include/service.h b/src/server/service/include/service.h index 3940340..5b4aa8d 100644 --- a/src/server/service/include/service.h +++ b/src/server/service/include/service.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2000 - 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -26,6 +26,7 @@ #define _SECURITY_MANAGER_SERVICE_ #include "base-service.h" +#include "credentials.h" #include "service_impl.h" namespace SecurityManager { @@ -62,18 +63,18 @@ private: * * @param buffer Raw received data buffer * @param send Raw data buffer to be sent - * @param uid User's identifier for whom application will be installed + * @param creds credentials of the requesting process */ - void processAppInstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid); + void processAppInstall(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds); /** * Process application uninstallation * * @param buffer Raw received data buffer * @param send Raw data buffer to be sent - * @param uid User's identifier for whom application will be uninstalled + * @param creds credentials of the requesting process */ - void processAppUninstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid); + void processAppUninstall(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds); /** * Process getting package identifier from an app identifier @@ -88,25 +89,22 @@ private: * * @param buffer Raw received data buffer * @param send Raw data buffer to be sent - * @param uid User's identifier for whom application will be launched - * @param pid Process id in which application will be launched + * @param creds credentials of the requesting process */ - void processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid); + void processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds); - void processUserAdd(MessageBuffer &buffer, MessageBuffer &send, uid_t uid); + void processUserAdd(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds); - void processUserDelete(MessageBuffer &buffer, MessageBuffer &send, uid_t uid); + void processUserDelete(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds); /** * Process policy update request * * @param buffer Raw received data buffer * @param send Raw data buffer to be sent - * @param uid Identifier of the user who sent the request - * @param pid PID of the process which sent the request - * @param smackLabel smack label of requesting app + * @param creds credentials of the requesting process */ - void processPolicyUpdate(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel); + void processPolicyUpdate(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds); /** * List all privileges for specific user, placed in Cynara's PRIVACY_MANAGER @@ -114,26 +112,24 @@ private: * * @param buffer Raw received data buffer * @param send Raw data buffer to be sent - * @param uid Identifier of the user who sent the request - * @param pid PID of the process which sent the request - * @param smackLabel smack label of requesting app + * @param creds credentials of the requesting process * @param forAdmin determines internal type of request */ - void processGetConfiguredPolicy(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel, bool forAdmin); + void processGetConfiguredPolicy(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds, bool forAdmin); /** * Get whole policy for specific user. Whole policy is a list of all apps, * and their permissions (based on what they've stated in their manifests). - * If uid is unprivileged, then only privileges for the caller uid will be - * listed. If uid is privileged, then apps for all the users will be listed. + * + * If calling user is unprivileged, then only privileges for the caller uid + * will be listed. If caller is privileged, then apps for all the users will + * be listed. * * @param buffer Raw received data buffer * @param send Raw data buffer to be sent - * @param uid Identifier of the user who sent the request - * @param pid PID of the process which sent the request - * @param smackLabel smack label of requesting app + * @param creds credentials of the requesting process */ - void processGetPolicy(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel); + void processGetPolicy(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds); /** * Process getting policies descriptions as strings from Cynara @@ -163,15 +159,18 @@ private: * * @param recv Raw received data buffer * @param send Raw data buffer to be sent + * @param creds credentials of the requesting process */ - void processApplyPrivateSharing(MessageBuffer &recv, MessageBuffer &send); + void processApplyPrivateSharing(MessageBuffer &recv, MessageBuffer &send, const Credentials &creds); + /** * Process drop private path sharing between applications. * * @param recv Raw received data buffer * @param send Raw data buffer to be sent + * @param creds credentials of the requesting process */ - void processDropPrivateSharing(MessageBuffer &recv, MessageBuffer &send); + void processDropPrivateSharing(MessageBuffer &recv, MessageBuffer &send, const Credentials &creds); }; } // namespace SecurityManager diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index 50e5635..e361737 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -64,18 +64,10 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer, MessageBuffer send; bool retval = false; - uid_t uid; - pid_t pid; - std::string smackLabel; - - if (!getPeerID(conn.sock, uid, pid, smackLabel)) { - LogError("Closing socket because of error: unable to get peer's uid, pid or smack label"); - m_serviceManager->Close(conn); - return false; - } - if (IFACE == interfaceID) { Try { + Credentials creds = Credentials::getCredentialsFromSocket(conn.sock); + // deserialize API call type int call_type_int; Deserialization::Deserialize(buffer, call_type_int); @@ -88,35 +80,35 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer, break; case SecurityModuleCall::APP_INSTALL: LogDebug("call_type: SecurityModuleCall::APP_INSTALL"); - processAppInstall(buffer, send, uid); + processAppInstall(buffer, send, creds); break; case SecurityModuleCall::APP_UNINSTALL: LogDebug("call_type: SecurityModuleCall::APP_UNINSTALL"); - processAppUninstall(buffer, send, uid); + processAppUninstall(buffer, send, creds); break; case SecurityModuleCall::APP_GET_PKG_NAME: processGetPkgName(buffer, send); break; case SecurityModuleCall::APP_GET_GROUPS: - processGetAppGroups(buffer, send, uid, pid); + processGetAppGroups(buffer, send, creds); break; case SecurityModuleCall::USER_ADD: - processUserAdd(buffer, send, uid); + processUserAdd(buffer, send, creds); break; case SecurityModuleCall::USER_DELETE: - processUserDelete(buffer, send, uid); + processUserDelete(buffer, send, creds); break; case SecurityModuleCall::POLICY_UPDATE: - processPolicyUpdate(buffer, send, uid, pid, smackLabel); + processPolicyUpdate(buffer, send, creds); break; case SecurityModuleCall::GET_CONF_POLICY_ADMIN: - processGetConfiguredPolicy(buffer, send, uid, pid, smackLabel, true); + processGetConfiguredPolicy(buffer, send, creds, true); break; case SecurityModuleCall::GET_CONF_POLICY_SELF: - processGetConfiguredPolicy(buffer, send, uid, pid, smackLabel, false); + processGetConfiguredPolicy(buffer, send, creds, false); break; case SecurityModuleCall::GET_POLICY: - processGetPolicy(buffer, send, uid, pid, smackLabel); + processGetPolicy(buffer, send, creds); break; case SecurityModuleCall::POLICY_GET_DESCRIPTIONS: processPolicyGetDesc(send); @@ -128,10 +120,10 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer, processAppHasPrivilege(buffer, send); break; case SecurityModuleCall::APP_APPLY_PRIVATE_SHARING: - processApplyPrivateSharing(buffer, send); + processApplyPrivateSharing(buffer, send, creds); break; case SecurityModuleCall::APP_DROP_PRIVATE_SHARING: - processDropPrivateSharing(buffer, send); + processDropPrivateSharing(buffer, send, creds); break; default: LogError("Invalid call: " << call_type_int); @@ -164,7 +156,7 @@ bool Service::processOne(const ConnectionID &conn, MessageBuffer &buffer, return retval; } -void Service::processAppInstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid) +void Service::processAppInstall(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { app_inst_req req; @@ -175,15 +167,16 @@ void Service::processAppInstall(MessageBuffer &buffer, MessageBuffer &send, uid_ Deserialization::Deserialize(buffer, req.uid); Deserialization::Deserialize(buffer, req.tizenVersion); Deserialization::Deserialize(buffer, req.authorName); - Serialization::Serialize(send, serviceImpl.appInstall(req, uid)); + Serialization::Serialize(send, serviceImpl.appInstall(creds, std::move(req))); } -void Service::processAppUninstall(MessageBuffer &buffer, MessageBuffer &send, uid_t uid) +void Service::processAppUninstall(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { - std::string appName; + app_inst_req req; - Deserialization::Deserialize(buffer, appName); - Serialization::Serialize(send, serviceImpl.appUninstall(appName, uid)); + req.uid = creds.uid; + Deserialization::Deserialize(buffer, req.appName); + Serialization::Serialize(send, serviceImpl.appUninstall(creds, std::move(req))); } void Service::processGetPkgName(MessageBuffer &buffer, MessageBuffer &send) @@ -199,14 +192,14 @@ void Service::processGetPkgName(MessageBuffer &buffer, MessageBuffer &send) Serialization::Serialize(send, pkgName); } -void Service::processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid) +void Service::processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { std::string appName; std::unordered_set gids; int ret; Deserialization::Deserialize(buffer, appName); - ret = serviceImpl.getAppGroups(appName, uid, pid, gids); + ret = serviceImpl.getAppGroups(creds, appName, gids); Serialization::Serialize(send, ret); if (ret == SECURITY_MANAGER_SUCCESS) { Serialization::Serialize(send, static_cast(gids.size())); @@ -216,7 +209,7 @@ void Service::processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, ui } } -void Service::processUserAdd(MessageBuffer &buffer, MessageBuffer &send, uid_t uid) +void Service::processUserAdd(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { int ret; uid_t uidAdded; @@ -225,40 +218,40 @@ void Service::processUserAdd(MessageBuffer &buffer, MessageBuffer &send, uid_t u Deserialization::Deserialize(buffer, uidAdded); Deserialization::Deserialize(buffer, userType); - ret = serviceImpl.userAdd(uidAdded, userType, uid); + ret = serviceImpl.userAdd(creds, uidAdded, userType); Serialization::Serialize(send, ret); } -void Service::processUserDelete(MessageBuffer &buffer, MessageBuffer &send, uid_t uid) +void Service::processUserDelete(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { int ret; uid_t uidRemoved; Deserialization::Deserialize(buffer, uidRemoved); - ret = serviceImpl.userDelete(uidRemoved, uid); + ret = serviceImpl.userDelete(creds, uidRemoved); Serialization::Serialize(send, ret); } -void Service::processPolicyUpdate(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel) +void Service::processPolicyUpdate(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { int ret; std::vector policyEntries; Deserialization::Deserialize(buffer, policyEntries); - ret = serviceImpl.policyUpdate(policyEntries, uid, pid, smackLabel); + ret = serviceImpl.policyUpdate(creds, policyEntries); Serialization::Serialize(send, ret); } -void Service::processGetConfiguredPolicy(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel, bool forAdmin) +void Service::processGetConfiguredPolicy(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds, bool forAdmin) { int ret; policy_entry filter; Deserialization::Deserialize(buffer, filter); std::vector policyEntries; - ret = serviceImpl.getConfiguredPolicy(forAdmin, filter, uid, pid, smackLabel, policyEntries); + ret = serviceImpl.getConfiguredPolicy(creds, forAdmin, filter, policyEntries); Serialization::Serialize(send, ret); Serialization::Serialize(send, static_cast(policyEntries.size())); @@ -267,14 +260,14 @@ void Service::processGetConfiguredPolicy(MessageBuffer &buffer, MessageBuffer &s }; } -void Service::processGetPolicy(MessageBuffer &buffer, MessageBuffer &send, uid_t uid, pid_t pid, const std::string &smackLabel) +void Service::processGetPolicy(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { int ret; policy_entry filter; Deserialization::Deserialize(buffer, filter); std::vector policyEntries; - ret = serviceImpl.getPolicy(filter, uid, pid, smackLabel, policyEntries); + ret = serviceImpl.getPolicy(creds, filter, policyEntries); Serialization::Serialize(send, ret); Serialization::Serialize(send, static_cast(policyEntries.size())); @@ -329,25 +322,25 @@ void Service::processAppHasPrivilege(MessageBuffer &recv, MessageBuffer &send) Serialization::Serialize(send, static_cast(result)); } -void Service::processApplyPrivateSharing(MessageBuffer &recv, MessageBuffer &send) +void Service::processApplyPrivateSharing(MessageBuffer &recv, MessageBuffer &send, const Credentials &creds) { std::string ownerAppName, targetAppName; std::vector paths; Deserialization::Deserialize(recv, ownerAppName); Deserialization::Deserialize(recv, targetAppName); Deserialization::Deserialize(recv, paths); - int ret = serviceImpl.applyPrivatePathSharing(ownerAppName, targetAppName, paths); + int ret = serviceImpl.applyPrivatePathSharing(creds, ownerAppName, targetAppName, paths); Serialization::Serialize(send, ret); } -void Service::processDropPrivateSharing(MessageBuffer &recv, MessageBuffer &send) +void Service::processDropPrivateSharing(MessageBuffer &recv, MessageBuffer &send, const Credentials &creds) { std::string ownerAppName, targetAppName; std::vector paths; Deserialization::Deserialize(recv, ownerAppName); Deserialization::Deserialize(recv, targetAppName); Deserialization::Deserialize(recv, paths); - int ret = serviceImpl.dropPrivatePathSharing(ownerAppName, targetAppName, paths); + int ret = serviceImpl.dropPrivatePathSharing(creds, ownerAppName, targetAppName, paths); Serialization::Serialize(send, ret); } } // namespace SecurityManager -- 2.7.4 From ae9f5b5482206cfeb468d04a4971a8282297a5a7 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Wed, 20 Apr 2016 17:27:50 +0200 Subject: [PATCH 05/16] Add installation types (global, local, preloaded). Before this commit installation type was based on UID. With this commit it is possible to set type of installation (global, local, preloaded) during app installation request. If type is not specified, and installation is performed by global user, default 'SM_APP_INSTALL_GLOBAL' type of installation is set. Otherwise installation type is set to 'SM_APP_INSTALL_LOCAL'. New API function avaliable: * int security_manager_app_inst_req_set_install_type(app_inst_req *p_req, const enum app_install_type type) Change-Id: I1abfff547482c7adfedc09d9832569a294752d41 --- src/client/client-security-manager.cpp | 14 +++++++++++- src/cmd/security-manager-cmd.cpp | 13 ++++++++++-- src/common/include/protocols.h | 1 + src/common/include/service_impl.h | 2 +- src/common/service_impl.cpp | 39 +++++++++++++++++++++++++++------- src/include/app-manager.h | 12 +++++++++++ src/include/security-manager-types.h | 9 ++++++++ src/server/service/service.cpp | 1 + 8 files changed, 79 insertions(+), 12 deletions(-) diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 3420e9a..6b35e74 100755 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -179,6 +179,17 @@ int security_manager_app_inst_req_add_path(app_inst_req *p_req, const char *path } SECURITY_MANAGER_API +int security_manager_app_inst_req_set_install_type(app_inst_req *p_req, const enum app_install_type type) +{ + if (!p_req || (type <= SM_APP_INSTALL_NONE) || (type >= SM_APP_INSTALL_END)) + return SECURITY_MANAGER_ERROR_INPUT_PARAM; + + p_req->installationType = static_cast(type); + + return SECURITY_MANAGER_SUCCESS; +} + +SECURITY_MANAGER_API int security_manager_app_install(const app_inst_req *p_req) { using namespace SecurityManager; @@ -207,7 +218,8 @@ int security_manager_app_install(const app_inst_req *p_req) p_req->appPaths, p_req->uid, p_req->tizenVersion, - p_req->authorName); + p_req->authorName, + p_req->installationType); //send buffer to server retval = sendToServer(SERVICE_SOCKET, send.Pop(), recv); diff --git a/src/cmd/security-manager-cmd.cpp b/src/cmd/security-manager-cmd.cpp index a1f55e5..0e9b5c5 100644 --- a/src/cmd/security-manager-cmd.cpp +++ b/src/cmd/security-manager-cmd.cpp @@ -55,6 +55,12 @@ static std::map user_type_map = { {"normal", SM_USER_TYPE_NORMAL} }; +static std::map install_type_map = { + {"local", SM_APP_INSTALL_LOCAL}, + {"global", SM_APP_INSTALL_GLOBAL}, + {"preloaded", SM_APP_INSTALL_PRELOADED} +}; + static po::options_description getGenericOptions() { po::options_description opts("Generic options"); @@ -95,6 +101,8 @@ static po::options_description getInstallOptions() "user identifier number (required)") ("author-id,c", po::value(), "unique author's identifier (required for trusted_rw paths)") + ("install-type", po::value(), + "type of installation (local, global, preloaded") ; return opts; } @@ -236,9 +244,10 @@ static void parseInstallOptions(int argc, char *argv[], } if (vm.count("uid")) req.uid = vm["uid"].as(); - if (vm.count("author-id")) { + if (vm.count("author-id")) req.authorName = vm["author-id"].as(); - } + if (vm.count("install-type")) + req.installationType = install_type_map.at(vm["install-type"].as()); } diff --git a/src/common/include/protocols.h b/src/common/include/protocols.h index 940564b..2283205 100644 --- a/src/common/include/protocols.h +++ b/src/common/include/protocols.h @@ -40,6 +40,7 @@ struct app_inst_req { uid_t uid; std::string tizenVersion; std::string authorName; + int installationType = SM_APP_INSTALL_NONE; }; struct user_req { diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index b6ffc9b..bae4c79 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -40,7 +40,7 @@ private: static bool isSubDir(const char *parent, const char *subdir); - static bool getUserAppDir(const uid_t &uid, std::string &userAppDir); + static bool getUserAppDir(const uid_t &uid, const app_install_type &installType, std::string &userAppDir); static void installRequestMangle(app_inst_req &req, std::string &cynaraUserStr); diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 66b0b1c..35b4d69 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -189,7 +189,7 @@ bool ServiceImpl::isSubDir(const char *parent, const char *subdir) return (*subdir == '/' || *parent == *subdir); } -bool ServiceImpl::getUserAppDir(const uid_t &uid, std::string &userAppDir) +bool ServiceImpl::getUserAppDir(const uid_t &uid, const app_install_type &installType, std::string &userAppDir) { struct tzplatform_context *tz_ctx = nullptr; @@ -206,8 +206,22 @@ bool ServiceImpl::getUserAppDir(const uid_t &uid, std::string &userAppDir) return false; } - enum tzplatform_variable id = - (uid == getGlobalUserId()) ? TZ_SYS_RW_APP : TZ_USER_APP; + enum tzplatform_variable id; + + switch (installType) { + case SM_APP_INSTALL_LOCAL: + id = TZ_USER_APP; + break; + case SM_APP_INSTALL_GLOBAL: + id = TZ_SYS_RW_APP; + break; + case SM_APP_INSTALL_PRELOADED: + id = TZ_SYS_RO_APP; + break; + default: + return false; + } + const char *appDir = tzplatform_context_getenv(tz_ctxPtr.get(), id); if (!appDir) { LogError("Error in tzplatform_context_getenv()"); @@ -232,18 +246,27 @@ void ServiceImpl::installRequestMangle(app_inst_req &req, std::string &cynaraUse if (req.uid == 0) req.uid = globalUid; - if (req.uid == globalUid) { + if (req.installationType == SM_APP_INSTALL_NONE) + req.installationType = (req.uid == globalUid) ? + SM_APP_INSTALL_GLOBAL : SM_APP_INSTALL_LOCAL; + + + if (req.installationType == SM_APP_INSTALL_GLOBAL) { LogDebug("Installation type: global installation"); cynaraUserStr = CYNARA_ADMIN_WILDCARD; - } else { + } else if (req.installationType == SM_APP_INSTALL_LOCAL) { LogDebug("Installation type: local installation"); cynaraUserStr = std::to_string(static_cast(req.uid)); - } + } else if (req.installationType == SM_APP_INSTALL_PRELOADED) { + LogDebug("Installation type: preloaded installation"); + cynaraUserStr = std::to_string(static_cast(req.uid)); + } else + LogError("Installation type: unknown"); } bool ServiceImpl::installRequestAuthCheck(const Credentials &creds, const app_inst_req &req) { - if (req.uid == getGlobalUserId() || req.uid != creds.uid) { + if (req.installationType != SM_APP_INSTALL_LOCAL || req.uid != creds.uid) { if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPINST_ADMIN, std::to_string(creds.uid), std::to_string(creds.pid))) { LogError("Caller is not permitted to install applications globally"); @@ -262,7 +285,7 @@ bool ServiceImpl::installRequestAuthCheck(const Credentials &creds, const app_in bool ServiceImpl::installRequestPathsCheck(const app_inst_req &req, std::string &appPath) { - if (!getUserAppDir(req.uid, appPath)) { + if (!getUserAppDir(req.uid, static_cast(req.installationType), appPath)) { LogError("Failed getting app dir for user uid: " << req.uid); return false; } diff --git a/src/include/app-manager.h b/src/include/app-manager.h index f58ff65..c297d75 100644 --- a/src/include/app-manager.h +++ b/src/include/app-manager.h @@ -112,6 +112,18 @@ int security_manager_app_inst_req_set_uid(app_inst_req *p_req, int security_manager_app_inst_req_set_author_id(app_inst_req *p_req, const char *author_id); /* + * This function is used to set up installation type (global, local, preloaded). + * If type is not set and if installation is performed by global user, type is set to + * 'SM_APP_INSTALL_GLOBAL'. Otherwise installation type is set to 'SM_APP_INSTALL_LOCAL'. + * + * \param[in] Pointer handling app_inst_req structure + * \param[in] Installation type + * \return API return code or error code + * + */ +int security_manager_app_inst_req_set_install_type(app_inst_req *p_req, const enum app_install_type type); + +/* * This function is used to install application based on * using filled up app_inst_req data structure * diff --git a/src/include/security-manager-types.h b/src/include/security-manager-types.h index 0acf328..01820b4 100644 --- a/src/include/security-manager-types.h +++ b/src/include/security-manager-types.h @@ -66,6 +66,15 @@ enum app_install_path_type { SECURITY_MANAGER_ENUM_END }; +enum app_install_type { + SM_APP_INSTALL_NONE = 0, + SM_APP_INSTALL_LOCAL, + SM_APP_INSTALL_GLOBAL, + SM_APP_INSTALL_PRELOADED, + SM_APP_INSTALL_END +}; +typedef enum app_install_type app_install_type; + /** * This enum has values equivalent to gumd user type. * The gum-utils help states that diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index e361737..7d4dae9 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -167,6 +167,7 @@ void Service::processAppInstall(MessageBuffer &buffer, MessageBuffer &send, cons Deserialization::Deserialize(buffer, req.uid); Deserialization::Deserialize(buffer, req.tizenVersion); Deserialization::Deserialize(buffer, req.authorName); + Deserialization::Deserialize(buffer, req.installationType); Serialization::Serialize(send, serviceImpl.appInstall(creds, std::move(req))); } -- 2.7.4 From d624c4d3f29e37490ef905f135520310730b4595 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Fri, 11 Mar 2016 09:10:21 +0100 Subject: [PATCH 06/16] refactoring: use common function template for getting label by libsmack Merging repeated code pattern where a libsmack function is used to fetch Smack label, the result must be wrapped into std::string and memory allocated by libsmack safely freed. Change-Id: I67136fc5f78fd7974d27feafb0ee2d3164df9461 Signed-off-by: Rafal Krypa --- src/common/smack-labels.cpp | 48 +++++++++++++++++---------------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/common/smack-labels.cpp b/src/common/smack-labels.cpp index 6674589..6f16901 100755 --- a/src/common/smack-labels.cpp +++ b/src/common/smack-labels.cpp @@ -259,36 +259,35 @@ std::string generateSharedPrivateLabel(const std::string &pkgName, const std::st return label; } -std::string getSmackLabelFromSocket(int socketFd) +template +static std::string getSmackLabel(FuncType func, ArgsType... args) { - char *label = nullptr; - ssize_t labelSize = smack_new_label_from_socket(socketFd, &label); - std::unique_ptr labelPtr(label, free); - - if (labelSize < 0) { - ThrowMsg(SmackException::Base, - "smack_new_label_from_socket error for socket: " << socketFd); - } + char *label; + ssize_t labelLen = func(args..., &label); + if (labelLen <= 0) + ThrowMsg(SmackException::Base, "Error while getting Smack label"); + std::unique_ptr labelPtr(label, free); + return std::string(labelPtr.get(), labelLen); +} - return std::string(labelPtr.get(), labelSize); +std::string getSmackLabelFromSocket(int socketFd) +{ + return getSmackLabel(&smack_new_label_from_socket, socketFd); } std::string getSmackLabelFromPath(const std::string &path) { - char *label = nullptr; - ssize_t labelSize = smack_new_label_from_path(path.c_str(), XATTR_NAME_SMACK, true, &label); - std::unique_ptr labelPtr(label, free); - - if (labelSize < 0) { - ThrowMsg(SmackException::FileError, - "smack_new_label_from_path error for path: " << path); - } + return getSmackLabel(&smack_new_label_from_path, path.c_str(), XATTR_NAME_SMACK, true); +} - return std::string(labelPtr.get(), labelSize); +std::string getSmackLabelFromSelf(void) +{ + return getSmackLabel(&smack_new_label_from_self); } std::string getSmackLabelFromPid(pid_t pid) { + // FIXME: libsmack should provide such a function std::ifstream smackFileStream("/proc/" + std::to_string(pid) + "/attr/current"); if (!smackFileStream.is_open()) ThrowMsg(SmackException::FileError, @@ -305,17 +304,6 @@ std::string getSmackLabelFromPid(pid_t pid) return result; } -std::string getSmackLabelFromSelf(void) -{ - char *label = nullptr; - ssize_t labelSize = smack_new_label_from_self(&label); - if (labelSize <= 0) - ThrowMsg(SmackException::InvalidLabel, "Error while fetching Smack label for current process"); - - std::unique_ptr labelPtr(label, free); - return std::string(labelPtr.get(), labelSize); -} - std::string generateAuthorLabel(const int authorId) { if (authorId < 0) { -- 2.7.4 From 86fe306466e407276fd127a666fe61c0df1432d8 Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Tue, 5 Apr 2016 20:35:53 +0200 Subject: [PATCH 07/16] Move smack files to new directory All smack rules generated by security-manager will be merged to one file. This will speed up start process as reading one big file is much faster than opening and reading a lot of small ones. The rules related with apps are loaded by security-manager-rules-loader service after local-fs.target. Before local-fs.terget smack rules related to user app are not required. We may load this rules in service that is triggered after local-fs.target and improve systemd start time. Change-Id: I64c961b90ee84772815f41dceefa15b567399763 --- CMakeLists.txt | 10 ++ migration/CMakeLists.txt | 29 ++++++ migration/security-manager-migration.in | 18 ++++ packaging/security-manager.spec | 9 ++ src/common/CMakeLists.txt | 4 + src/common/filesystem.cpp | 82 ++++++++++++++++ src/common/include/filesystem-exception.h | 42 +++++++++ src/common/include/filesystem.h | 43 +++++++++ src/common/include/smack-rules.h | 11 ++- src/common/service_impl.cpp | 3 + src/common/smack-rules.cpp | 113 ++++++++++++++++++++--- src/dpl/core/include/dpl/fstream_accessors.h | 46 +++++++++ systemd/CMakeLists.txt | 2 + systemd/security-manager-rules-loader.service.in | 13 +++ 14 files changed, 410 insertions(+), 15 deletions(-) create mode 100644 migration/CMakeLists.txt create mode 100644 migration/security-manager-migration.in create mode 100644 src/common/filesystem.cpp create mode 100644 src/common/include/filesystem-exception.h create mode 100644 src/common/include/filesystem.h create mode 100644 src/dpl/core/include/dpl/fstream_accessors.h create mode 100644 systemd/security-manager-rules-loader.service.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 28790d8..1d9c1af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,6 +26,15 @@ PROJECT("security-manager") INCLUDE(FindPkgConfig) +######################### let's setup directories ############################# + +SET(LOCAL_STATE_DIR + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LOCALSTATEDIR}" + CACHE PATH + "Modifiable single-machine data directory") + +ADD_DEFINITIONS("-DLOCAL_STATE_DIR=\"${LOCAL_STATE_DIR}\"") + ############################# compiler flags ################################## SET(CMAKE_C_FLAGS_PROFILING "-g -O0 -pg -Wp,-U_FORTIFY_SOURCE") @@ -63,3 +72,4 @@ ADD_SUBDIRECTORY(pc) ADD_SUBDIRECTORY(systemd) ADD_SUBDIRECTORY(db) ADD_SUBDIRECTORY(policy) +ADD_SUBDIRECTORY(migration) diff --git a/migration/CMakeLists.txt b/migration/CMakeLists.txt new file mode 100644 index 0000000..0094aa6 --- /dev/null +++ b/migration/CMakeLists.txt @@ -0,0 +1,29 @@ +# Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# @file CMakeLists.txt +# @author Bartlomiej Grzelewski +# @brief CMake for migration script +# + +CONFIGURE_FILE(security-manager-migration.in security-manager-migration @ONLY) + +INSTALL(PROGRAMS + ${CMAKE_BINARY_DIR}/migration/security-manager-migration + DESTINATION + ${BIN_INSTALL_DIR} + ) + + + diff --git a/migration/security-manager-migration.in b/migration/security-manager-migration.in new file mode 100644 index 0000000..f08469c --- /dev/null +++ b/migration/security-manager-migration.in @@ -0,0 +1,18 @@ +#!/bin/bash + +SRC_PATH=/etc/smack/accesses.d +DSC_PATH=@LOCAL_STATE_DIR@/security-manager/rules +MRG_PATH=@LOCAL_STATE_DIR@/security-manager/rules-merged/rules.merged + +if [ -e $MRG_PATH ]; then + echo "No migration needed. File ${MRG_PATH} already exists." + exit 0 +fi + +echo "Running migration process" +echo "Moving files from ${SRC_PATH} to ${DSC_PATH}" +mv ${SRC_PATH}/app_* ${SRC_PATH}/pkg_* ${SRC_PATH}/author_* ${DSC_PATH} +echo "Merging files from ${DSC_PATH} to ${MRG_PATH}" +cat ${DSC_PATH}/* > ${MRG_PATH} +echo "Migration process was finished" + diff --git a/packaging/security-manager.spec b/packaging/security-manager.spec index 34819da..97eec65 100755 --- a/packaging/security-manager.spec +++ b/packaging/security-manager.spec @@ -76,6 +76,7 @@ export LDFLAGS+="-Wl,--rpath=%{_libdir}" %cmake . -DVERSION=%{version} \ -DBIN_INSTALL_DIR=%{_bindir} \ -DDB_INSTALL_DIR=%{TZ_SYS_DB} \ + -DLOCAL_STATE_DIR=%{TZ_SYS_VAR} \ -DSYSTEMD_INSTALL_DIR=%{_unitdir} \ -DCMAKE_BUILD_TYPE=%{?build_type:%build_type}%{!?build_type:RELEASE} \ -DCMAKE_VERBOSE_MAKEFILE=ON @@ -90,8 +91,10 @@ cp LICENSE %{buildroot}%{_datadir}/license/libsecurity-manager-client mkdir -p %{buildroot}/%{_unitdir}/sockets.target.wants mkdir -p %{buildroot}/%{_unitdir}/sysinit.target.wants +mkdir -p %{buildroot}/%{_unitdir}/basic.target.wants ln -s ../security-manager.socket %{buildroot}/%{_unitdir}/sockets.target.wants/security-manager.socket ln -s ../security-manager-cleanup.service %{buildroot}/%{_unitdir}/sysinit.target.wants/security-manager-cleanup.service +ln -s ../security-manager-rules-loader.service %{buildroot}/%{_unitdir}/basic.target.wants/security-manager-rules-loader.service mkdir -p %{buildroot}/%{TZ_SYS_DB} touch %{buildroot}/%{TZ_SYS_DB}/.security-manager.db @@ -111,6 +114,7 @@ fi if [ $1 = 2 ]; then # update + %{_bindir}/security-manager-migration systemctl restart security-manager.service %{_datadir}/security-manager/db/update.sh fi @@ -140,15 +144,20 @@ fi %files -n security-manager %manifest security-manager.manifest %defattr(-,root,root,-) +%attr(755,root,root) %{_bindir}/security-manager-migration %attr(755,root,root) %{_bindir}/security-manager %attr(755,root,root) %{_bindir}/security-manager-cmd %attr(755,root,root) %{_bindir}/security-manager-cleanup %attr(755,root,root) %{_sysconfdir}/gumd/useradd.d/50_security-manager-add.post %attr(755,root,root) %{_sysconfdir}/gumd/userdel.d/50_security-manager-remove.pre +%dir %attr(700,root,root) %{TZ_SYS_VAR}/security-manager/rules +%dir %attr(700,root,root) %{TZ_SYS_VAR}/security-manager/rules-merged %{_libdir}/libsecurity-manager-commons.so.* %attr(-,root,root) %{_unitdir}/security-manager.* %attr(-,root,root) %{_unitdir}/security-manager-cleanup.* +%attr(-,root,root) %{_unitdir}/security-manager-rules-loader.service +%attr(-,root,root) %{_unitdir}/basic.target.wants/security-manager-rules-loader.service %attr(-,root,root) %{_unitdir}/sockets.target.wants/security-manager.* %attr(-,root,root) %{_unitdir}/sysinit.target.wants/security-manager-cleanup.* %config(noreplace) %attr(0600,root,root) %{TZ_SYS_DB}/.security-manager.db diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 582f7db..45b3d83 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -54,6 +54,7 @@ SET(COMMON_SOURCES ${COMMON_PATH}/smack-rules.cpp ${COMMON_PATH}/smack-check.cpp ${COMMON_PATH}/service_impl.cpp + ${COMMON_PATH}/filesystem.cpp ) ADD_LIBRARY(${TARGET_COMMON} SHARED ${COMMON_SOURCES}) @@ -71,3 +72,6 @@ TARGET_LINK_LIBRARIES(${TARGET_COMMON} ) INSTALL(TARGETS ${TARGET_COMMON} DESTINATION ${LIB_INSTALL_DIR}) +INSTALL(DIRECTORY DESTINATION ${LOCAL_STATE_DIR}/security-manager/rules) +INSTALL(DIRECTORY DESTINATION ${LOCAL_STATE_DIR}/security-manager/rules-merged) + diff --git a/src/common/filesystem.cpp b/src/common/filesystem.cpp new file mode 100644 index 0000000..b278ef1 --- /dev/null +++ b/src/common/filesystem.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Rafal Krypa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/** + * @file filesystem.cpp + * @author Bartlomiej Grzelewski + * @version 1.0 + * @brief Wrappers for filesystem operations. + * + */ +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace SecurityManager { +namespace FS { + +FileNameVector getFilesFromDirectory(const std::string &path) +{ + FileNameVector result; + dirent tmp, *ptr; + int err; + std::unique_ptr> dir(opendir(path.c_str()), closedir); + + if (!dir.get()) { + err = errno; + ThrowMsg(FS::Exception::FileError, "Error opening directory: " << GetErrnoString(err)); + } + + while (true) { + if (readdir_r(dir.get(), &tmp, &ptr)) { + err = errno; + ThrowMsg(FS::Exception::FileError, "Error reading directory: " << GetErrnoString(err)); + } + + if (!ptr) + break; + + struct stat finfo; + std::string filepath = path + "/" + ptr->d_name; + if (0 > stat(filepath.c_str(), &finfo)) { + ThrowMsg(FS::Exception::FileError, "Error reading: " << filepath); + continue; + } + + if (S_ISREG(finfo.st_mode)) { + result.push_back(ptr->d_name); + } + } + + return result; +} + +} // namespace FS +} // nanespace SecurityManager + diff --git a/src/common/include/filesystem-exception.h b/src/common/include/filesystem-exception.h new file mode 100644 index 0000000..4e13019 --- /dev/null +++ b/src/common/include/filesystem-exception.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Rafal Krypa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/** + * @file fs-exceptions.h + * @author Rafal Krypa + * @version 1.0 + * @brief Declaration of Smack-specific exceptions + * + */ +#ifndef _FILESYSTEM_EXCEPTIONS_H_ +#define _FILESYSTEM_EXCEPTIONS_H_ + +#include + +namespace SecurityManager { +namespace FS { + +class Exception { +public: + DECLARE_EXCEPTION_TYPE(SecurityManager::Exception, Base) + DECLARE_EXCEPTION_TYPE(Base, FileError) +}; + +} // namespace FS +} // namespace SecurityManager + +#endif /* _FILESYSTEM_EXCEPTIONS_H_ */ diff --git a/src/common/include/filesystem.h b/src/common/include/filesystem.h new file mode 100644 index 0000000..e2f15f8 --- /dev/null +++ b/src/common/include/filesystem.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Contact: Rafal Krypa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +/** + * @file filesystem.h + * @author Bartlomiej Grzelewski + * @version 1.0 + * @brief Very simple wrapper for opendir/readdir. + * + */ +#ifndef _FILESYSTEM_H_ +#define _FILESYSTEM_H_ + +#include +#include +#include + +namespace SecurityManager { +namespace FS { + +typedef std::vector FileNameVector; + +FileNameVector getFilesFromDirectory(const std::string &path); + +} // namespace FS +} // nanespace SecurityManager + +#endif // _FILESYSTEM_H_ + diff --git a/src/common/include/smack-rules.h b/src/common/include/smack-rules.h index 7a5b5b0..2a88b60 100644 --- a/src/common/include/smack-rules.h +++ b/src/common/include/smack-rules.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -62,7 +62,7 @@ public: void apply() const; void clear() const; - void saveToFile(const std::string &path, bool truncFile = true) const; + void saveToFile(const std::string &path) const; /** * Create cross dependencies for all applications in a package @@ -196,6 +196,13 @@ public: static void updatePackageRules(const std::string &pkgName, const std::vector &pkgContents); + /** + * This function will read all rules created by security-manager and + * save them in one file. This file will be used during next system + * boot. + */ + static void mergeRules(); + private: static void useTemplate( const std::string &templatePath, diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 35b4d69..bd8e740 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -411,6 +411,7 @@ int ServiceImpl::appInstall(const Credentials &creds, app_inst_req &&req) LogDebug("Adding Smack rules for new appName: " << req.appName << " with pkgName: " << req.pkgName << ". Applications in package: " << pkgContents.size()); SmackRules::installApplicationRules(req.appName, req.pkgName, authorId, pkgContents, allTizen2XApps, allTizen2XPackages); + SmackRules::mergeRules(); } catch (const SmackException::InvalidParam &e) { LogError("Invalid paramater during labeling: " << e.GetMessage()); return SECURITY_MANAGER_ERROR_INPUT_PARAM; @@ -521,6 +522,8 @@ int ServiceImpl::appUninstall(const Credentials &creds, app_inst_req &&req, LogDebug("Removing Smack rules for authorId " << authorId); SmackRules::uninstallAuthorRules(authorId); } + + SmackRules::mergeRules(); } catch (const SmackException::Base &e) { LogError("Error while removing Smack rules for application: " << e.DumpToString()); return SECURITY_MANAGER_ERROR_SETTING_FILE_LABEL_FAILED; diff --git a/src/common/smack-rules.cpp b/src/common/smack-rules.cpp index 494fe1c..f00fd33 100755 --- a/src/common/smack-rules.cpp +++ b/src/common/smack-rules.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2014-2016 Samsung Electronics Co., Ltd All Rights Reserved * * Contact: Rafal Krypa * @@ -32,11 +32,14 @@ #include #include #include +#include #include #include #include +#include +#include #include "smack-labels.h" #include "smack-rules.h" @@ -49,6 +52,9 @@ const char *const APP_RULES_TEMPLATE_FILE_PATH = tzplatform_mkpath4(TZ_SYS_RO_SH const char *const PKG_RULES_TEMPLATE_FILE_PATH = tzplatform_mkpath4(TZ_SYS_RO_SHARE, "security-manager", "policy", "pkg-rules-template.smack"); const char *const AUTHOR_RULES_TEMPLATE_FILE_PATH = tzplatform_mkpath4(TZ_SYS_RO_SHARE, "security-manager", "policy", "author-rules-template.smack"); +const char *const SMACK_RULES_PATH_MERGED = LOCAL_STATE_DIR "/security-manager/rules-merged/rules.merged"; +const char *const SMACK_RULES_PATH_MERGED_T = LOCAL_STATE_DIR "/security-manager/rules-merged/rules.merged.temp"; +const char *const SMACK_RULES_PATH = LOCAL_STATE_DIR "/security-manager/rules"; const char *const SMACK_APP_IN_PACKAGE_PERMS = "rwxat"; const char *const SMACK_APP_CROSS_PKG_PERMS = "rx"; const char *const SMACK_APP_PATH_OWNER_PERMS = "rwxat"; @@ -58,6 +64,7 @@ const char *const SMACK_USER = "User"; const char *const SMACK_SYSTEM = "System"; const char *const SMACK_APP_PATH_SYSTEM_PERMS = "rwxat"; const char *const SMACK_APP_PATH_USER_PERMS = "rwxat"; +const std::string TEMPORARY_FILE_SUFFIX = ".temp"; SmackRules::SmackRules() { @@ -119,10 +126,11 @@ void SmackRules::loadFromFile(const std::string &path) } } -void SmackRules::saveToFile(const std::string &path, bool truncFile) const +void SmackRules::saveToFile(const std::string &destPath) const { int fd; - int flags = O_CREAT | O_WRONLY | (truncFile ? O_TRUNC : O_APPEND); + int flags = O_CREAT | O_WRONLY | O_TRUNC; + std::string path = destPath + TEMPORARY_FILE_SUFFIX; fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, 0644)); if (fd == -1) { @@ -147,6 +155,12 @@ void SmackRules::saveToFile(const std::string &path, bool truncFile) const LogWarning("Error while closing the file: " << path << ", error: " << GetErrnoString(errno)); } } + + if (0 > rename(path.c_str(), destPath.c_str())) { + LogError("Error moving file " << path << " to " << destPath << ". Errno: " << GetErrnoString(errno)); + unlink(path.c_str()); + ThrowMsg(SmackException::FileError, "Error moving file " << path << " to " << destPath << ". Errno: " << GetErrnoString(errno)); + } } void SmackRules::addFromTemplateFile( @@ -287,20 +301,91 @@ void SmackRules::generateAllowOther2XApplicationDeps( std::string SmackRules::getPackageRulesFilePath(const std::string &pkgName) { - std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", ("pkg_" + pkgName).c_str())); - return path; + return std::string(SMACK_RULES_PATH) + "/pkg_" + pkgName; } std::string SmackRules::getApplicationRulesFilePath(const std::string &appName) { - std::string path(tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", ("app_" + appName).c_str())); - return path; + return std::string(SMACK_RULES_PATH) + "/app_" + appName; } std::string SmackRules::getAuthorRulesFilePath(const int authorId) { - std::string authorIdStr = std::to_string(authorId); - return tzplatform_mkpath3(TZ_SYS_SMACK, "accesses.d", ("author_" + authorIdStr).c_str()); + return std::string(SMACK_RULES_PATH) + "/author_" + std::to_string(authorId); +} + +void SmackRules::mergeRules() +{ + int tmp; + FS::FileNameVector files = FS::getFilesFromDirectory(SMACK_RULES_PATH); + + // remove ignore files with ".temp" suffix + files.erase( + std::remove_if(files.begin(), files.end(), + [&](const std::string &path) -> bool { + if (path.size() < TEMPORARY_FILE_SUFFIX.size()) + return false; + return std::equal( + TEMPORARY_FILE_SUFFIX.rbegin(), + TEMPORARY_FILE_SUFFIX.rend(), + path.rbegin()); + }), + files.end()); + + std::ofstream dst(SMACK_RULES_PATH_MERGED_T, std::ios::binary); + + if (dst.fail()) { + LogError("Error creating file: " << SMACK_RULES_PATH_MERGED_T); + ThrowMsg(SmackException::FileError, "Error creating file: " << SMACK_RULES_PATH_MERGED_T); + } + + for(auto const &e : files) { + std::ifstream src(std::string(SMACK_RULES_PATH) + "/" + e, std::ios::binary); + dst << src.rdbuf() << '\n'; + if (dst.bad()) { + LogError("I/O Error. File " << SMACK_RULES_PATH_MERGED << " will not be updated!"); + unlink(SMACK_RULES_PATH_MERGED_T); + ThrowMsg(SmackException::FileError, + "I/O Error. File " << SMACK_RULES_PATH_MERGED << " will not be updated!"); + } + + if (dst.fail()) { + // src.rdbuf() was empty + dst.clear(); + } + } + + if (dst.flush().fail()) { + LogError("Error flushing file: " << SMACK_RULES_PATH_MERGED_T); + unlink(SMACK_RULES_PATH_MERGED_T); + ThrowMsg(SmackException::FileError, "Error flushing file: " << SMACK_RULES_PATH_MERGED_T); + } + + if (0 > fsync(DPL::FstreamAccessors::GetFd(dst))) { + LogError("Error fsync on file: " << SMACK_RULES_PATH_MERGED_T); + unlink(SMACK_RULES_PATH_MERGED_T); + ThrowMsg(SmackException::FileError, "Error fsync on file: " << SMACK_RULES_PATH_MERGED_T); + } + + dst.close(); + if (dst.fail()) { + LogError("Error closing file: " << SMACK_RULES_PATH_MERGED_T); + unlink(SMACK_RULES_PATH_MERGED_T); + ThrowMsg(SmackException::FileError, "Error closing file: " << SMACK_RULES_PATH_MERGED_T); + } + + if ((tmp = rename(SMACK_RULES_PATH_MERGED_T, SMACK_RULES_PATH_MERGED)) == 0) + return; + + int err = errno; + + LogError("Error during file rename: " + << SMACK_RULES_PATH_MERGED_T << " to " + << SMACK_RULES_PATH_MERGED << " Errno: " << GetErrnoString(err)); + unlink(SMACK_RULES_PATH_MERGED_T); + ThrowMsg(SmackException::FileError, "Error during file rename: " + << SMACK_RULES_PATH_MERGED_T << " to " + << SMACK_RULES_PATH_MERGED << " Errno: " << GetErrnoString(err)); } void SmackRules::useTemplate( @@ -341,10 +426,12 @@ void SmackRules::updatePackageRules( const std::vector &pkgContents, const std::vector &appsGranted) { - useTemplate(PKG_RULES_TEMPLATE_FILE_PATH, getPackageRulesFilePath(pkgName), std::string(), pkgName); - SmackRules smackRules; - std::string pkgPath = getPackageRulesFilePath(pkgName); + smackRules.addFromTemplateFile( + PKG_RULES_TEMPLATE_FILE_PATH, + std::string(), + pkgName, + -1); smackRules.generatePackageCrossDeps(pkgContents); smackRules.generateAllowOther2XApplicationDeps(pkgName, appsGranted); @@ -352,7 +439,7 @@ void SmackRules::updatePackageRules( if (smack_smackfs_path() != NULL) smackRules.apply(); - smackRules.saveToFile(pkgPath, false); + smackRules.saveToFile(getPackageRulesFilePath(pkgName)); } diff --git a/src/dpl/core/include/dpl/fstream_accessors.h b/src/dpl/core/include/dpl/fstream_accessors.h new file mode 100644 index 0000000..bc0e167 --- /dev/null +++ b/src/dpl/core/include/dpl/fstream_accessors.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2000 - 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + * + * @file fstream-helper.h + * @author Marek Smolinski (m.smolinski@samsung.com) + * @version 1.0 + * @brief This file is the implementation file of fstream-helper + * + */ + +#ifndef _FSTREAM_ACCESSORS_H +#define _FSTREAM_ACCESSORS_H + +namespace DPL { + +/* + * Bypass lack of public member function to get file + * descriptor from fstream objects in std + * This feature is needed for flushing data from kernel space buffer to + * physical device [fsync(int fd) - syscall] on opened fstream object +*/ + +template +class FstreamAccessors : T::__filebuf_type { + typedef FstreamAccessors MyType; +public: + static int GetFd(T &strm) { + return static_cast(strm.rdbuf())->_M_file.fd(); + } +}; + +} // namespace DPL + +#endif // _FSTREAM_ACCESSORS_H diff --git a/systemd/CMakeLists.txt b/systemd/CMakeLists.txt index 2d14d5f..8bba273 100644 --- a/systemd/CMakeLists.txt +++ b/systemd/CMakeLists.txt @@ -1,10 +1,12 @@ CONFIGURE_FILE(security-manager.service.in security-manager.service @ONLY) CONFIGURE_FILE(security-manager-cleanup.service.in security-manager-cleanup.service @ONLY) +CONFIGURE_FILE(security-manager-rules-loader.service.in security-manager-rules-loader.service @ONLY) INSTALL(FILES security-manager.service security-manager.socket security-manager-cleanup.service + security-manager-rules-loader.service DESTINATION ${SYSTEMD_INSTALL_DIR} ) diff --git a/systemd/security-manager-rules-loader.service.in b/systemd/security-manager-rules-loader.service.in new file mode 100644 index 0000000..d10a3b5 --- /dev/null +++ b/systemd/security-manager-rules-loader.service.in @@ -0,0 +1,13 @@ +[Unit] +Description=SMACK merged rules loading +After=local-fs.target +Before=basic.target +DefaultDependencies=no + +[Service] +Type=oneshot +ExecStart=/bin/dd if=@LOCAL_STATE_DIR@/security-manager/rules-merged/rules.merged of=/sys/fs/smackfs/load2 bs=20M + +[Install] +WantedBy=basic.target + -- 2.7.4 From 31f77aea957f603c982f41acadf1023166fad509 Mon Sep 17 00:00:00 2001 From: Bartlomiej Grzelewski Date: Thu, 28 Apr 2016 15:06:46 +0200 Subject: [PATCH 08/16] Fix implementation of filesystem.cpp The function getFilesFromDirectory should not follow links. It should return the list of files in directlry. Change-Id: I142f8e0bc3a992da2f14d69e758426aff5df2ab6 --- src/common/filesystem.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/common/filesystem.cpp b/src/common/filesystem.cpp index b278ef1..5ad5232 100644 --- a/src/common/filesystem.cpp +++ b/src/common/filesystem.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -46,7 +47,7 @@ FileNameVector getFilesFromDirectory(const std::string &path) FileNameVector result; dirent tmp, *ptr; int err; - std::unique_ptr> dir(opendir(path.c_str()), closedir); + std::unique_ptr dir(opendir(path.c_str()), closedir); if (!dir.get()) { err = errno; @@ -63,10 +64,8 @@ FileNameVector getFilesFromDirectory(const std::string &path) break; struct stat finfo; - std::string filepath = path + "/" + ptr->d_name; - if (0 > stat(filepath.c_str(), &finfo)) { - ThrowMsg(FS::Exception::FileError, "Error reading: " << filepath); - continue; + if (0 > fstatat(dirfd(dir.get()), ptr->d_name, &finfo, AT_SYMLINK_NOFOLLOW)) { + ThrowMsg(FS::Exception::FileError, "Error reading: " << ptr->d_name); } if (S_ISREG(finfo.st_mode)) { -- 2.7.4 From e5aaf17b1afbe7a8efb916325222d9fdbf1615e4 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Fri, 29 Apr 2016 19:44:12 +0200 Subject: [PATCH 09/16] Don't check permissions on API calls in off-line mode Off-line mode was introduced to be used during image creation, when no services are running. It enables root to perform some security-manager operations on the client side. But in off-line mode not only security-manager isn't running. No services run, including cynara service. When libsecurity-manager-client tries to check whether the off-line mode user has access to proper privilege, it fails because cynara_check() has no off-line mode. Permission checking in such scenario isn't required. The user is already checked for UID 0 and even if it gets away from that check, it wouldn't be able to perform actual operations without being super user. Change-Id: I087bbc6b29a702a445d4498b96a950ca1e919efd Signed-off-by: Rafal Krypa --- src/client/client-offline.cpp | 8 +++++ src/client/client-security-manager.cpp | 5 ++- src/client/include/client-offline.h | 2 ++ src/common/include/credentials.h | 1 + src/common/include/service_impl.h | 5 +-- src/common/service_impl.cpp | 60 +++++++++++++++++----------------- 6 files changed, 46 insertions(+), 35 deletions(-) diff --git a/src/client/client-offline.cpp b/src/client/client-offline.cpp index 159d28b..c2d95df 100644 --- a/src/client/client-offline.cpp +++ b/src/client/client-offline.cpp @@ -81,4 +81,12 @@ bool ClientOffline::isOffline(void) return offlineMode; } +Credentials ClientOffline::getCredentials() +{ + Credentials creds = Credentials::getCredentialsFromSelf(); + if (isOffline()) + creds.authenticated = true; + return creds; +} + } // namespace SecurityManager diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 6b35e74..28403c6 100755 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -47,7 +47,6 @@ #include #include #include -#include #include #include @@ -204,7 +203,7 @@ int security_manager_app_install(const app_inst_req *p_req) int retval; ClientOffline offlineMode; if (offlineMode.isOffline()) { - Credentials creds = SecurityManager::Credentials::getCredentialsFromSelf(); + Credentials creds = offlineMode.getCredentials(); retval = SecurityManager::ServiceImpl().appInstall(creds, app_inst_req(*p_req)); } else { MessageBuffer send, recv; @@ -592,7 +591,7 @@ int security_manager_user_add(const user_req *p_req) int retval; ClientOffline offlineMode; if (offlineMode.isOffline()) { - Credentials creds = SecurityManager::Credentials::getCredentialsFromSelf(); + Credentials creds = offlineMode.getCredentials(); retval = SecurityManager::ServiceImpl().userAdd(creds, p_req->uid, p_req->utype); } else { MessageBuffer send, recv; diff --git a/src/client/include/client-offline.h b/src/client/include/client-offline.h index 3f2e421..d50fc6f 100644 --- a/src/client/include/client-offline.h +++ b/src/client/include/client-offline.h @@ -25,6 +25,7 @@ #ifndef _SECURITY_MANAGER_OFFLINE_ #define _SECURITY_MANAGER_OFFLINE_ +#include #include namespace SecurityManager { @@ -34,6 +35,7 @@ public: ClientOffline(); ~ClientOffline(); bool isOffline(void); + Credentials getCredentials(); private: bool offlineMode; diff --git a/src/common/include/credentials.h b/src/common/include/credentials.h index 003a37e..06546fc 100644 --- a/src/common/include/credentials.h +++ b/src/common/include/credentials.h @@ -35,6 +35,7 @@ public: uid_t uid; /* user ID of the sending process */ gid_t gid; /* group ID of the sending process */ std::string label; /* security context of the sending process */ + bool authenticated = false; /* Indicate that the caller has already been authenticated for access */ Credentials() = delete; static Credentials getCredentialsFromSelf(void); diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index bae4c79..313ae0e 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -36,6 +36,8 @@ namespace SecurityManager { class ServiceImpl { private: + static bool authenticate(const Credentials &creds, const std::string &privilege); + static uid_t getGlobalUserId(void); static bool isSubDir(const char *parent, const char *subdir); @@ -75,11 +77,10 @@ public: * * @param[in] creds credentials of the requesting process * @param[in] req uninstallation request - * @param[in] authenticated whether the caller has been already checked against Cynara policy * * @return API return code, as defined in protocols.h */ - int appUninstall(const Credentials &creds, app_inst_req &&req, bool authenticated = false); + int appUninstall(const Credentials &creds, app_inst_req &&req); /** * Process package id query. diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index bd8e740..60c0317 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -174,6 +174,14 @@ ServiceImpl::~ServiceImpl() { } +bool ServiceImpl::authenticate(const Credentials &creds, const std::string &privilege) +{ + if (creds.authenticated) + return true; + return Cynara::getInstance().check(creds.label, privilege, + std::to_string(creds.uid), std::to_string(creds.pid)); +} + uid_t ServiceImpl::getGlobalUserId(void) { static uid_t globaluid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER); @@ -266,16 +274,14 @@ void ServiceImpl::installRequestMangle(app_inst_req &req, std::string &cynaraUse bool ServiceImpl::installRequestAuthCheck(const Credentials &creds, const app_inst_req &req) { - if (req.installationType != SM_APP_INSTALL_LOCAL || req.uid != creds.uid) { - if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPINST_ADMIN, - std::to_string(creds.uid), std::to_string(creds.pid))) { - LogError("Caller is not permitted to install applications globally"); + if (req.installationType == SM_APP_INSTALL_LOCAL && req.uid == creds.uid) { + if (!authenticate(creds, Config::PRIVILEGE_APPINST_USER)) { + LogError("Caller is not permitted to install applications locally"); return false; } } else { - if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPINST_USER, - std::to_string(creds.uid), std::to_string(creds.pid))) { - LogError("Caller is not permitted to install applications"); + if (!authenticate(creds, Config::PRIVILEGE_APPINST_ADMIN)) { + LogError("Caller is not permitted to install applications globally"); return false; } } @@ -429,8 +435,7 @@ int ServiceImpl::appInstall(const Credentials &creds, app_inst_req &&req) return SECURITY_MANAGER_SUCCESS; } -int ServiceImpl::appUninstall(const Credentials &creds, app_inst_req &&req, - bool authenticated) +int ServiceImpl::appUninstall(const Credentials &creds, app_inst_req &&req) { std::string tizenVersion; std::string smackLabel; @@ -446,7 +451,7 @@ int ServiceImpl::appUninstall(const Credentials &creds, app_inst_req &&req, LogDebug("Uninstall parameters: appName=" << req.appName << ", uid=" << req.uid); - if (!authenticated && !installRequestAuthCheck(creds, req)) { + if (!installRequestAuthCheck(creds, req)) { LogError("Request from uid=" << creds.uid << ", Smack=" << creds.label << " for app uninstallation denied"); return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; @@ -625,9 +630,7 @@ int ServiceImpl::getAppGroups(const Credentials &creds, const std::string &appNa int ServiceImpl::userAdd(const Credentials &creds, uid_t uidAdded, int userType) { - if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_USER_ADMIN, - std::to_string(creds.uid), std::to_string(creds.pid))) { - + if (!authenticate(creds, Config::PRIVILEGE_USER_ADMIN)) { LogError("Caller is not permitted to manage users"); return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; } @@ -644,9 +647,7 @@ int ServiceImpl::userDelete(const Credentials &creds, uid_t uidDeleted) { int ret = SECURITY_MANAGER_SUCCESS; - if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_USER_ADMIN, - std::to_string(creds.uid), std::to_string(creds.pid))) { - + if (!authenticate(creds, Config::PRIVILEGE_USER_ADMIN)) { LogError("Caller is not permitted to manage users"); return SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED; } @@ -660,11 +661,14 @@ int ServiceImpl::userDelete(const Credentials &creds, uid_t uidDeleted) return SECURITY_MANAGER_ERROR_SERVER_ERROR; } + // Don't check whether the caller may uninstall apps of the removed user + Credentials credsTmp(creds); + credsTmp.authenticated = true; for (const auto &app : userApps) { app_inst_req req; req.uid = uidDeleted; req.appName = app; - if (appUninstall(creds, std::move(req), true) != SECURITY_MANAGER_SUCCESS) { + if (appUninstall(credsTmp, std::move(req)) != SECURITY_MANAGER_SUCCESS) { /*if uninstallation of this app fails, just go on trying to uninstall another ones. we do not have anything special to do about that matter - user will be deleted anyway.*/ ret = SECURITY_MANAGER_ERROR_SERVER_ERROR; @@ -709,14 +713,12 @@ int ServiceImpl::policyUpdate(const Credentials &creds, const std::vector listOfUsers; - if (Cynara::getInstance().check(creds.label, Config::PRIVILEGE_POLICY_ADMIN, uidStr, pidStr)) { + if (authenticate(creds, Config::PRIVILEGE_POLICY_ADMIN)) { LogDebug("User is privileged"); if (filter.user.compare(SECURITY_MANAGER_ANY)) { LogDebug("Limitting Cynara query to user: " << filter.user); @@ -1088,8 +1090,7 @@ int ServiceImpl::applyPrivatePathSharing( std::vector pkgContents; try { - if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPSHARING_ADMIN, - std::to_string(creds.uid), std::to_string(creds.pid))) { + if (!authenticate(creds, Config::PRIVILEGE_APPSHARING_ADMIN)) { LogError("Caller is not permitted to manage file sharing"); return SECURITY_MANAGER_ERROR_ACCESS_DENIED; } @@ -1182,8 +1183,7 @@ int ServiceImpl::dropPrivatePathSharing( { int errorRet; try { - if (!Cynara::getInstance().check(creds.label, Config::PRIVILEGE_APPSHARING_ADMIN, - std::to_string(creds.uid), std::to_string(creds.pid))) { + if (!authenticate(creds, Config::PRIVILEGE_APPSHARING_ADMIN)) { LogError("Caller is not permitted to manage file sharing"); return SECURITY_MANAGER_ERROR_ACCESS_DENIED; } -- 2.7.4 From 5aa65d374fe9f086ee68a189ef0dda1ae1d35574 Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Fri, 29 Apr 2016 20:06:00 +0200 Subject: [PATCH 10/16] Change logic of security_manager_set_process_groups_from_appid The API function sets groups in application candidate process. The following changes are applied: - groups are based on privileges assigned to appId, not pkgId - don't consider privileges granted to other apps in the package - if the process was previously added to any group that is mapped to a privilege and app doesn't have access to that privilege, the group will be removed from the process - no group will be added to the process more than once Change-Id: Ifbb5fe48f2ad0bcc69ca00c13e6d7f2a20b148a2 Signed-off-by: Rafal Krypa --- src/client/client-security-manager.cpp | 198 ++++++++++++++++++++++----------- src/common/cynara.cpp | 15 +++ src/common/include/cynara.h | 11 ++ src/common/include/service_impl.h | 7 +- src/common/service_impl.cpp | 60 +++++----- src/server/service/service.cpp | 12 +- 6 files changed, 192 insertions(+), 111 deletions(-) diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index 28403c6..dd55443 100755 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -405,87 +406,154 @@ int security_manager_set_process_label_from_appid(const char *app_name) return SECURITY_MANAGER_SUCCESS; } -SECURITY_MANAGER_API -int security_manager_set_process_groups_from_appid(const char *app_name) +static int getProcessGroups(std::vector &groups) { - using namespace SecurityManager; - MessageBuffer send, recv; - int ret; + int ret = getgroups(0, nullptr); + if (ret == -1) { + LogError("Unable to get number of current supplementary groups: " << + GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } + int cnt = ret; - LogDebug("security_manager_set_process_groups_from_appid() called"); + std::unique_ptr groupsPtr(new gid_t[cnt]); + if (!groupsPtr) { + LogError("Memory allocation failed."); + return SECURITY_MANAGER_ERROR_MEMORY; + } - return try_catch([&]() -> int { - //checking parameters + ret = getgroups(cnt, groupsPtr.get()); + if (ret == -1) { + LogError("Unable to get list of current supplementary groups: " << + GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } - if (app_name == nullptr) { - LogError("app_name is NULL"); - return SECURITY_MANAGER_ERROR_INPUT_PARAM; - } + groups.assign(groupsPtr.get(), groupsPtr.get() + cnt); + return SECURITY_MANAGER_SUCCESS; +} - //put data into buffer - Serialization::Serialize(send, static_cast(SecurityModuleCall::APP_GET_GROUPS), - std::string(app_name)); +static int setProcessGroups(const std::vector &groups) +{ + int ret = setgroups(groups.size(), groups.data()); + if (ret == -1) { + LogError("Unable to set list of current supplementary groups: " << + GetErrnoString(errno)); + return SECURITY_MANAGER_ERROR_UNKNOWN; + } - //send buffer to server - int retval = sendToServer(SERVICE_SOCKET, send.Pop(), recv); - if (retval != SECURITY_MANAGER_SUCCESS) { - LogDebug("Error in sendToServer. Error code: " << retval); - return retval; - } + return SECURITY_MANAGER_SUCCESS; +} - //receive response from server - Deserialization::Deserialize(recv, retval); - if (retval != SECURITY_MANAGER_SUCCESS) { - LogError("Failed to get list of groups from security-manager service. Error code: " << retval); - return retval; +static int groupNamesToGids(const std::vector &groupNames, + std::vector &groups) +{ + groups.reserve(groupNames.size()); + for (const auto &groupName : groupNames) { + struct group *grp = getgrnam(groupName.c_str()); + if (grp == nullptr) { + LogError("No such group: " << groupName); + return SECURITY_MANAGER_ERROR_UNKNOWN; } + groups.push_back(grp->gr_gid); + } - //How many new groups? - int newGroupsCnt; - Deserialization::Deserialize(recv, newGroupsCnt); + return SECURITY_MANAGER_SUCCESS; +} - //And how many groups do we belong to already? - int oldGroupsCnt; - ret = getgroups(0, nullptr); - if (ret == -1) { - LogError("Unable to get list of current supplementary groups: " << - GetErrnoString(errno)); - return SECURITY_MANAGER_ERROR_UNKNOWN; - } - oldGroupsCnt = ret; +static int getPrivilegedGroups(std::vector &groups) +{ + MessageBuffer send, recv; + int ret; - //Allocate an array for both old and new groups gids - std::unique_ptr groups(new gid_t[oldGroupsCnt + newGroupsCnt]); - if (!groups.get()) { - LogError("Memory allocation failed."); - return SECURITY_MANAGER_ERROR_MEMORY; - } + Serialization::Serialize(send, static_cast(SecurityModuleCall::GROUPS_GET)); + ret = sendToServer(SERVICE_SOCKET, send.Pop(), recv); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogDebug("Error in sendToServer. Error code: " << ret); + return ret; + } - //Get the old groups from process - ret = getgroups(oldGroupsCnt, groups.get()); - if (ret == -1) { - LogError("Unable to get list of current supplementary groups: " << - GetErrnoString(errno)); - return SECURITY_MANAGER_ERROR_UNKNOWN; - } + Deserialization::Deserialize(recv, ret); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogError("Failed to get list of groups from security-manager service. " << + "Error code: " << ret); + return ret; + } - //Get the new groups from server response - for (int i = 0; i < newGroupsCnt; ++i) { - gid_t gid; - Deserialization::Deserialize(recv, gid); - groups.get()[oldGroupsCnt + i] = gid; - LogDebug("Adding process to group " << gid); - } + std::vector groupNames; + Deserialization::Deserialize(recv, groupNames); + return groupNamesToGids(groupNames, groups); +} - //Apply the modified groups list - ret = setgroups(oldGroupsCnt + newGroupsCnt, groups.get()); - if (ret == -1) { - LogError("Unable to get list of current supplementary groups: " << - GetErrnoString(errno)); - return SECURITY_MANAGER_ERROR_UNKNOWN; +static int getAppGroups(const std::string appName, std::vector &groups) +{ + MessageBuffer send, recv; + int ret; + + Serialization::Serialize(send, static_cast(SecurityModuleCall::APP_GET_GROUPS), appName); + ret = sendToServer(SERVICE_SOCKET, send.Pop(), recv); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogDebug("Error in sendToServer. Error code: " << ret); + return ret; + } + + Deserialization::Deserialize(recv, ret); + if (ret != SECURITY_MANAGER_SUCCESS) { + LogError("Failed to get list of groups from security-manager service. " << + "Error code: " << ret); + return ret; + } + + std::vector groupNames; + Deserialization::Deserialize(recv, groupNames); + return groupNamesToGids(groupNames, groups); +} + +SECURITY_MANAGER_API +int security_manager_set_process_groups_from_appid(const char *app_name) +{ + using namespace SecurityManager; + int ret; + + LogDebug("security_manager_set_process_groups_from_appid() called"); + + return try_catch([&]() -> int { + //checking parameters + + if (app_name == nullptr) { + LogError("app_name is NULL"); + return SECURITY_MANAGER_ERROR_INPUT_PARAM; } - return SECURITY_MANAGER_SUCCESS; + std::vector currentGroups; + ret = getProcessGroups(currentGroups); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + LogDebug("Current supplementary groups count: " << currentGroups.size()); + + std::vector privilegedGroups; + ret = getPrivilegedGroups(privilegedGroups); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + LogDebug("All privileged supplementary groups count: " << privilegedGroups.size()); + + std::vector allowedGroups; + ret = getAppGroups(app_name, allowedGroups); + if (ret != SECURITY_MANAGER_SUCCESS) + return ret; + LogDebug("Allowed privileged supplementary groups count: " << allowedGroups.size()); + + std::unordered_set groupsSet(currentGroups.begin(), currentGroups.end()); + // Remove all groups that are mapped to privileges, so if app is not granted + // the privilege, the group will be dropped from current process + for (gid_t group : privilegedGroups) + groupsSet.erase(group); + + // Re-add those privileged groups that an app is entitled to + groupsSet.insert(allowedGroups.begin(), allowedGroups.end()); + LogDebug("Final supplementary groups count: " << groupsSet.size()); + + return setProcessGroups(std::vector(groupsSet.begin(), groupsSet.end())); }); } diff --git a/src/common/cynara.cpp b/src/common/cynara.cpp index bd336a2..d08f7d4 100755 --- a/src/common/cynara.cpp +++ b/src/common/cynara.cpp @@ -324,6 +324,21 @@ void CynaraAdmin::UpdateAppPolicy( SetPolicies(policies); } +void CynaraAdmin::GetAppPolicy(const std::string &label, const std::string &user, + std::vector &privileges) +{ + std::vector policies; + CynaraAdmin::getInstance().ListPolicies( + CynaraAdmin::Buckets.at(Bucket::MANIFESTS), + label, user, CYNARA_ADMIN_ANY, policies); + + for (auto &policy : policies) { + std::string privilege = policy.privilege; + if (privilege.compare(CYNARA_ADMIN_WILDCARD)) + privileges.push_back(std::move(privilege)); + } +} + void CynaraAdmin::UserInit(uid_t uid, security_manager_user_type userType) { Bucket bucket; diff --git a/src/common/include/cynara.h b/src/common/include/cynara.h index 62763d0..219667d 100644 --- a/src/common/include/cynara.h +++ b/src/common/include/cynara.h @@ -132,6 +132,17 @@ public: const std::vector &privileges); /** + * Fetch Cynara policies for the application and the user. + * Caller must have permission to access Cynara administrative socket. + * + * @param[in] label application Smack label + * @param[in] user user identifier + * @param[out] privileges currently enabled privileges + */ + void GetAppPolicy(const std::string &label, const std::string &user, + std::vector &privileges); + + /** * Depending on user type, create link between MAIN bucket and appropriate * USER_TYPE_* bucket for newly added user uid to apply permissions for that * user type. diff --git a/src/common/include/service_impl.h b/src/common/include/service_impl.h index 313ae0e..e96f02c 100644 --- a/src/common/include/service_impl.h +++ b/src/common/include/service_impl.h @@ -27,7 +27,7 @@ #include #include -#include +#include #include "credentials.h" #include "security-manager.h" @@ -102,11 +102,12 @@ public: * * @param[in] creds credentials of the requesting process * @param[in] appName application identifier - * @param[out] gids returned set of allowed group ids + * @param[out] groups returned vector of allowed groups * * @return API return code, as defined in protocols.h */ - int getAppGroups(const Credentials &creds, const std::string &appName, std::unordered_set &gids); + int getAppGroups(const Credentials &creds, const std::string &appName, + std::vector &groups); /** * Process user adding request. diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 60c0317..89b4c57 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -560,57 +560,47 @@ int ServiceImpl::getPkgName(const std::string &appName, std::string &pkgName) return SECURITY_MANAGER_SUCCESS; } +template +static void vectorRemoveDuplicates(std::vector &vec) +{ + std::sort(vec.begin(), vec.end()); + vec.erase(std::unique(vec.begin(), vec.end()), vec.end()); +} + int ServiceImpl::getAppGroups(const Credentials &creds, const std::string &appName, - std::unordered_set &gids) + std::vector &groups) { try { - std::string pkgName; - std::string smackLabel; - std::string uidStr = std::to_string(creds.uid); - std::string pidStr = std::to_string(creds.pid); - LogDebug("appName: " << appName); - - PrivilegeDb::getInstance().GetAppPkgName(appName, pkgName); - if (pkgName.empty()) { - LogWarning("Application " << appName << " not found in database"); - return SECURITY_MANAGER_ERROR_NO_SUCH_OBJECT; - } - LogDebug("pkgName: " << pkgName); - - smackLabel = SmackLabels::generateAppLabel(appName); + std::string smackLabel = SmackLabels::generateAppLabel(appName); LogDebug("smack label: " << smackLabel); std::vector privileges; - PrivilegeDb::getInstance().GetPkgPrivileges(pkgName, creds.uid, privileges); - /*there is also a need of checking, if privilege is granted to all users*/ - size_t tmp = privileges.size(); - PrivilegeDb::getInstance().GetPkgPrivileges(pkgName, getGlobalUserId(), privileges); - /*privileges needs to be sorted and with no duplications - for cynara sake*/ - std::inplace_merge(privileges.begin(), privileges.begin() + tmp, privileges.end()); - privileges.erase(unique(privileges.begin(), privileges.end()), privileges.end()); + std::string uidStr = std::to_string(creds.uid); + CynaraAdmin::getInstance().GetAppPolicy(smackLabel, uidStr, privileges); + CynaraAdmin::getInstance().GetAppPolicy(smackLabel, CYNARA_ADMIN_WILDCARD, privileges); + + vectorRemoveDuplicates(privileges); + + std::string pidStr = std::to_string(creds.pid); for (const auto &privilege : privileges) { - std::vector gidsTmp; - PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, gidsTmp); - if (!gidsTmp.empty()) { + std::vector privGroups; + PrivilegeDb::getInstance().GetPrivilegeGroups(privilege, privGroups); + if (!privGroups.empty()) { LogDebug("Considering privilege " << privilege << " with " << - gidsTmp.size() << " groups assigned"); - // TODO: create method in Cynara class for fetching all privileges of an application + privGroups.size() << " groups assigned"); + if (Cynara::getInstance().check(smackLabel, privilege, uidStr, pidStr)) { - for_each(gidsTmp.begin(), gidsTmp.end(), [&] (std::string group) { - struct group *grp = getgrnam(group.c_str()); - if (grp == NULL) { - LogError("No such group: " << group.c_str()); - return; - } - gids.insert(grp->gr_gid); - }); + groups.insert(groups.end(), + std::make_move_iterator(privGroups.begin()), + std::make_move_iterator(privGroups.end())); LogDebug("Cynara allowed, adding groups"); } else LogDebug("Cynara denied, not adding groups"); } } + vectorRemoveDuplicates(groups); } catch (const PrivilegeDb::Exception::Base &e) { LogError("Database error: " << e.DumpToString()); return SECURITY_MANAGER_ERROR_SERVER_ERROR; diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index 7d4dae9..a0607a0 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -196,18 +196,14 @@ void Service::processGetPkgName(MessageBuffer &buffer, MessageBuffer &send) void Service::processGetAppGroups(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) { std::string appName; - std::unordered_set gids; + std::vector groups; int ret; Deserialization::Deserialize(buffer, appName); - ret = serviceImpl.getAppGroups(creds, appName, gids); + ret = serviceImpl.getAppGroups(creds, appName, groups); Serialization::Serialize(send, ret); - if (ret == SECURITY_MANAGER_SUCCESS) { - Serialization::Serialize(send, static_cast(gids.size())); - for (const auto &gid : gids) { - Serialization::Serialize(send, gid); - } - } + if (ret == SECURITY_MANAGER_SUCCESS) + Serialization::Serialize(send, groups); } void Service::processUserAdd(MessageBuffer &buffer, MessageBuffer &send, const Credentials &creds) -- 2.7.4 From 68780c5ef183480b6d662dacf74a2dff5dc481df Mon Sep 17 00:00:00 2001 From: Tomasz Swierczek Date: Mon, 2 May 2016 07:51:39 +0200 Subject: [PATCH 11/16] Release version 1.1.3 Changes: Change logic of security_manager_set_process_groups_from_appid Don't check permissions on API calls in off-line mode Fix implementation of filesystem.cpp Move smack files to new directory refactoring: use common function template for getting label by libsmack Add installation types (global, local, preloaded). Integrate with Cynara, clients must be privileged db: update schema to version 2 Add constraint error in database logic. More error messages Use app instead of app_pkg_view in sqlite queries security-manager-policy-reload: don't print errors on image build Add privilege-group mapping for iotcon Remove unused table version Adjust Cynara privileges required by privacy manager APIs Revert "Add installation types (global, local, preloaded)." DB: Change app ids to app names in private sharing Revoke subject label of uninstalled application Change-Id: I0882ea1a261643b942e35cf528d0367599293c3d Signed-off-by: Tomasz Swierczek --- packaging/security-manager.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/security-manager.spec b/packaging/security-manager.spec index 97eec65..23b73c3 100755 --- a/packaging/security-manager.spec +++ b/packaging/security-manager.spec @@ -1,6 +1,6 @@ Name: security-manager Summary: Security manager and utilities -Version: 1.1.2 +Version: 1.1.3 Release: 4 Group: Security/Service License: Apache-2.0 -- 2.7.4 From a13100d77fcfeb2cfc6cbc70f66263cb449218a9 Mon Sep 17 00:00:00 2001 From: jooseong lee Date: Mon, 9 May 2016 15:19:17 +0900 Subject: [PATCH 12/16] Use wildcard user in cynara policy installing a preloaded app Preloaded app is a global app, which is installed in TZ_SYS_RO. User credential in cynara app policy should be wildcard. Change-Id: I54841d051d1e7671e23e2cecae0a1ed1a601395a Signed-off-by: jooseong lee --- src/common/service_impl.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 89b4c57..9e4cb22 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -259,15 +259,13 @@ void ServiceImpl::installRequestMangle(app_inst_req &req, std::string &cynaraUse SM_APP_INSTALL_GLOBAL : SM_APP_INSTALL_LOCAL; - if (req.installationType == SM_APP_INSTALL_GLOBAL) { + if (req.installationType == SM_APP_INSTALL_GLOBAL + || req.installationType == SM_APP_INSTALL_PRELOADED) { LogDebug("Installation type: global installation"); cynaraUserStr = CYNARA_ADMIN_WILDCARD; } else if (req.installationType == SM_APP_INSTALL_LOCAL) { LogDebug("Installation type: local installation"); cynaraUserStr = std::to_string(static_cast(req.uid)); - } else if (req.installationType == SM_APP_INSTALL_PRELOADED) { - LogDebug("Installation type: preloaded installation"); - cynaraUserStr = std::to_string(static_cast(req.uid)); } else LogError("Installation type: unknown"); } -- 2.7.4 From 69f043c7346fa6bc7f0cfeee8d8cdcbbcd58d130 Mon Sep 17 00:00:00 2001 From: jooseong lee Date: Mon, 9 May 2016 19:29:44 +0900 Subject: [PATCH 13/16] Release version 1.1.4 Changes: Use wildcard user in cynara policy installing a preloaded app Change-Id: I695c9422a1ff77c493484e18f07fcd9090a2af4e Signed-off-by: jooseong lee --- packaging/security-manager.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/security-manager.spec b/packaging/security-manager.spec index 23b73c3..6ee2cf2 100755 --- a/packaging/security-manager.spec +++ b/packaging/security-manager.spec @@ -1,6 +1,6 @@ Name: security-manager Summary: Security manager and utilities -Version: 1.1.3 +Version: 1.1.4 Release: 4 Group: Security/Service License: Apache-2.0 -- 2.7.4 From b80796991d0b761e1a711f9bf5243fa5afaab5fc Mon Sep 17 00:00:00 2001 From: jooseong lee Date: Wed, 11 May 2016 20:06:59 +0900 Subject: [PATCH 14/16] Add privilege-group mapping for tethering.admin privilege Refer to : * https://review.tizen.org/gerrit/69079 * https://review.tizen.org/gerrit/69071 Change-Id: Idb914ceaaed4ca208e1de725a22395fd5e82b7d5 Signed-off-by: jooseong lee --- policy/privilege-group.list | 1 + 1 file changed, 1 insertion(+) diff --git a/policy/privilege-group.list b/policy/privilege-group.list index d7c9650..113a547 100644 --- a/policy/privilege-group.list +++ b/policy/privilege-group.list @@ -12,3 +12,4 @@ http://tizen.org/privilege/message.read priv_message_read http://tizen.org/privilege/mapservice priv_mapservice http://tizen.org/privilege/internet priv_internet http://tizen.org/privilege/network.get priv_network_get +http://tizen.org/privilege/tethering.admin priv_tethering_admin -- 2.7.4 From 7482e3bc69c343b696c901127c894eeaf1ca93ef Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Wed, 27 Apr 2016 11:19:43 +0200 Subject: [PATCH 15/16] Add path registration API stub [Problem] Path registration is package specific and requires a separate processing. [Solution] Create API for package path registration. [Verification] Successfull compilation Change-Id: Ie31d756b7dc7ca9bca82305b03dd8000ba6b9bc5 --- src/client/client-security-manager.cpp | 4 +- src/cmd/security-manager-cmd.cpp | 8 ++-- src/common/include/protocols.h | 11 ++++- src/common/service_impl.cpp | 6 +-- src/include/app-manager.h | 76 ++++++++++++++++++++++++++++++++++ src/include/security-manager-types.h | 5 +++ src/server/service/service.cpp | 2 +- 7 files changed, 101 insertions(+), 11 deletions(-) diff --git a/src/client/client-security-manager.cpp b/src/client/client-security-manager.cpp index dd55443..9958c49 100755 --- a/src/client/client-security-manager.cpp +++ b/src/client/client-security-manager.cpp @@ -173,7 +173,7 @@ int security_manager_app_inst_req_add_path(app_inst_req *p_req, const char *path if (!p_req || !path || (path_type < 0) || (path_type >= SECURITY_MANAGER_ENUM_END)) return SECURITY_MANAGER_ERROR_INPUT_PARAM; - p_req->appPaths.push_back(std::make_pair(path, path_type)); + p_req->pkgPaths.push_back(std::make_pair(path, path_type)); return SECURITY_MANAGER_SUCCESS; } @@ -215,7 +215,7 @@ int security_manager_app_install(const app_inst_req *p_req) p_req->appName, p_req->pkgName, p_req->privileges, - p_req->appPaths, + p_req->pkgPaths, p_req->uid, p_req->tizenVersion, p_req->authorName, diff --git a/src/cmd/security-manager-cmd.cpp b/src/cmd/security-manager-cmd.cpp index 0e9b5c5..762587b 100644 --- a/src/cmd/security-manager-cmd.cpp +++ b/src/cmd/security-manager-cmd.cpp @@ -192,7 +192,7 @@ static bool loadPaths(const std::vector &paths, LogDebug("Wrong paths size: " << paths.size()); return false; } - req.appPaths.clear(); + req.pkgPaths.clear(); for (std::vector::size_type i = 1; i < paths.size(); i += 2) { app_install_path_type pathType; LogDebug("path: " << paths[i - 1]); @@ -201,13 +201,13 @@ static bool loadPaths(const std::vector &paths, } catch (const std::out_of_range &e) { std::cout << "Invalid path type found." << std::endl; LogError("Invalid path type found."); - req.appPaths.clear(); + req.pkgPaths.clear(); return false; } LogDebug("path type: " << pathType << " (" << paths[i] << ")"); - req.appPaths.push_back(std::make_pair(paths[i - 1], pathType)); + req.pkgPaths.push_back(std::make_pair(paths[i - 1], pathType)); } - return (!req.appPaths.empty()); + return (!req.pkgPaths.empty()); } static void parseInstallOptions(int argc, char *argv[], diff --git a/src/common/include/protocols.h b/src/common/include/protocols.h index 2283205..3926606 100644 --- a/src/common/include/protocols.h +++ b/src/common/include/protocols.h @@ -32,11 +32,13 @@ #include #include +typedef std::vector> pkg_paths; + struct app_inst_req { std::string appName; std::string pkgName; std::vector privileges; - std::vector> appPaths; + pkg_paths pkgPaths; uid_t uid; std::string tizenVersion; std::string authorName; @@ -54,6 +56,13 @@ struct private_sharing_req { std::vector paths; }; +struct path_req { + std::string pkgName; + uid_t uid; + pkg_paths pkgPaths; + int installationType = SM_APP_INSTALL_NONE; +}; + namespace SecurityManager { extern char const * const SERVICE_SOCKET; diff --git a/src/common/service_impl.cpp b/src/common/service_impl.cpp index 9e4cb22..a961d24 100755 --- a/src/common/service_impl.cpp +++ b/src/common/service_impl.cpp @@ -298,7 +298,7 @@ bool ServiceImpl::installRequestPathsCheck(const app_inst_req &req, std::string correctPath.append("/").append(req.pkgName); LogDebug("correctPath: " << correctPath); - for (const auto &path : req.appPaths) { + for (const auto &path : req.pkgPaths) { std::unique_ptr> real_path( realpath(path.first.c_str(), NULL), free); if (!real_path.get()) { @@ -402,11 +402,11 @@ int ServiceImpl::appInstall(const Credentials &creds, app_inst_req &&req) } try { - if (!req.appPaths.empty()) + if (!req.pkgPaths.empty()) SmackLabels::setupAppBasePath(req.pkgName, appPath); // register paths - for (const auto &appPath : req.appPaths) { + for (const auto &appPath : req.pkgPaths) { const std::string &path = appPath.first; app_install_path_type pathType = static_cast(appPath.second); SmackLabels::setupPath(req.pkgName, path, pathType, authorId); diff --git a/src/include/app-manager.h b/src/include/app-manager.h index c297d75..02207b2 100644 --- a/src/include/app-manager.h +++ b/src/include/app-manager.h @@ -153,6 +153,82 @@ int security_manager_app_install(const app_inst_req *p_req); */ int security_manager_app_uninstall(const app_inst_req *p_req); +/* + * This function is responsible for initialize path_req data structure. It uses + * dynamic allocation inside and user responsibility is to call + * security_manager_path_req_free() for freeing allocated resources. + * + * \param[in] pp_req Address of pointer for handle path_req structure + * \return API return code or error code + */ +int security_manager_path_req_new(path_req **pp_req); + +/* + * This function is used to free resources allocated by calling + * security_manager_path_req_new(). + * \param[in] p_req Pointer handling allocated path_req structure + */ +void security_manager_path_req_free(path_req *p_req); + +/* + * This function is used to set up package identifier in path_req structure. + * + * \param[in] p_req Pointer handling path_req structure + * \param[in] pkg_id Package identifier + * \return API return code or error code + */ +int security_manager_path_req_set_pkg_id(path_req *p_req, const char *pkg_id); + +/* + * This function is used to set up installation type (global, local, preloaded). + * If type is not set and if installation is performed by global user, type is set to + * 'SM_APP_INSTALL_GLOBAL'. Otherwise installation type is set to 'SM_APP_INSTALL_LOCAL'. + * + * \param[in] p_req Pointer handling path_req structure + * \param[in] type Installation type + * \return API return code or error code + */ +int security_manager_path_req_set_install_type(path_req *p_req, const enum app_install_type type); + +/* + * This function is used to add a package path to path_req structure. It can be + * called multiple times. + * + * \param[in] p_req Pointer handling path_req structure + * \param[in] path Package path + * \param[in] path_type Package path type + * \return API return code or error code + */ +int security_manager_path_req_add_path(path_req *p_req, const char *path, const int path_type); + +/* + * This function is used to set up user identifier in path_req structure. + * This field simplifies support for online and offline modes. + * + * \param[in] p_req Pointer handling path_req structure + * \param[in] uid User identifier (UID) + * \return API return code or error code + */ +int security_manager_path_req_set_uid(path_req *p_req, const uid_t uid); + +/* + * This function is used to register a set of paths for given package using + * filled up path_req data structure. + * + * Required privileges: + * - http://tizen.org/privilege/notexist (if uid is not set or set to current user's uid) + * - http://tizen.org/privilege/notexist (if uid is set to some other user's uid) + * + * \param[in] p_req Pointer handling path_req structure + * + * \return API return code or error code: it would be + * - SECURITY_MANAGER_SUCCESS on success, + * - SECURITY_MANAGER_ERROR_AUTHENTICATION_FAILED when user does not + * have rights to install requested directories, + * - SECURITY_MANAGER_ERROR_UNKNOWN on other errors. + */ +int security_manager_paths_register(const path_req *p_req); + #ifdef __cplusplus } #endif diff --git a/src/include/security-manager-types.h b/src/include/security-manager-types.h index 01820b4..8f1c1db 100644 --- a/src/include/security-manager-types.h +++ b/src/include/security-manager-types.h @@ -115,6 +115,11 @@ typedef struct policy_entry policy_entry; struct private_sharing_req; typedef struct private_sharing_req private_sharing_req; +/*! brief data structure responsible for handling informations required to register + * a set of paths for given package. */ +struct path_req; +typedef struct path_req path_req; + /*! \brief wildcard to be used in requests to match all possible values of given field. * Use it, for example when it is desired to list or apply policy change for all * users or all apps for selected user. diff --git a/src/server/service/service.cpp b/src/server/service/service.cpp index a0607a0..a102801 100644 --- a/src/server/service/service.cpp +++ b/src/server/service/service.cpp @@ -163,7 +163,7 @@ void Service::processAppInstall(MessageBuffer &buffer, MessageBuffer &send, cons Deserialization::Deserialize(buffer, req.appName); Deserialization::Deserialize(buffer, req.pkgName); Deserialization::Deserialize(buffer, req.privileges); - Deserialization::Deserialize(buffer, req.appPaths); + Deserialization::Deserialize(buffer, req.pkgPaths); Deserialization::Deserialize(buffer, req.uid); Deserialization::Deserialize(buffer, req.tizenVersion); Deserialization::Deserialize(buffer, req.authorName); -- 2.7.4 From afe66aa54b5c4ecb7c107e6a39c043ba82a7b05a Mon Sep 17 00:00:00 2001 From: Krzysztof Jackiewicz Date: Thu, 28 Apr 2016 17:26:28 +0200 Subject: [PATCH 16/16] Move author_id to pkg - db migration [Problem] Paths will be registered per pkg but path can be shared between apps of the same author and the author is a feature of an app. [Solution] Make author a feature of a pkg. Modify db accordingly and add proper migration script. [Verification] Install on v2 version and run tests. Change-Id: I6a9933ec25094a92f20b76b3f72cbd4064f060c7 --- db/db.sql | 28 +++++++++++++++++++--------- db/updates/update-db-to-v3.sql | 28 ++++++++++++++++++++++++++++ src/common/include/privilege_db.h | 2 +- 3 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 db/updates/update-db-to-v3.sql diff --git a/db/db.sql b/db/db.sql index 8d85ba2..b5236b1 100644 --- a/db/db.sql +++ b/db/db.sql @@ -4,12 +4,14 @@ PRAGMA auto_vacuum = NONE; BEGIN EXCLUSIVE TRANSACTION; -PRAGMA user_version = 2; +PRAGMA user_version = 3; CREATE TABLE IF NOT EXISTS pkg ( pkg_id INTEGER PRIMARY KEY, name VARCHAR NOT NULL, +author_id INTEGER, UNIQUE (name) +FOREIGN KEY (author_id) REFERENCES autor (author_id) ); CREATE TABLE IF NOT EXISTS app ( @@ -18,10 +20,8 @@ pkg_id INTEGER NOT NULL, uid INTEGER NOT NULL, name VARCHAR NOT NULL, version VARCHAR NOT NULL, -author_id INTEGER, UNIQUE (name, uid), FOREIGN KEY (pkg_id) REFERENCES pkg (pkg_id) -FOREIGN KEY (author_id) REFERENCES author (author_id) ); CREATE TABLE IF NOT EXISTS privilege ( @@ -91,7 +91,7 @@ SELECT app.uid, pkg.name as pkg_name, app.version as version, - app.author_id, + pkg.author_id, author.name as author_name FROM app LEFT JOIN pkg USING (pkg_id) @@ -118,14 +118,24 @@ DROP TRIGGER IF EXISTS app_pkg_view_insert_trigger; CREATE TRIGGER app_pkg_view_insert_trigger INSTEAD OF INSERT ON app_pkg_view BEGIN + SELECT RAISE(ABORT, 'Another application from this package is already installed with different author') + WHERE EXISTS (SELECT 1 FROM app_pkg_view + WHERE pkg_name=NEW.pkg_name + AND author_name IS NOT NULL + AND NEW.author_name IS NOT NULL + AND author_name!=NEW.author_name); + INSERT OR IGNORE INTO author(name) VALUES (NEW.author_name); - INSERT OR IGNORE INTO pkg(name) VALUES (NEW.pkg_name); - INSERT OR IGNORE INTO app(pkg_id, name, uid, version, author_id) VALUES ( + INSERT OR IGNORE INTO pkg(name, author_id) VALUES ( + NEW.pkg_name, + (SELECT author_id FROM author WHERE name=NEW.author_name)); + -- If pkg have already existed with empty author do update it + UPDATE pkg SET author_id=(SELECT author_id FROM author WHERE name=NEW.author_name) WHERE name=NEW.pkg_name AND author_id IS NULL; + INSERT OR IGNORE INTO app(pkg_id, name, uid, version) VALUES ( (SELECT pkg_id FROM pkg WHERE name=NEW.pkg_name), NEW.app_name, NEW.uid, - NEW.version, - (SELECT author_id FROM author WHERE name=NEW.author_name)); + NEW.version); END; DROP TRIGGER IF EXISTS app_pkg_view_delete_trigger; @@ -134,7 +144,7 @@ INSTEAD OF DELETE ON app_pkg_view BEGIN DELETE FROM app WHERE app_id=OLD.app_id AND uid=OLD.uid; DELETE FROM pkg WHERE pkg_id NOT IN (SELECT DISTINCT pkg_id from app); - DELETE FROM author WHERE author_id NOT IN (SELECT author_id FROM app WHERE author_id IS NOT NULL); + DELETE FROM author WHERE author_id NOT IN (SELECT DISTINCT author_id FROM pkg WHERE author_id IS NOT NULL); END; DROP VIEW IF EXISTS app_private_sharing_view; diff --git a/db/updates/update-db-to-v3.sql b/db/updates/update-db-to-v3.sql new file mode 100644 index 0000000..82d4d55 --- /dev/null +++ b/db/updates/update-db-to-v3.sql @@ -0,0 +1,28 @@ +PRAGMA foreign_keys = OFF; +BEGIN EXCLUSIVE TRANSACTION; + +PRAGMA user_version = 3; + +-- Tables +ALTER TABLE pkg ADD COLUMN author_id INTEGER REFERENCES author (author_id); + +CREATE TABLE app_new ( +app_id INTEGER PRIMARY KEY, +pkg_id INTEGER NOT NULL, +uid INTEGER NOT NULL, +name VARCHAR NOT NULL, +version VARCHAR NOT NULL, +UNIQUE (name, uid), +FOREIGN KEY (pkg_id) REFERENCES pkg (pkg_id) +); + +INSERT INTO app_new SELECT app_id, pkg_id, uid, name, version FROM app; + +-- TODO this will ignore all other authors of given pkg apps except 1st one. Maybe the migration should fail in such case? +UPDATE pkg SET author_id = (SELECT author_id FROM app_pkg_view WHERE author_id IS NOT NULL AND pkg_id = pkg.pkg_id); + +DROP TABLE app; +ALTER TABLE app_new RENAME TO app; + +COMMIT TRANSACTION; +PRAGMA foreign_keys = ON; diff --git a/src/common/include/privilege_db.h b/src/common/include/privilege_db.h index 84d595c..e0c1160 100644 --- a/src/common/include/privilege_db.h +++ b/src/common/include/privilege_db.h @@ -125,7 +125,7 @@ private: { StmtType::EGetAllTizen2XPackages, "SELECT DISTINCT pkg_name FROM app_pkg_view WHERE version LIKE '2.%%' AND app_name <> ?" }, { StmtType::EGetAppsInPkg, " SELECT app_name FROM app_pkg_view WHERE pkg_name = ?" }, { StmtType::EGetGroups, "SELECT DISTINCT group_name FROM privilege_group_view" }, - { StmtType::EGetAppAuthorId, "SELECT author_id FROM app WHERE name = ? AND author_id IS NOT NULL"}, + { StmtType::EGetAppAuthorId, "SELECT author_id FROM app_pkg_view WHERE app_name = ? AND author_id IS NOT NULL"}, { StmtType::EAuthorIdExists, "SELECT count(*) FROM author where author_id=?"}, }; -- 2.7.4