已合并
修改controlled存储授权记录逻辑 #481
修改controlled存储授权记录逻辑 #481
已合并
LinShen创建于 7 天前
18 个文件变更+494-37
Mframeworks/secure_access_fence/definition/src/lib.rs+9-0
@@ -350,6 +350,15 @@ pub enum GrantType {
350 /// Remote grant.350 /// Remote grant.
351 RemoteGrant = 0x02,351 RemoteGrant = 0x02,
352}352}
353+ 
354+/// Remote grant status enum.
355+#[derive(Debug, Clone, Copy, PartialEq)]
356+pub enum RemoteGrantStatus {
357+ /// Enable remote grant.
358+ Enable = 1,
359+ /// Disable remote grant.
360+ Disable = 2,
361+}
353// ============================================================================362// ============================================================================
354// Basic Structures363// Basic Structures
355// ============================================================================364// ============================================================================
Mframeworks/secure_access_fence/inner_api/agent_fence/src/saf_agent_fence.cpp+34-0
@@ -251,6 +251,40 @@ int32_t SafAgentFence::VerifyControllerDevicePackage(
251 return resultCode;251 return resultCode;
252}252}
253 253 
254+int32_t SafAgentFence::GetRemoteGrantStatus(int32_t& remoteGrantStatus)
255+{
256+ LOGI("SafAgentFence::GetRemoteGrantStatus enter");
257+ 
258+ auto proxy = GetProxy(g_mutex);
259+ IF_TRUE_LOGE_RETURN_ERR(proxy == nullptr, SAF_ERR_SERVICE_UNAVAILABLE, "load sa fail.");
260+ 
261+ int32_t resultCode = SAF_SUCCESS;
262+ int32_t ret = HandleIpcError(proxy, [&]() {
263+ return proxy->GetRemoteGrantStatus(remoteGrantStatus, resultCode);
264+ });
265+ IF_ERROR_LOGE_RETURN_ERR(ret, SAF_ERR_IPC_PROXY_FAIL, "IPC call failed, ret=%{public}d", ret);
266+ 
267+ LOGI("SafAgentFence::GetRemoteGrantStatus finished, resultCode = 0x%{public}x", resultCode);
268+ return resultCode;
269+}
270+ 
271+int32_t SafAgentFence::UpdateRemoteGrantStatus(int32_t remoteGrantStatus)
272+{
273+ LOGI("SafAgentFence::UpdateRemoteGrantStatus enter, remoteGrantStatus=%{public}d", remoteGrantStatus);
274+ 
275+ auto proxy = GetProxy(g_mutex);
276+ IF_TRUE_LOGE_RETURN_ERR(proxy == nullptr, SAF_ERR_SERVICE_UNAVAILABLE, "load sa fail.");
277+ 
278+ int32_t resultCode = SAF_SUCCESS;
279+ int32_t ret = HandleIpcError(proxy, [&]() {
280+ return proxy->UpdateRemoteGrantStatus(remoteGrantStatus, resultCode);
281+ });
282+ IF_ERROR_LOGE_RETURN_ERR(ret, SAF_ERR_IPC_PROXY_FAIL, "IPC call failed, ret=%{public}d", ret);
283+ 
284+ LOGI("SafAgentFence::UpdateRemoteGrantStatus finished, resultCode = 0x%{public}x", resultCode);
285+ return resultCode;
286+}
287+ 
254int32_t SafAgentFence::VerifyTicket(288int32_t SafAgentFence::VerifyTicket(
255 int32_t osAccountId,289 int32_t osAccountId,
256 const std::string &callerId,290 const std::string &callerId,
Mframeworks/secure_access_fence/inner_api/ipc/ISecureAccessFence.idl+20-0
@@ -105,6 +105,26 @@ interface OHOS.Security.SAF.ISecureAccessFence {
105 [out] int resultCode105 [out] int resultCode
106 );106 );
107 107 
108+ /**
109+ * @brief Get remote grant status.
110+ * @param remoteGrantStatus Output remote grant status.
111+ * @param resultCode Error code output (0 for success).
112+ */
113+ [ipccode 8] void GetRemoteGrantStatus(
114+ [out] int remoteGrantStatus,
115+ [out] int resultCode
116+ );
117+ 
118+ /**
119+ * @brief Update remote grant status.
120+ * @param remoteGrantStatus Remote grant status to set.
121+ * @param resultCode Error code output (0 for success).
122+ */
123+ [ipccode 9] void UpdateRemoteGrantStatus(
124+ [in] int remoteGrantStatus,
125+ [out] int resultCode
126+ );
127+ 
108 /**128 /**
109 * @brief Batch query command permissions.129 * @brief Batch query command permissions.
110 * @param cmds List of commands to query (input parameter).130 * @param cmds List of commands to query (input parameter).
Mframeworks/secure_access_fence/inner_api/ipc/SecureAccessFenceType.idl+10-0
@@ -176,3 +176,13 @@ struct RemoteUserAuthResults {
176 /** Permission query associated with the authentication. */176 /** Permission query associated with the authentication. */
177 PermissionQuery permissionQuery;177 PermissionQuery permissionQuery;
178};178};
179+ 
180+/**
181+ * @brief Remote grant status enum.
182+ */
183+enum RemoteGrantStatus {
184+ /** Enable remote grant. */
185+ ENABLE = 1,
186+ /** Disable remote grant. */
187+ DISABLE = 2
188+};
Mframeworks/secure_access_fence/inner_api/ipc/src/lib.rs+5-1
@@ -63,7 +63,11 @@ pub const CMD_VERIFY_CONTROLLED_DEVICE_PACKAGE: u32 = 5;
63pub const CMD_GENERATE_CONTROLLER_DEVICE_PACKAGE: u32 = 6; 63pub const CMD_GENERATE_CONTROLLER_DEVICE_PACKAGE: u32 = 6;
64/// IPC code for VerifyControllerDevicePackage. 64/// IPC code for VerifyControllerDevicePackage.
65pub const CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE: u32 = 7;65pub const CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE: u32 = 7;
66- 66+/// IPC code for GetRemoteGrantStatus.
67+pub const CMD_GET_REMOTE_GRANT_STATUS: u32 = 8;
68+/// IPC code for UpdateRemoteGrantStatus.
69+pub const CMD_UPDATE_REMOTE_GRANT_STATUS: u32 = 9;
70+
67const MAX_MAP_CAPACITY: u32 = 64;71const MAX_MAP_CAPACITY: u32 = 64;
68pub(crate) const MAX_VEC_CAPACITY: u32 = 0x10000;72pub(crate) const MAX_VEC_CAPACITY: u32 = 0x10000;
69pub(crate) const MAX_TICKET_CAPACITY: u32 = 99;73pub(crate) const MAX_TICKET_CAPACITY: u32 = 99;
Mframeworks/secure_access_fence/js/napi/inc/agent_fence_napi_context.h+10-0
@@ -79,6 +79,16 @@ public:
79 RemoteInfo remoteInfo {};79 RemoteInfo remoteInfo {};
80 std::vector<bool> verifyRes {};80 std::vector<bool> verifyRes {};
81};81};
82+ 
83+class GetRemoteGrantStatusContext : public AgentFenceAsyncContext {
84+public:
85+ int32_t remoteGrantStatus {};
86+};
87+ 
88+class UpdateRemoteGrantStatusContext : public AgentFenceAsyncContext {
89+public:
90+ int32_t remoteGrantStatus {};
91+};
82} // namespace SAF92} // namespace SAF
83} // namespace Security93} // namespace Security
84} // namespace OHOS94} // namespace OHOS
Mframeworks/secure_access_fence/js/napi/src/agent_fence_napi.cpp+77-0
@@ -287,6 +287,71 @@ napi_value NapiVerifyControllerDevicePackage(const napi_env env, napi_callback_i
287 return CreateAsyncWork(env, info, std::move(asyncContext), __func__);287 return CreateAsyncWork(env, info, std::move(asyncContext), __func__);
288}288}
289 289 
290+napi_value NapiGetRemoteGrantStatus(const napi_env env, napi_callback_info info)
291+{
292+ auto asyncContext = std::unique_ptr<GetRemoteGrantStatusContext>(
293+ new (std::nothrow) GetRemoteGrantStatusContext());
294+ NAPI_THROW(env, asyncContext == nullptr, COMMON_INTERNAL_ERROR,
295+ "Failed to create GetRemoteGrantStatusContext");
296+ 
297+ asyncContext->parse = [](napi_env env, napi_callback_info info, AgentFenceAsyncContext *context)
298+ -> napi_status {
299+ return napi_ok;
300+ };
301+ 
302+ asyncContext->execute = [](napi_env env, void* data) {
303+ GetRemoteGrantStatusContext *asyncContext =
304+ static_cast<GetRemoteGrantStatusContext *>(data);
305+ asyncContext->result = SafAgentFence::GetRemoteGrantStatus(asyncContext->remoteGrantStatus);
306+ };
307+ 
308+ asyncContext->resolve = [](napi_env env, AgentFenceAsyncContext *context) -> napi_value {
309+ GetRemoteGrantStatusContext *asyncContext =
310+ static_cast<GetRemoteGrantStatusContext *>(context);
311+ napi_value jsResult = nullptr;
312+ NAPI_CALL(env, napi_create_uint32(env, static_cast<uint32_t>(asyncContext->remoteGrantStatus),
313+ &jsResult));
314+ return jsResult;
315+ };
316+ 
317+ return CreateAsyncWork(env, info, std::move(asyncContext), __func__);
318+}
319+ 
320+napi_value NapiUpdateRemoteGrantStatus(const napi_env env, napi_callback_info info)
321+{
322+ auto asyncContext = std::unique_ptr<UpdateRemoteGrantStatusContext>(
323+ new (std::nothrow) UpdateRemoteGrantStatusContext());
324+ NAPI_THROW(env, asyncContext == nullptr, COMMON_INTERNAL_ERROR,
325+ "Failed to create UpdateRemoteGrantStatusContext");
326+ 
327+ asyncContext->parse = [](napi_env env, napi_callback_info info, AgentFenceAsyncContext *context)
328+ -> napi_status {
329+ UpdateRemoteGrantStatusContext *asyncContext =
330+ static_cast<UpdateRemoteGrantStatusContext *>(context);
331+ size_t argc = 1;
332+ napi_value argv[1] = { nullptr };
333+ NAPI_CALL_RETURN_ERR(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
334+ NAPI_THROW_RETURN_ERR(env, argc < 1, GENERAL_PARAMETER_ERROR, "Invalid number of arguments");
335+ NAPI_CALL_RETURN_ERR(env, napi_get_value_uint32(env, argv[0],
336+ reinterpret_cast<uint32_t*>(&asyncContext->remoteGrantStatus)));
337+ return napi_ok;
338+ };
339+ 
340+ asyncContext->execute = [](napi_env env, void* data) {
341+ UpdateRemoteGrantStatusContext *asyncContext =
342+ static_cast<UpdateRemoteGrantStatusContext *>(data);
343+ asyncContext->result = SafAgentFence::UpdateRemoteGrantStatus(asyncContext->remoteGrantStatus);
344+ };
345+ 
346+ asyncContext->resolve = [](napi_env env, AgentFenceAsyncContext *context) -> napi_value {
347+ napi_value jsResult = nullptr;
348+ NAPI_CALL(env, napi_get_undefined(env, &jsResult));
349+ return jsResult;
350+ };
351+ 
352+ return CreateAsyncWork(env, info, std::move(asyncContext), __func__);
353+}
354+ 
290napi_value DeclareOperationType(const napi_env env)355napi_value DeclareOperationType(const napi_env env)
291{356{
292 napi_value status = nullptr;357 napi_value status = nullptr;
@@ -317,6 +382,15 @@ napi_value DeclareRole(const napi_env env)
317 return role;382 return role;
318}383}
319 384 
385+napi_value DeclareRemoteGrantStatus(const napi_env env)
386+{
387+ napi_value status = nullptr;
388+ NAPI_CALL(env, napi_create_object(env, &status));
389+ AddUint32Property(env, status, "ENABLE", static_cast<uint32_t>(RemoteGrantStatus::ENABLE));
390+ AddUint32Property(env, status, "DISABLE", static_cast<uint32_t>(RemoteGrantStatus::DISABLE));
391+ return status;
392+}
393+ 
320napi_value Register(const napi_env env, napi_value exports)394napi_value Register(const napi_env env, napi_value exports)
321{395{
322 napi_property_descriptor desc[] = {396 napi_property_descriptor desc[] = {
@@ -326,10 +400,13 @@ napi_value Register(const napi_env env, napi_value exports)
326 DECLARE_NAPI_FUNCTION("verifyControlledDevicePackage", NapiVerifyControlledDevicePackage),400 DECLARE_NAPI_FUNCTION("verifyControlledDevicePackage", NapiVerifyControlledDevicePackage),
327 DECLARE_NAPI_FUNCTION("generateControllerDevicePackage", NapiGenerateControllerDevicePackage),401 DECLARE_NAPI_FUNCTION("generateControllerDevicePackage", NapiGenerateControllerDevicePackage),
328 DECLARE_NAPI_FUNCTION("verifyControllerDevicePackage", NapiVerifyControllerDevicePackage),402 DECLARE_NAPI_FUNCTION("verifyControllerDevicePackage", NapiVerifyControllerDevicePackage),
403+ DECLARE_NAPI_FUNCTION("getRemoteGrantStatus", NapiGetRemoteGrantStatus),
404+ DECLARE_NAPI_FUNCTION("updateRemoteGrantStatus", NapiUpdateRemoteGrantStatus),
329 405 
330 DECLARE_NAPI_PROPERTY("OperationType", DeclareOperationType(env)),406 DECLARE_NAPI_PROPERTY("OperationType", DeclareOperationType(env)),
331 DECLARE_NAPI_PROPERTY("AuthStatus", DeclareAuthStatus(env)),407 DECLARE_NAPI_PROPERTY("AuthStatus", DeclareAuthStatus(env)),
332 DECLARE_NAPI_PROPERTY("Role", DeclareRole(env)),408 DECLARE_NAPI_PROPERTY("Role", DeclareRole(env)),
409+ DECLARE_NAPI_PROPERTY("RemoteGrantStatus", DeclareRemoteGrantStatus(env)),
333 };410 };
334 411 
335 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));412 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
Minterfaces/inner_kits/c/secure_access_fence/agent_fence/inc/saf_agent_fence.h+16-0
@@ -152,6 +152,22 @@ public:
152 const std::vector<RemoteAuthPackage> &ticketInfo,152 const std::vector<RemoteAuthPackage> &ticketInfo,
153 const RemoteInfo &remoteInfo,153 const RemoteInfo &remoteInfo,
154 std::vector<bool> &verifyRes);154 std::vector<bool> &verifyRes);
155+ 
156+ /**
157+ * @brief Get remote grant status.
158+ *
159+ * @param remoteGrantStatus Output remote grant status.
160+ * @return Returns 0 on success, or error code on failure.
161+ */
162+ static int32_t GetRemoteGrantStatus(int32_t& remoteGrantStatus);
163+ 
164+ /**
165+ * @brief Update remote grant status.
166+ *
167+ * @param remoteGrantStatus Remote grant status to set.
168+ * @return Returns 0 on success, or error code on failure.
169+ */
170+ static int32_t UpdateRemoteGrantStatus(int32_t remoteGrantStatus);
155};171};
156 172 
157}173}
Mservices/secure_access_fence/core_service/src/cxx/inc/permission_manager.h+2-0
@@ -107,6 +107,8 @@ private:
107 void GetValidPermissions(std::vector<std::string> &permissions, const std::vector<PermissionInfo> &permissionInfos);107 void GetValidPermissions(std::vector<std::string> &permissions, const std::vector<PermissionInfo> &permissionInfos);
108 108 
109 int32_t GetVerifyTicketInfo(const UserAuthResult &userAuthResult, VerifyTicketInfo &ticketInfo);109 int32_t GetVerifyTicketInfo(const UserAuthResult &userAuthResult, VerifyTicketInfo &ticketInfo);
110+
111+ void StoreGrantRecordIfValid(const UserAuthResult &userAuthResult, const TicketMessageInfo &ticketMessageInfo);
110};112};
111 113 
112} // namespace OHOS::Security::SAF114} // namespace OHOS::Security::SAF
Mservices/secure_access_fence/core_service/src/cxx/src/permission_manager.cpp+50-0
@@ -840,6 +840,56 @@ int32_t PermissionManager::GetVerifyTicketInfo(const UserAuthResult &userAuthRes
840 }840 }
841 IF_ERROR_LOGE_RETURN(ret,841 IF_ERROR_LOGE_RETURN(ret,
842 "GetVerifyTicketInfo :: GenerateTicketInfoWithTimeStamp failed, ret=%{public}d", ret);842 "GetVerifyTicketInfo :: GenerateTicketInfoWithTimeStamp failed, ret=%{public}d", ret);
843+
844+ StoreGrantRecordIfValid(userAuthResult, ticketMessageInfo);
845+
843 return SAF_SUCCESS;846 return SAF_SUCCESS;
844}847}
848+ 
849+void PermissionManager::StoreGrantRecordIfValid(const UserAuthResult &userAuthResult,
850+ const TicketMessageInfo &ticketMessageInfo)
851+{
852+ if (IsRemoteInfoEmpty(userAuthResult.permissionQuery.remoteInfo)) {
853+ return;
854+ }
855+
856+ std::vector<std::string> grantedPermissions;
857+ for (const auto &permInfo : userAuthResult.permissionInfo) {
858+ if (permInfo.permissionStatus == PermissionStatus::GRANTED) {
859+ grantedPermissions.push_back(permInfo.permission);
860+ }
861+ }
862+
863+ if (grantedPermissions.empty()) {
864+ return;
865+ }
866+
867+ std::string controlledDeviceName =
868+ userAuthResult.permissionQuery.remoteInfo.remoteControlParams.controlledDeviceName;
869+ std::string controllerDeviceName =
870+ userAuthResult.permissionQuery.remoteInfo.remoteControlParams.controllerDeviceName;
871+
872+ int32_t osAccountId;
873+ int32_t callerUid = IPCSkeleton::GetCallingUid();
874+ bool retFlag = GetOsAccountIdFromUid(callerUid, &osAccountId);
875+ if (!retFlag || osAccountId < MIN_OS_ACCOUNT_ID) {
876+ LOGE("invalid uid or osAccountId");
877+ return;
878+ }
879+
880+ rust::Vec<rust::String> rustPermissions;
881+ for (const auto &perm : grantedPermissions) {
882+ rustPermissions.push_back(rust::String(perm));
883+ }
884+ int32_t storeRet = OHOS::Security::SAF::cxx_store_controlled_grant_record(
885+ osAccountId,
886+ rust::String(controlledDeviceName),
887+ rust::String(controllerDeviceName),
888+ rustPermissions,
889+ static_cast<int32_t>(ticketMessageInfo.callerTokenId)
890+ );
891+ if (storeRet != SAF_SUCCESS) {
892+ LOGE("StoreGrantRecordIfValid :: cxx_store_controlled_grant_record failed, ret=%{public}d", storeRet);
893+ }
894+}
845}895}
Mservices/secure_access_fence/core_service/src/lib.rs+1-0
@@ -39,6 +39,7 @@ use crate::wrapper::{cxx_is_screen_locked, notify_error, notify_performance_metr
39 39 
40mod common_event;40mod common_event;
41mod remote_control;41mod remote_control;
42+mod remote_grant_status;
42mod stub;43mod stub;
43mod ticket_operation;44mod ticket_operation;
44mod wrapper;45mod wrapper;
Mservices/secure_access_fence/core_service/src/remote_control/cli_manager.rs+9-0
@@ -17,6 +17,7 @@
17 17 
18use saf_definition::{macros_lib, CommandInfo, ErrCode, Result};18use saf_definition::{macros_lib, CommandInfo, ErrCode, Result};
19use saf_log::loge;19use saf_log::loge;
20+use std::collections::HashSet;
20 21 
21const MAX_PERMISSION_BUF_SIZE: usize = 4096;22const MAX_PERMISSION_BUF_SIZE: usize = 4096;
22 23 
@@ -69,6 +70,8 @@ pub fn batch_query_cli_permission(
69 call_cxx_batch_query(&cxx_cmds, &mut out_buf, &mut result)?;70 call_cxx_batch_query(&cxx_cmds, &mut out_buf, &mut result)?;
70 parse_permissions_from_buffer(&out_buf, result.perm_count, permissions)?;71 parse_permissions_from_buffer(&out_buf, result.perm_count, permissions)?;
71 72 
73+ deduplicate_permissions(permissions);
74+ 
72 Ok(())75 Ok(())
73}76}
74 77 
@@ -111,6 +114,12 @@ fn call_cxx_batch_query(
111 Ok(())114 Ok(())
112}115}
113 116 
117+/// Deduplicate permissions while preserving insertion order
118+fn deduplicate_permissions(permissions: &mut Vec<String>) {
119+ let mut seen = HashSet::new();
120+ permissions.retain(|p| seen.insert(p.clone()));
121+}
122+ 
114fn parse_permissions_from_buffer(123fn parse_permissions_from_buffer(
115 out_buf: &[u8],124 out_buf: &[u8],
116 perm_count: i32,125 perm_count: i32,
Mservices/secure_access_fence/core_service/src/remote_control/controller_device.rs+0-4
@@ -227,10 +227,6 @@ fn validate_and_verify_single_controller_package(
227 }227 }
228 };228 };
229 229 
230- if verify_result {
231- store_grant_record_if_success(os_account_id, package, Role::Controlled);
232- }
233- 
234 Ok(verify_result)230 Ok(verify_result)
235}231}
236 232 
Mservices/secure_access_fence/core_service/src/remote_control/mod.rs+2-1
@@ -286,7 +286,8 @@ pub use controller_device::{
286 verify_controller_device_package,286 verify_controller_device_package,
287};287};
288 288 
289-pub use grant_record::{store_grant_record, StoreGrantRecordParams};289+pub use grant_record::{get_bundle_name_from_token,
290+ store_grant_record, StoreGrantRecordParams};
290 291 
291pub use cli_manager::batch_query_cli_permission;292pub use cli_manager::batch_query_cli_permission;
292 293 
Mservices/secure_access_fence/core_service/src/remote_control/remote_challenge_manager.rs+93-30
@@ -30,7 +30,7 @@ use saf_definition::{macros_lib, ErrCode, Result, DeviceIdHeader};
30#[cfg(not(feature = "SAFTest"))]30#[cfg(not(feature = "SAFTest"))]
31use saf_log::{loge, logi};31use saf_log::{loge, logi};
32#[cfg(not(feature = "SAFTest"))]32#[cfg(not(feature = "SAFTest"))]
33-use saf_utils::{JsonValue, get_compact_json_value};33+use saf_utils::{JsonValue, get_compact_json_value, system_time_in_millis};
34#[cfg(not(feature = "SAFTest"))]34#[cfg(not(feature = "SAFTest"))]
35use saf_common::JsonBuilder;35use saf_common::JsonBuilder;
36 36 
@@ -45,6 +45,8 @@ const CACHE_DIR_SUFFIX: &str = "secure_access_fence/agent_plugin";
45const CACHE_FILE_NAME: &str = "challenge_cache_list.txt";45const CACHE_FILE_NAME: &str = "challenge_cache_list.txt";
46#[cfg(not(feature = "SAFTest"))]46#[cfg(not(feature = "SAFTest"))]
47const FILE_MODE: u32 = 0o640;47const FILE_MODE: u32 = 0o640;
48+#[cfg(not(feature = "SAFTest"))]
49+const CHALLENGE_EXPIRATION_MILLIS: u64 = 86_400_000;
48 50 
49#[cfg(not(feature = "SAFTest"))]51#[cfg(not(feature = "SAFTest"))]
50#[derive(Debug, Clone)]52#[derive(Debug, Clone)]
@@ -103,12 +105,12 @@ fn ensure_cache_dir_exists(os_account_id: i32) -> Result<()> {
103 if !path.exists() {105 if !path.exists() {
104 fs::create_dir_all(path).map_err(|e| {106 fs::create_dir_all(path).map_err(|e| {
105 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,107 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,
106- "Failed to create cache dir {}: {}", dir_path, e)108+ "Failed to create cache dir: {}", e)
107 })?;109 })?;
108 110
109 fs::set_permissions(path, fs::Permissions::from_mode(0o750)).map_err(|e| {111 fs::set_permissions(path, fs::Permissions::from_mode(0o750)).map_err(|e| {
110 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,112 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,
111- "Failed to set dir permissions {}: {}", dir_path, e)113+ "Failed to set dir permissions: {}", e)
112 })?;114 })?;
113 }115 }
114 Ok(())116 Ok(())
@@ -140,12 +142,12 @@ pub fn cache_challenge(os_account_id: i32, challenge: &str, timestamp: u64, devi
140 .open(path)142 .open(path)
141 .map_err(|e| {143 .map_err(|e| {
142 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,144 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,
143- "Failed to open cache file {}: {}", file_path, e)145+ "Failed to open cache file: {}", e)
144 })?;146 })?;
145 147 
146 file.write_all(line.as_bytes()).map_err(|e| {148 file.write_all(line.as_bytes()).map_err(|e| {
147 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,149 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,
148- "Failed to write cache file {}: {}", file_path, e)150+ "Failed to write cache file: {}", e)
149 })?;151 })?;
150 152 
151 logi!("[challenge_cache] Cached challenge for os_account_id={}", os_account_id);153 logi!("[challenge_cache] Cached challenge for os_account_id={}", os_account_id);
@@ -159,7 +161,7 @@ pub fn verify_and_remove_challenge(
159 device_id_header: &DeviceIdHeader161 device_id_header: &DeviceIdHeader
160) -> Result<bool> {162) -> Result<bool> {
161 let _lock = CHALLENGE_CACHE_LOCK.lock().unwrap_or_else(|e| e.into_inner());163 let _lock = CHALLENGE_CACHE_LOCK.lock().unwrap_or_else(|e| e.into_inner());
162- 164+ 
163 let file_path = get_cache_file_full_path(os_account_id);165 let file_path = get_cache_file_full_path(os_account_id);
164 let path = Path::new(&file_path);166 let path = Path::new(&file_path);
165 167 
@@ -179,9 +181,9 @@ pub fn verify_and_remove_challenge(
179 rewrite_cache_file(&file_path, &remaining_lines)?;181 rewrite_cache_file(&file_path, &remaining_lines)?;
180 logi!("[challenge_cache] Verified and removed challenge for os_account_id={}", os_account_id);182 logi!("[challenge_cache] Verified and removed challenge for os_account_id={}", os_account_id);
181 } else {183 } else {
182- loge!("[challenge_cache] Challenge not found: {}", challenge);184+ loge!("[challenge_cache] Challenge not found");
183 return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected,185 return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected,
184- "Challenge not found: {}", challenge);186+ "Challenge not found");
185 }187 }
186 188
187 Ok(found)189 Ok(found)
@@ -194,9 +196,15 @@ fn find_and_remove_challenge_in_file(
194 expected_controller_id: &str,196 expected_controller_id: &str,
195 expected_controlled_id: &str,197 expected_controlled_id: &str,
196) -> Result<(bool, Vec<String>)> {198) -> Result<(bool, Vec<String>)> {
199+ let current_time_millis = system_time_in_millis().map_err(|e| {
200+ loge!("[challenge_cache] Failed to get system time: {:?}", e);
201+ macros_lib::log_and_into_saf_error!(ErrCode::GeneralError,
202+ "Failed to get system time: {:?}", e)
203+ })?;
204+ 
197 let file = File::open(file_path).map_err(|e| {205 let file = File::open(file_path).map_err(|e| {
198 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,206 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,
199- "Failed to open cache file {}: {}", file_path, e)207+ "Failed to open cache file: {}", e)
200 })?;208 })?;
201 209 
202 let reader = BufReader::new(file);210 let reader = BufReader::new(file);
@@ -213,31 +221,67 @@ fn find_and_remove_challenge_in_file(
213 continue;221 continue;
214 }222 }
215 223 
216- if let Some((cached_challenge, cached_entry_json)) = line.split_once('|') {224+ match process_line(&line, challenge, expected_controller_id, expected_controlled_id, current_time_millis) {
217- if cached_challenge == challenge {225+ Ok(ProcessResult::Matched) => found = true,
218- if let Ok(entry) = deserialize_cache_entry(cached_entry_json) {226+ Ok(ProcessResult::Keep) => remaining_lines.push(line),
219- if entry.controller_device_id == expected_controller_id227+ Ok(ProcessResult::Skip) => {}
220- && entry.controlled_device_id == expected_controlled_id {228+ Err(e) => return Err(e),
221- found = true;
222- continue;
223- } else {
224- loge!("[challenge_cache] DeviceIdHeader mismatch for challenge");
225- return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected,
226- "DeviceIdHeader mismatch for challenge");
227- }
228- } else {
229- loge!("[challenge_cache] DeviceIdHeader mismatch for challenge");
230- return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected,
231- "DeviceIdHeader mismatch for challenge");
232- }
233- }
234 }229 }
235- remaining_lines.push(line);
236 }230 }
237 231 
238 Ok((found, remaining_lines))232 Ok((found, remaining_lines))
239}233}
240 234 
235+#[cfg(not(feature = "SAFTest"))]
236+enum ProcessResult {
237+ Matched,
238+ Keep,
239+ Skip,
240+}
241+ 
242+#[cfg(not(feature = "SAFTest"))]
243+fn process_line(
244+ line: &str,
245+ challenge: &str,
246+ expected_controller_id: &str,
247+ expected_controlled_id: &str,
248+ current_time_millis: u64,
249+) -> Result<ProcessResult> {
250+ let Some((cached_challenge, cached_entry_json)) = line.split_once('|') else {
251+ loge!("[challenge_cache] Invalid line format, skipping...");
252+ return Ok(ProcessResult::Skip);
253+ };
254+ 
255+ let Ok(entry) = deserialize_cache_entry(cached_entry_json) else {
256+ loge!("[challenge_cache] Failed to parse entry, skipping...");
257+ return Ok(ProcessResult::Skip);
258+ };
259+ 
260+ let is_expired = current_time_millis > entry.timestamp
261+ && current_time_millis - entry.timestamp > CHALLENGE_EXPIRATION_MILLIS;
262+ 
263+ if cached_challenge == challenge {
264+ if is_expired {
265+ loge!("[challenge_cache] Challenge expired");
266+ return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected,
267+ "Challenge expired");
268+ }
269+ if entry.controller_device_id == expected_controller_id
270+ && entry.controlled_device_id == expected_controlled_id {
271+ return Ok(ProcessResult::Matched);
272+ }
273+ loge!("[challenge_cache] DeviceIdHeader mismatch for challenge");
274+ return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected,
275+ "DeviceIdHeader mismatch for challenge");
276+ }
277+ 
278+ if is_expired {
279+ Ok(ProcessResult::Skip)
280+ } else {
281+ Ok(ProcessResult::Keep)
282+ }
283+}
284+ 
241#[cfg(not(feature = "SAFTest"))]285#[cfg(not(feature = "SAFTest"))]
242fn rewrite_cache_file(file_path: &str, lines: &[String]) -> Result<()> {286fn rewrite_cache_file(file_path: &str, lines: &[String]) -> Result<()> {
243 let path = Path::new(file_path);287 let path = Path::new(file_path);
@@ -251,7 +295,7 @@ fn rewrite_cache_file(file_path: &str, lines: &[String]) -> Result<()> {
251 .open(tmp_path_ref)295 .open(tmp_path_ref)
252 .map_err(|e| {296 .map_err(|e| {
253 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,297 macros_lib::log_and_into_saf_error!(ErrCode::FileOperationError,
254- "Failed to open tmp cache file for writing {}: {}", file_path, e)298+ "Failed to open tmp cache file for writing: {}", e)
255 })?;299 })?;
256 300
257 for line in lines {301 for line in lines {
@@ -285,6 +329,11 @@ use lazy_static::lazy_static;
285use std::collections::HashMap;329use std::collections::HashMap;
286#[cfg(feature = "SAFTest")]330#[cfg(feature = "SAFTest")]
287use std::sync::Mutex;331use std::sync::Mutex;
332+#[cfg(feature = "SAFTest")]
333+use saf_utils::system_time_in_millis;
334+ 
335+#[cfg(feature = "SAFTest")]
336+const CHALLENGE_EXPIRATION_MILLIS: u64 = 86_400_000;
288 337 
289#[cfg(feature = "SAFTest")]338#[cfg(feature = "SAFTest")]
290lazy_static! {339lazy_static! {
@@ -307,10 +356,24 @@ pub fn verify_and_remove_challenge(
307 challenge: &str,356 challenge: &str,
308 device_id_header: &DeviceIdHeader,357 device_id_header: &DeviceIdHeader,
309) -> Result<bool> {358) -> Result<bool> {
359+ let current_time_millis = system_time_in_millis().unwrap_or(0);
310 let key = format!("{}:{}", os_account_id, challenge);360 let key = format!("{}:{}", os_account_id, challenge);
311 let mut cache = MOCK_CHALLENGE_CACHE.lock().unwrap();361 let mut cache = MOCK_CHALLENGE_CACHE.lock().unwrap();
362+ 
363+ cache.retain(|_, (timestamp, _, _)| {
364+ current_time_millis <= *timestamp || current_time_millis.saturating_sub(*timestamp) <= CHALLENGE_EXPIRATION_MILLIS
365+ });
366+ 
312 match cache.remove(&key) {367 match cache.remove(&key) {
313- Some((_, cached_controller, cached_controlled)) => {368+ Some((timestamp, cached_controller, cached_controlled)) => {
369+ let is_expired = current_time_millis > timestamp
370+ && current_time_millis - timestamp > CHALLENGE_EXPIRATION_MILLIS;
371+ 
372+ if is_expired {
373+ return macros_lib::log_throw_error!(ErrCode::ReplayAttackDetected,
374+ "Challenge expired: {}", challenge);
375+ }
376+ 
314 if cached_controller == device_id_header.controller_device_id377 if cached_controller == device_id_header.controller_device_id
315 && cached_controlled == device_id_header.controlled_device_id {378 && cached_controlled == device_id_header.controlled_device_id {
316 Ok(true)379 Ok(true)
Aservices/secure_access_fence/core_service/src/remote_grant_status.rs+42-0
@@ -0,0 +1,42 @@
1+/*
2+ * Copyright (c) 2026 Huawei Device Co., Ltd.
3+ * Licensed under the Apache License, Version 2.0 (the "License");
4+ * you may not use this file except in compliance with the License.
5+ * You may obtain a copy of the License at
6+ *
7+ * http://www.apache.org/licenses/LICENSE-2.0
8+ *
9+ * Unless required by applicable law or agreed to in writing, software
10+ * distributed under the License is distributed on an "AS IS" BASIS,
11+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ * See the License for the specific language governing permissions and
13+ * limitations under the License.
14+ */
15+ 
16+//! This module implements remote grant status management.
17+ 
18+use saf_definition::{macros_lib, ErrCode, Result, RemoteGrantStatus};
19+ 
20+/// Get remote grant status.
21+pub fn get_remote_grant_status() -> Result<i32> {
22+ macros_lib::log_throw_error!(
23+ ErrCode::PluginNotSupport,
24+ "Plugin not support for get remote grant status"
25+ )
26+}
27+ 
28+/// Update remote grant status.
29+pub fn update_remote_grant_status(status: i32) -> Result<()> {
30+ if status != RemoteGrantStatus::Enable as i32 && status != RemoteGrantStatus::Disable as i32 {
31+ return macros_lib::log_throw_error!(
32+ ErrCode::InvalidArgument,
33+ "Invalid remote grant status: {}",
34+ status
35+ );
36+ }
37+
38+ macros_lib::log_throw_error!(
39+ ErrCode::PluginNotSupport,
40+ "Plugin not support for update remote grant status"
41+ )
42+}
Mservices/secure_access_fence/core_service/src/stub.rs+58-1
@@ -30,6 +30,7 @@ use saf_ipc::{
30 CMD_BATCH_GENERATE_TICKET, CMD_BATCH_VERIFY_TICKET, CMD_VERIFY_TICKET,30 CMD_BATCH_GENERATE_TICKET, CMD_BATCH_VERIFY_TICKET, CMD_VERIFY_TICKET,
31 CMD_GENERATE_CONTROLLED_DEVICE_PACKAGE, CMD_VERIFY_CONTROLLED_DEVICE_PACKAGE,31 CMD_GENERATE_CONTROLLED_DEVICE_PACKAGE, CMD_VERIFY_CONTROLLED_DEVICE_PACKAGE,
32 CMD_GENERATE_CONTROLLER_DEVICE_PACKAGE, CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE,32 CMD_GENERATE_CONTROLLER_DEVICE_PACKAGE, CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE,
33+ CMD_GET_REMOTE_GRANT_STATUS, CMD_UPDATE_REMOTE_GRANT_STATUS,
33 IPC_SUCCESS, SA_NAME,34 IPC_SUCCESS, SA_NAME,
34};35};
35use saf_log::{loge, logi};36use saf_log::{loge, logi};
@@ -37,6 +38,7 @@ use saf_plugin::saf_plugin::SAFPlugin;
37use saf_sdk::{ErrCode, Result, SAFError};38use saf_sdk::{ErrCode, Result, SAFError};
38 39 
39use crate::remote_control;40use crate::remote_control;
41+use crate::remote_grant_status;
40use crate::wrapper;42use crate::wrapper;
41use crate::SAFService;43use crate::SAFService;
42 44 
@@ -114,7 +116,13 @@ fn on_remote_request(stub: &SAFService, code: u32, data: &mut MsgParcel, reply:
114 }, 116 },
115 CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE => { 117 CMD_VERIFY_CONTROLLER_DEVICE_PACKAGE => {
116 handle_verify_controller_device_package(data, reply) 118 handle_verify_controller_device_package(data, reply)
117- }, 119+ },
120+ CMD_GET_REMOTE_GRANT_STATUS => {
121+ handle_get_remote_grant_status(data, reply)
122+ }
123+ CMD_UPDATE_REMOTE_GRANT_STATUS => {
124+ handle_update_remote_grant_status(data, reply)
125+ }
118 _ => {126 _ => {
119 if code >= C_REDIRECT_START_CODE {127 if code >= C_REDIRECT_START_CODE {
120 let res = wrapper::on_remote_request(code, data, reply);128 let res = wrapper::on_remote_request(code, data, reply);
@@ -378,6 +386,55 @@ fn handle_verify_controller_device_package(
378 Ok(())386 Ok(())
379}387}
380 388 
389+fn handle_get_remote_grant_status(_data: &mut MsgParcel, reply: &mut MsgParcel) -> IpcResult<()> {
390+ logi!("GetRemoteGrantStatus received");
391+
392+ let result = remote_grant_status::get_remote_grant_status();
393+
394+ reply.write::<i32>(&(IPC_SUCCESS as i32))?;
395+
396+ match result {
397+ Ok(status) => {
398+ reply.write::<i32>(&status)?;
399+ reply.write::<i32>(&0)?;
400+ logi!("GetRemoteGrantStatus success, status={}", status);
401+ },
402+ Err(e) => {
403+ reply.write::<i32>(&0)?;
404+ reply.write::<i32>(&(e.code as i32))?;
405+ loge!("GetRemoteGrantStatus failed: {}", e.msg);
406+ },
407+ }
408+
409+ Ok(())
410+}
411+ 
412+fn handle_update_remote_grant_status(data: &mut MsgParcel, reply: &mut MsgParcel) -> IpcResult<()> {
413+ let status = data.read::<i32>().map_err(|e| {
414+ loge!("[FATAL]Read status failed: {:?}", e);
415+ IpcStatusCode::Failed
416+ })?;
417+
418+ logi!("UpdateRemoteGrantStatus received, status={}", status);
419+
420+ let result = remote_grant_status::update_remote_grant_status(status);
421+
422+ reply.write::<i32>(&(IPC_SUCCESS as i32))?;
423+
424+ match result {
425+ Ok(()) => {
426+ reply.write::<i32>(&0)?;
427+ logi!("UpdateRemoteGrantStatus success");
428+ },
429+ Err(e) => {
430+ reply.write::<i32>(&(e.code as i32))?;
431+ loge!("UpdateRemoteGrantStatus failed: {}", e.msg);
432+ },
433+ }
434+
435+ Ok(())
436+}
437+ 
381fn on_extension_request(_stub: &SAFService, code: u32, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 {438fn on_extension_request(_stub: &SAFService, code: u32, data: &mut MsgParcel, reply: &mut MsgParcel) -> i32 {
382 if let Ok(load) = SAFPlugin::get_instance().load_plugin() {439 if let Ok(load) = SAFPlugin::get_instance().load_plugin() {
383 match load.on_remote_request(code, data, reply) {440 match load.on_remote_request(code, data, reply) {
Mservices/secure_access_fence/core_service/src/wrapper.rs+56-0
@@ -50,6 +50,13 @@ pub mod ffi {
50 fn get_policy_auth_status(permissions: &Vec<String>, auth_statuses: &mut Vec<i32>) -> i32;50 fn get_policy_auth_status(permissions: &Vec<String>, auth_statuses: &mut Vec<i32>) -> i32;
51 fn verify_remote_ticket(domain_id: String, remote_control_ticket: String, os_account_id: i32) -> i32;51 fn verify_remote_ticket(domain_id: String, remote_control_ticket: String, os_account_id: i32) -> i32;
52 fn cxx_store_challenge(caller_token_id: &str, challenge: &str, expire_time_ms: u64) -> i32;52 fn cxx_store_challenge(caller_token_id: &str, challenge: &str, expire_time_ms: u64) -> i32;
53+ fn cxx_store_controlled_grant_record(
54+ os_account_id: i32,
55+ controlled_device_name: String,
56+ controller_device_name: String,
57+ permission_names: Vec<String>,
58+ caller_token_id: i32,
59+ ) -> i32;
53 }60 }
54 61 
55 // Rust callable C++ functions62 // Rust callable C++ functions
@@ -200,6 +207,55 @@ pub fn cxx_store_challenge(caller_token_id: &str, challenge: &str, expire_time_m
200 }207 }
201}208}
202 209 
210+/// C++ -> Rust bridge for store_controlled_grant_record.
211+pub fn cxx_store_controlled_grant_record(
212+ os_account_id: i32,
213+ controlled_device_name: String,
214+ controller_device_name: String,
215+ permission_names: Vec<String>,
216+ caller_token_id: i32,
217+) -> i32 {
218+ logi!("[Wrapper cxx_store_controlled_grant_record] os_account_id={}, caller_token_id={}, permission_count={}",
219+ os_account_id, caller_token_id, permission_names.len());
220+
221+ let calling_bundle_name = match crate::remote_control::get_bundle_name_from_token(caller_token_id) {
222+ Ok(name) => name,
223+ Err(e) => {
224+ loge!("[cxx_store_controlled_grant_record] Failed to get bundle name: {:?}", e);
225+ return e.code as i32;
226+ }
227+ };
228+
229+ let params = crate::remote_control::StoreGrantRecordParams {
230+ os_account_id,
231+ controlled_device_name,
232+ controller_device_name,
233+ is_self_grant: false,
234+ permission_names,
235+ device_role: saf_definition::Role::Controlled,
236+ calling_bundle_name,
237+ grant_type: saf_definition::GrantType::RemoteGrant,
238+ timestamp: match saf_utils::system_time_in_millis() {
239+ Ok(t) => t,
240+ Err(e) => {
241+ loge!("[cxx_store_controlled_grant_record] Failed to get timestamp: {:?}", e);
242+ return e.code as i32;
243+ }
244+ },
245+ };
246+
247+ match crate::remote_control::store_grant_record(params) {
248+ Ok(()) => {
249+ logi!("[cxx_store_controlled_grant_record] success");
250+ 0
251+ },
252+ Err(e) => {
253+ loge!("[cxx_store_controlled_grant_record] Failed to store grant record: {:?}", e);
254+ e.code as i32
255+ }
256+ }
257+}
258+ 
203/// Get Device UDID via plugin process_event259/// Get Device UDID via plugin process_event
204pub fn get_device_udid(os_account_id: i32) -> saf_definition::Result<String> {260pub fn get_device_udid(os_account_id: i32) -> saf_definition::Result<String> {
205 if os_account_id < 0 {261 if os_account_id < 0 {