- The current white list implementation needs to access /proc/xxx/attr/current.
- We have to remove the codes directly accessing above file.
- And the auth-fw is not a proper place to manage the while list
because each division may want to change it on its own.
- The privilege is more flexible than white list.
required privilege: "http://tizen.org/privilege/internal/default/platform"
Change-Id: I036ecd132fb163adc58b2e721b448c7a2668e33c
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(pkgconfig)
ADD_SUBDIRECTORY(systemd)
-ADD_SUBDIRECTORY(conf)
ADD_SUBDIRECTORY(tests)
+++ /dev/null
-# Copyright (c) 2015 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 Jooseong Lee <jooseong.lee@samsung.com>
-#
-
-INSTALL(FILES client-whitelist DESTINATION ${SYS_CONFIG_DIR}/${SERVICE_NAME})
-INSTALL(FILES admin-client-whitelist DESTINATION ${SYS_CONFIG_DIR}/${SERVICE_NAME})
+++ /dev/null
-# subject labels allowed to use password reset and password policy setting
-# put each allowed label in new line
-System
-System::Privileged
+++ /dev/null
-# subject labels allowed to use password checking/setting
-# put each allowed label in new line
-# If the preloaded app can hold internal privilege later,
-# the whitelist would be replaced with cynara.
-System
-System::Privileged
-User
-# This is for the mobile preloaded app.
-User::Pkg::org.tizen.keyguard
-User::Pkg::org.tizen.lockscreen
-User::Pkg::org.tizen.setting
<request>
<domain name="_" />
</request>
- <assign>
- <filesystem path="/usr/bin/auth-fw-test" exec_label="System" />
- </assign>
</manifest>
BuildRequires: pkgconfig(libsmack)
BuildRequires: pkgconfig(libsystemd)
BuildRequires: pkgconfig(libtzplatform-config)
+BuildRequires: pkgconfig(cynara-client)
+BuildRequires: pkgconfig(cynara-creds-socket)
+BuildRequires: pkgconfig(cynara-session)
Requires: security-config
Requires: lib%{name}-sw-backend = %{version}-%{release}
%{?systemd_requires}
-DCMAKE_VERBOSE_MAKEFILE=ON \
-DSERVICE_NAME=%{name} \
-DBIN_DIR:PATH=%{bin_dir} \
- -DSYS_CONFIG_DIR:PATH=%{_sysconfdir} \
-DRUN_DIR:PATH=%{run_dir} \
-DRW_DATA_DIR:PATH=%{rw_data_dir} \
-DPLUGIN_DIR:PATH=%{plugin_dir} \
%{_unitdir}/sockets.target.wants/%{sock_passwd_set}
%{_unitdir}/sockets.target.wants/%{sock_passwd_reset}
%{_unitdir}/sockets.target.wants/%{sock_passwd_policy}
-%{_sysconfdir}/%{name}/client-whitelist
-%{_sysconfdir}/%{name}/admin-client-whitelist
%dir %attr(770, %{user_name}, %{group_name}) %{rw_data_dir}
%files -n lib%{name}-client
Summary: Authentication framework (test)
Group: Security/Testing
BuildRequires: pkgconfig(klay)
+BuildRequires: pkgconfig(security-manager)
Requires: %{name} = %{version}
%description test
* no checks before password replacement (expiration time check, currently set password checks,
* history check and attempt count check are skipped).
*
+ * \par Client privilege:
+ * This API requires the following privilege. It means only an internal module or
+ * an application with the platform privilege can call this API.
+ * - http://tizen.org/privilege/internal/default/platform
+ *
* \par Sync (or) Async:
* This is a Synchronous API.
*
* if policies is valid. When there are new polices for max attempts and valid period,
* these policies apply to current normal(lock) password immediately.
*
+ * \par Client privilege:
+ * This API requires the following privilege. It means only an internal module or
+ * an application with the platform privilege can call this API.
+ * - http://tizen.org/privilege/internal/default/platform
+ *
* \par Sync (or) Async:
* This is a Synchronous API.
*
* \par Method of function operation:
* Sends input password to auth-fw, auth-fw compares hashed current password and hashed input password.
*
+ * \par Client privilege:
+ * This API requires the following privilege. It means only an internal module or
+ * an application with the platform privilege can call this API.
+ * - http://tizen.org/privilege/internal/default/platform
+ *
* \par Sync (or) Async:
* This is a Synchronous API.
*
* \par Method of function operation:
* Sends a validate request to auth-fw and auth-fw replies with password information.
*
+ * \par Client privilege:
+ * This API requires the following privilege. It means only an internal module or
+ * an application with the platform privilege can call this API.
+ * - http://tizen.org/privilege/internal/default/platform
+ *
* \par Sync (or) Async:
* This is a Synchronous API.
*
* \par Method of function operation:
* Sends a check request to auth-fw and auth-fw replies with password availability.
*
+ * \par Client privilege:
+ * This API requires the following privilege. It means only an internal module or
+ * an application with the platform privilege can call this API.
+ * - http://tizen.org/privilege/internal/default/platform
+ *
* \par Sync (or) Async:
* This is a Synchronous API.
*
* \par Method of function operation:
* Sends a check request to auth-fw and auth-fw replies with password reusability information.
*
+ * \par Client privilege:
+ * This API requires the following privilege. It means only an internal module or
+ * an application with the platform privilege can call this API.
+ * - http://tizen.org/privilege/internal/default/platform
+ *
* \par Sync (or) Async:
* This is a Synchronous API.
*
* checks current password and set new password to current only when current
* password is correct and new password meet password policies.
*
+ * \par Client privilege:
+ * This API requires the following privilege. It means only an internal module or
+ * an application with the platform privilege can call this API.
+ * - http://tizen.org/privilege/internal/default/platform
+ *
* \par Sync (or) Async:
* This is a Synchronous API.
*
REQUIRED
libsmack
libsystemd
+ cynara-client
+ cynara-creds-socket
+ cynara-session
)
FIND_PACKAGE(Threads REQUIRED)
SET(SERVER_SOURCES
main/generic-socket-manager.cpp
+ main/privilege-check.cpp
main/server-main.cpp
main/smack-check.cpp
main/socket-manager.cpp
--- /dev/null
+/*
+ * Authentication password
+ *
+ * Copyright (c) 2025 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact: Jooseong Lee <jooseong.lee@samsung.com>
+ *
+ * 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
+ */
+
+#ifndef _PRIVILEGE_CHECK_H_
+#define _PRIVILEGE_CHECK_H_
+
+#include <memory>
+#include <string>
+
+#include <cynara-client.h>
+
+namespace AuthPasswd {
+
+const std::string PLATFORM_PRIVILEGE = "http://tizen.org/privilege/internal/default/platform";
+
+class CynaraChecker {
+public:
+ // singleton pattern
+ static CynaraChecker& instance() {
+ static CynaraChecker inst; // created once
+ return inst;
+ }
+
+ // deletes copy constructor & copy assignment operator
+ CynaraChecker(const CynaraChecker&) = delete;
+ CynaraChecker& operator=(const CynaraChecker&) = delete;
+
+ // deletes move constructor & move assignment operator
+ CynaraChecker(CynaraChecker&&) = delete;
+ CynaraChecker& operator=(CynaraChecker&) = delete;
+
+ // calls cynara_finish();
+ ~CynaraChecker();
+
+ // checks if a client has the given privilege.
+ bool checkClientPrivilege(int sockfd, const std::string& privilege);
+
+private:
+ cynara* m_CynaraInstance;
+
+ // calls cynara_initialize()
+ CynaraChecker();
+};
+
+
+} // namespace AuthPasswd
+
+#endif // _PRIVILEGE_CHECK_H_
*/
int smack_check(void);
-/*
- * Check whether client is allowed or not on whitelist.
- * Returns true if client label is present, fail otherwise.
- */
-bool checkClientOnWhitelist(int sockfd, std::string whitelistPath);
-
} // namespace AuthPasswd
#endif // _SMACK_CHECK_H_
--- /dev/null
+/*
+ * Copyright (c) 2025 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
+ */
+/*
+ * @author Dongsun Lee(ds73.lee@samsung.com)
+ * @version 1.0
+ * @brief Check client's privilege
+ */
+#include "privilege-check.h"
+
+#include <dpl/log/log.h>
+
+#include <cynara-creds-socket.h>
+#include <cynara-session.h>
+
+
+namespace AuthPasswd {
+
+struct string_free_deleter {
+ void operator()(char * p) const {
+ free(p);
+ }
+};
+
+static inline std::string cynara_error_to_string(int error) {
+ char buffer[256];
+ int ret = cynara_strerror(error, buffer, sizeof(buffer));
+ if(ret == CYNARA_API_SUCCESS)
+ return std::string(buffer);
+ return std::string("Can't translate error");
+}
+
+CynaraChecker::CynaraChecker()
+{
+ cynara_configuration* cynara_conf = nullptr;
+ int error = cynara_configuration_create(&cynara_conf);
+ if(error != CYNARA_API_SUCCESS) {
+ LogError("Can't initialize Cynara configuration: " << error);
+ throw std::runtime_error("Can't initialize Cynara configuration");
+ }
+
+ error = cynara_initialize(&m_CynaraInstance, cynara_conf);
+ cynara_configuration_destroy(cynara_conf);
+ if(error != CYNARA_API_SUCCESS) {
+ LogError("Can't initialize Cynara instance: " << error);
+ throw std::runtime_error("Can't initialize Cynara instance");
+ }
+
+ LogDebug("CynaraChecker: cynara_initialize() was done.");
+}
+
+CynaraChecker::~CynaraChecker()
+{
+ cynara_finish(m_CynaraInstance);
+ LogDebug("CynaraChecker: cynara_finish() was done.");
+}
+
+
+bool CynaraChecker::checkClientPrivilege(int sockfd, const std::string& privilege)
+{
+ int ret = 0;
+ char* tmp_str;
+ pid_t pid = 0;
+
+ std::unique_ptr<char, string_free_deleter> user;
+ std::unique_ptr<char, string_free_deleter> client;
+ std::unique_ptr<char, string_free_deleter> client_session;
+
+ /* Get user info */
+ tmp_str = nullptr;
+ ret = cynara_creds_socket_get_user(sockfd, USER_METHOD_DEFAULT, &tmp_str);
+ if(ret != CYNARA_API_SUCCESS) {
+ LogError("Can't get user from socket : " << ret << " - " << cynara_error_to_string(ret));
+ return false;
+ }
+ user.reset(tmp_str);
+
+ /* Get client info */
+ tmp_str = nullptr;
+ ret = cynara_creds_socket_get_client(sockfd, CLIENT_METHOD_DEFAULT, &tmp_str);
+ if(ret != CYNARA_API_SUCCESS) {
+ LogError("Can't get client from socket : " << ret << " - " << cynara_error_to_string(ret));
+ return false;
+ }
+ client.reset(tmp_str);
+
+ /* Get client PID from socket */
+ ret = cynara_creds_socket_get_pid(sockfd, &pid);
+ if(ret != CYNARA_API_SUCCESS) {
+ LogError("Can't get PID from socket : " << ret << " - " << cynara_error_to_string(ret));
+ return false;
+ }
+
+ client_session.reset(cynara_session_from_pid(pid));
+ if(!client_session) {
+ LogError("Can't get session identifier from PID");
+ return false;
+ }
+
+ LogDebug("Got new session from " << pid << " with user " << user.get() <<
+ ", client ID " << client.get() << " and session ID " << client_session.get());
+
+ ret = cynara_check(m_CynaraInstance, client.get(), client_session.get(), user.get(), privilege.c_str());
+
+ if(ret != CYNARA_API_ACCESS_ALLOWED) {
+ LogError("Application access denied for " << pid << ", privilege: " <<
+ privilege << ", error: - " << cynara_error_to_string(ret));
+ return false;
+ }
+
+ LogDebug("Access granted for " << pid << " with privilege " << privilege);
+ return true;
+}
+
+} // namespace AuthPasswd
#endif
}
-bool checkClientOnWhitelist(int sockfd, std::string whitelistPath)
-{
- struct ucred cr;
- socklen_t len = sizeof(struct ucred);
-
- // get client smack label from socket
- if (getsockopt(sockfd, SOL_SOCKET, SO_PEERCRED, &cr, &len)) {
- int err = errno;
- LogError("getsockopt() failed: " << errnoToString(err));
- return false;
- }
-
- std::string clientSmackLabel;
- std::string path("/proc/" + std::to_string(cr.pid) + "/attr/current");
- std::ifstream file(path.c_str());
- if (!file.is_open()) {
- LogError("failed to open " << path);
- return false;
- }
-
- std::getline(file, clientSmackLabel);
- file.close();
- if (clientSmackLabel.empty())
- return false;
-
- // compare with whitelist labels
- std::string line;
- std::ifstream whitelistFile(whitelistPath.c_str());
- if (!whitelistFile.is_open()) {
- LogError("failed to open " << whitelistPath);
- return false;
- }
-
- while (std::getline(whitelistFile, line)) {
- if (line.empty())
- continue;
- if (line.at(0) == COMMENT)
- continue;
- if (line.compare(clientSmackLabel) == 0) {
- whitelistFile.close();
- LogDebug("Client " << clientSmackLabel << " is on whitelist");
- return true;
- }
- }
- whitelistFile.close();
- LogError("Client " << clientSmackLabel << " is not on whitelist");
-
- return false;
-}
-
} // namespace AuthPasswd
#include <dpl/log/log.h>
#include <dpl/serialization.h>
-#include <smack-check.h>
+#include <privilege-check.h>
#include <user-check.h>
#include <password.h>
int tempHdr;
Deserialization::Deserialize(buffer, tempHdr);
PasswordHdrs hdr = static_cast<PasswordHdrs>(tempHdr);
+ auto& cynaraChecker = CynaraChecker::instance();
try { //try..catch for internal service errors, assigns error code for returning.
switch (interfaceID) {
case SOCKET_ID_CHECK:
- if (!checkClientOnWhitelist(conn.sock, CLIENT_WHITELIST))
+ if (!cynaraChecker.checkClientPrivilege(conn.sock, PLATFORM_PRIVILEGE))
retCode = AUTH_PASSWD_API_ERROR_ACCESS_DENIED;
else if (socket_get_user(conn.sock, cur_user))
retCode = AUTH_PASSWD_API_ERROR_NO_USER;
break;
case SOCKET_ID_SET:
- if (!checkClientOnWhitelist(conn.sock, CLIENT_WHITELIST))
+ if (!cynaraChecker.checkClientPrivilege(conn.sock, PLATFORM_PRIVILEGE))
retCode = AUTH_PASSWD_API_ERROR_ACCESS_DENIED;
else if (socket_get_user(conn.sock, cur_user))
retCode = AUTH_PASSWD_API_ERROR_NO_USER;
break;
case SOCKET_ID_RESET:
- if (!checkClientOnWhitelist(conn.sock, ADMIN_CLIENT_WHITELIST))
+ if (!cynaraChecker.checkClientPrivilege(conn.sock, PLATFORM_PRIVILEGE))
retCode = AUTH_PASSWD_API_ERROR_ACCESS_DENIED;
else
retCode = processResetFunctions(hdr, buffer);
break;
case SOCKET_ID_POLICY:
- if (!checkClientOnWhitelist(conn.sock, ADMIN_CLIENT_WHITELIST))
+ if (!cynaraChecker.checkClientPrivilege(conn.sock, PLATFORM_PRIVILEGE))
retCode = AUTH_PASSWD_API_ERROR_ACCESS_DENIED;
else
retCode = processPolicyFunctions(hdr, buffer);
${SERVER_PATH}/service/plugin-loader.cpp
${SERVER_PATH}/service/plugin-manager.cpp)
-PKG_CHECK_MODULES(${TARGET_TEST}_DEP REQUIRED klay)
+PKG_CHECK_MODULES(${TARGET_TEST}_DEP REQUIRED klay security-manager)
INCLUDE_DIRECTORIES(SYSTEM ${${TARGET_TEST}_DEP_INCLUDE_DIRS}
${INCLUDE_PATH}
ret = auth_passwd_disable_policy(getuid());
TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
}
+
+TESTCASE(T00207_reset_passwd_privilege)
+{
+ auto reset_passwd_function = [] () -> int {
+ return auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ };
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.settings.main",
+ AUTH_PASSWD_API_ERROR_ACCESS_DENIED, reset_passwd_function));
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.Apps",
+ AUTH_PASSWD_API_SUCCESS, reset_passwd_function));
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM, "",
+ AUTH_PASSWD_API_SUCCESS, reset_passwd_function));
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM_PRIVILEGED, "",
+ AUTH_PASSWD_API_SUCCESS, reset_passwd_function));
+}
+
+TESTCASE(T00208_set_policy_privilege)
+{
+ auto set_policy_function = [] () -> int {
+ test::ScopedPolicy sp(test::create_policy_h(), auth_passwd_free_policy);
+ policy_h *policy = sp.get();
+
+ int ret = auth_passwd_set_user(policy, getuid());
+ if (ret != AUTH_PASSWD_API_SUCCESS)
+ return ret;
+ ret = auth_passwd_set_max_attempts(policy, 10);
+ if (ret != AUTH_PASSWD_API_SUCCESS)
+ return ret;
+
+ return auth_passwd_set_policy(policy);;
+ };
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.settings.main",
+ AUTH_PASSWD_API_ERROR_ACCESS_DENIED, set_policy_function));
+
+ int ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.Apps",
+ AUTH_PASSWD_API_SUCCESS, set_policy_function));
+
+ ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM, "",
+ AUTH_PASSWD_API_SUCCESS, set_policy_function));
+
+ ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM_PRIVILEGED, "",
+ AUTH_PASSWD_API_SUCCESS, set_policy_function));
+}
ret = auth_passwd_disable_policy(getuid());
TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
}
+
+
+TESTCASE(T00120_check_passwd_privilege)
+{
+ auto check_passwd_function = [] () -> int {
+ return check_passwd(AUTH_PWD_NORMAL, default_pass);
+ };
+
+ int ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+
+ ret = auth_passwd_disable_policy(getuid());
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.settings.main",
+ AUTH_PASSWD_API_ERROR_ACCESS_DENIED, check_passwd_function));
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.Apps",
+ AUTH_PASSWD_API_SUCCESS, check_passwd_function));
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM, "",
+ AUTH_PASSWD_API_SUCCESS, check_passwd_function));
+
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM_PRIVILEGED, "",
+ AUTH_PASSWD_API_SUCCESS, check_passwd_function));
+}
+
+TESTCASE(T00121_set_passwd_privilege)
+{
+ auto set_passwd_function = [] () -> int {
+ sleep_for_retry_timeout();
+ return auth_passwd_set_passwd(AUTH_PWD_NORMAL, default_pass, NULL);
+ };
+
+ int ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.settings.main",
+ AUTH_PASSWD_API_ERROR_ACCESS_DENIED, set_passwd_function));
+
+ ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_APP, "org.tizen.Apps",
+ AUTH_PASSWD_API_SUCCESS, set_passwd_function));
+
+ ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM, "",
+ AUTH_PASSWD_API_SUCCESS, set_passwd_function));
+
+ ret = auth_passwd_reset_passwd(AUTH_PWD_NORMAL, getuid(), default_pass);
+ TEST_EXPECT(AUTH_PASSWD_API_SUCCESS, ret);
+ TEST_EXPECT(true, test::run_in_process(SM_PROCESS_TYPE_SYSTEM_PRIVILEGED, "",
+ AUTH_PASSWD_API_SUCCESS, set_passwd_function));
+}
#include <auth-passwd-admin.h>
#include <memory>
+#include <string>
+#include <iostream>
+
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <app-runtime.h>
namespace test {
using ScopedPolicy = std::unique_ptr<policy_h, decltype(&auth_passwd_free_policy)>;
+template<typename Functor>
+bool run_in_process(process_type ptype, const std::string& appId, int expected, Functor functor)
+{
+ pid_t p;
+ int stat;
+
+ /* split this program into two processes */
+ p = fork();
+
+ if( p == 0 ) { // child process is running
+ int ret = security_manager_set_identity(ptype, appId.c_str());
+
+ if(ret != SECURITY_MANAGER_SUCCESS) {
+ std::cout << "security_manager_set_identity() failed... ret=" << ret
+ << ", appId=" << appId << std::endl;
+ exit(2); // error case
+ }
+ ret = functor();
+ if(ret != expected) {
+ std::cout << "The test in separated process failed..."
+ << "ptype=" << ptype << ", appId=" << appId
+ << ", expected=" << expected << ", actual=" << ret
+ << std::endl;
+ exit(2); // error case
+ }
+ exit(0);
+ } else { // parent process is runnin
+ wait(&stat); // wait for the child
+ return WIFEXITED(stat) & (WEXITSTATUS(stat) == 0); // check result from child
+ }
+}
+
} // namespace test