1 /*
2 * Copyright (c) 2025 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 #include <thread>
17 #include <chrono>
18 #include <cinttypes>
19
20 #include "file_util.h"
21 #include "json_util.h"
22 #include "security_guard_utils.h"
23 #include "data_collect_manager.h"
24 #include "ffrt.h"
25 #include "directory_ex.h"
26 #include "detect_plugin_manager.h"
27
28 namespace OHOS::Security::SecurityGuard {
29 namespace {
30 constexpr int32_t MAX_RETRY_INTERVAL = 60;
31 constexpr int32_t RETRY_INTERVAL_INCREMENT = 10;
32 constexpr int32_t MAX_PLUGIN_SIZE = 20;
33 constexpr const char *PLUGIN_PREFIX_PATH = "/system/lib64/";
34 }
35
getInstance()36 DetectPluginManager& DetectPluginManager::getInstance()
37 {
38 static DetectPluginManager instance;
39 return instance;
40 }
41
42 // LCOV_EXCL_START
LoadAllPlugins()43 void DetectPluginManager::LoadAllPlugins()
44 {
45 SGLOGI("Start LoadAllPlugins.");
46 const std::string fileName = "/system/etc/detect_plugin.json";
47 if (!ParsePluginConfig(fileName)) {
48 return;
49 }
50 for (const auto &plugin : plugins_) {
51 LoadPlugin(plugin);
52 }
53 if (!isFailedEventStartRetry_) {
54 isFailedEventStartRetry_ = true;
55 ffrt::submit([this] { RetrySubscriptionTask(); });
56 }
57 SGLOGI("LoadAllPlugins finished");
58 }
59
LoadPlugin(const PluginCfg & pluginCfg)60 void DetectPluginManager::LoadPlugin(const PluginCfg &pluginCfg)
61 {
62 void *handle = dlopen(pluginCfg.pluginPath.c_str(), RTLD_LAZY);
63 if (handle == nullptr) {
64 SGLOGE("Plugin open failed, pluginName: %{public}s, reason: %{public}s",
65 pluginCfg.pluginName.c_str(), dlerror());
66 return;
67 }
68 std::shared_ptr<DetectPluginAttrs> detectPluginAttrs = std::make_shared<DetectPluginAttrs>();
69 detectPluginAttrs->SetPluginName(pluginCfg.pluginName);
70 detectPluginAttrs->SetHandle(handle);
71 auto createDetectPlugin = (CreateDetectPlugin)dlsym(handle, "CreateDetectPlugin");
72 if (createDetectPlugin == nullptr) {
73 SGLOGE("createDetectPlugin func is nullptr");
74 return;
75 }
76 IDetectPlugin *instance = createDetectPlugin();
77 if (instance == nullptr) {
78 SGLOGE("IDetectPlugin instance is nullptr");
79 return;
80 }
81 detectPluginAttrs->SetInstance(instance);
82 if (!instance->Init()) {
83 SGLOGE("Plugin init failed, pluginName: %{public}s", pluginCfg.pluginName.c_str());
84 return;
85 }
86 for (const int64_t& eventId : pluginCfg.depEventIds) {
87 size_t count = 0;
88 {
89 std::lock_guard<ffrt::mutex> lock(mutex_);
90 count = eventIdMap_.count(eventId);
91 }
92 if (count == 0) {
93 SubscribeEvent(eventId);
94 }
95 std::lock_guard<ffrt::mutex> lock(mutex_);
96 eventIdMap_[eventId].emplace_back(detectPluginAttrs);
97 }
98 if (pluginCfg.depEventIds.empty()) {
99 const int64_t sepcialId = -1;
100 std::lock_guard<ffrt::mutex> lock(mutex_);
101 eventIdMap_[sepcialId].emplace_back(detectPluginAttrs);
102 }
103 SGLOGI("Load plugin success, pluginName: %{public}s", pluginCfg.pluginName.c_str());
104 }
105 // LCOV_EXCL_STOP
106
SubscribeEvent(int64_t eventId)107 void DetectPluginManager::SubscribeEvent(int64_t eventId)
108 {
109 SecurityCollector::Event sgEvent {};
110 sgEvent.eventId = eventId;
111 auto subscriber = std::make_shared<DetectPluginManagerSubscriber>(sgEvent);
112 int32_t code = SecurityGuard::DataCollectManager::GetInstance().Subscribe(subscriber);
113 if (code != SecurityGuard::SUCCESS) {
114 SGLOGE("Subscribe failed, code: %{public}d, eventId: 0x%{public}" PRIx64 "in retry list", code, eventId);
115 failedEventIdset_.insert(eventId);
116 return;
117 }
118 SGLOGI("DetectPluginManager subscribe success, eventId: 0x%{public}" PRIx64, eventId);
119 }
120
RetrySubscriptionTask()121 void DetectPluginManager::RetrySubscriptionTask()
122 {
123 int32_t retryInterval = 10;
124 while (!failedEventIdset_.empty()) {
125 auto tmpSet = failedEventIdset_;
126 for (const auto eventId : tmpSet) {
127 SGLOGI("Retry subscription event: 0x%{public}" PRIx64, eventId);
128 failedEventIdset_.erase(eventId);
129 SubscribeEvent(eventId);
130 }
131 if (failedEventIdset_.empty()) {
132 break;
133 }
134 ffrt::this_task::sleep_for(std::chrono::seconds(retryInterval));
135 retryInterval = std::min(retryInterval + RETRY_INTERVAL_INCREMENT, MAX_RETRY_INTERVAL);
136 }
137 }
138
ParsePluginConfig(const std::string & fileName)139 bool DetectPluginManager::ParsePluginConfig(const std::string &fileName)
140 {
141 std::ios::pos_type pluginCfgFileMaxSize = 1 * 1024 * 1024; // byte
142 std::string jsonStr;
143 if (!FileUtil::ReadFileToStr(fileName, pluginCfgFileMaxSize, jsonStr)) {
144 SGLOGE("Read plugin cfg file error.");
145 return false;
146 }
147 cJSON *inJson = cJSON_Parse(jsonStr.c_str());
148 if (inJson == nullptr) {
149 SGLOGE("Parse json error.");
150 return false;
151 }
152 cJSON *plugins = cJSON_GetObjectItem(inJson, "plugins");
153 if (plugins == nullptr || !cJSON_IsArray(plugins)) {
154 SGLOGE("Json Parse Error: plugins is null or not an array.");
155 cJSON_Delete(inJson);
156 inJson = nullptr;
157 return false;
158 }
159 ParsePluginConfigObjArray(plugins);
160 cJSON_Delete(inJson);
161 inJson = nullptr;
162 return true;
163 }
164
ParsePluginConfigObjArray(const cJSON * plugins)165 void DetectPluginManager::ParsePluginConfigObjArray(const cJSON *plugins)
166 {
167 int size = cJSON_GetArraySize(plugins);
168 for (int i = 0; i < size; i++) {
169 cJSON *item = cJSON_GetArrayItem(plugins, i);
170 PluginCfg pluginCfg;
171 if (!JsonUtil::GetString(item, "pluginName", pluginCfg.pluginName) ||
172 !JsonUtil::GetString(item, "version", pluginCfg.version) ||
173 !ParsePluginDepEventIds(item, pluginCfg.depEventIds)) {
174 SGLOGE("Json Parse Error: pluginName or version or depEventIds not correct.");
175 continue;
176 }
177 if (!CheckPluginNameAndSize(pluginCfg)) {
178 continue;
179 }
180 plugins_.emplace_back(pluginCfg);
181 }
182 }
183
CheckPluginNameAndSize(PluginCfg & newPlugin)184 bool DetectPluginManager::CheckPluginNameAndSize(PluginCfg &newPlugin)
185 {
186 if (plugins_.size() >= MAX_PLUGIN_SIZE) {
187 SGLOGE("The number of managed plugins exceeds the specification");
188 return false;
189 }
190
191 const std::string path = PLUGIN_PREFIX_PATH + newPlugin.pluginName;
192 if (!PathToRealPath(path, newPlugin.pluginPath) || newPlugin.pluginPath.find(PLUGIN_PREFIX_PATH) != 0) {
193 SGLOGE("Check plugin path failed, pluginName: %{public}s", newPlugin.pluginName.c_str());
194 return false;
195 }
196 auto it = std::find_if(plugins_.begin(), plugins_.end(),
197 [&](const PluginCfg &plugin) {return plugin.pluginName == newPlugin.pluginName;});
198 if (it != plugins_.end()) {
199 SGLOGE("Duplicate plugin names are not allowed.");
200 return false;
201 }
202 return true;
203 }
204
ParsePluginDepEventIds(const cJSON * plugin,std::unordered_set<int64_t> & depEventIds)205 bool DetectPluginManager::ParsePluginDepEventIds(const cJSON *plugin,
206 std::unordered_set<int64_t> &depEventIds)
207 {
208 cJSON *depEventIdsJson = cJSON_GetObjectItem(plugin, "depEventIds");
209 int size = cJSON_GetArraySize(depEventIdsJson);
210 if (depEventIdsJson == nullptr || !cJSON_IsArray(depEventIdsJson)) {
211 SGLOGE("Json Parse Error: depEventIds not correct.");
212 return false;
213 }
214 for (int i = 0; i < size; i++) {
215 cJSON *eventIdJson = cJSON_GetArrayItem(depEventIdsJson, i);
216 std::string eventId;
217 if (!JsonUtil::GetStringNoKey(eventIdJson, eventId)) {
218 SGLOGE("Json Parse Error: eventId not correct.");
219 return false;
220 }
221 int64_t tmp = 0;
222 if (!SecurityGuardUtils::StrToI64Hex(eventId, tmp)) {
223 SGLOGE("Json Parse Error: eventId not int_64.");
224 return false;
225 }
226 depEventIds.insert(tmp);
227 }
228 return true;
229 }
230
DispatchEvent(const SecurityCollector::Event & event)231 void DetectPluginManager::DispatchEvent(const SecurityCollector::Event &event)
232 {
233 SGLOGD("Start distributing events, eventId: 0x%{public}" PRIx64, event.eventId);
234 std::lock_guard<ffrt::mutex> lock(mutex_);
235 auto it = eventIdMap_.find(event.eventId);
236 if (it == eventIdMap_.end()) {
237 SGLOGE("No Plugin is available to process th event, eventId: 0x%{public}" PRIx64,
238 event.eventId);
239 return;
240 }
241 for (auto& detectPlugin : it->second) {
242 detectPlugin->GetInstance()->HandleEvent(event.eventId, event.content,
243 AssembleMetadata(event));
244 SGLOGD("Event distributed successfully, eventId: 0x%{public}" PRIx64 ", pluginName: %{public}s",
245 event.eventId, detectPlugin->GetPluginName().c_str());
246 }
247 }
248
AssembleMetadata(const SecurityCollector::Event & event)249 std::string DetectPluginManager::AssembleMetadata(const SecurityCollector::Event &event)
250 {
251 std::string metadata = "";
252 cJSON* jsonObj = cJSON_CreateObject();
253 if (jsonObj == nullptr) {
254 SGLOGE("cJSON_CreateObject nullptr");
255 return metadata;
256 }
257 if (!JsonUtil::AddString(jsonObj, "version", event.version)) {
258 SGLOGE("AddString version failed");
259 cJSON_Delete(jsonObj);
260 jsonObj = nullptr;
261 return metadata;
262 }
263 if (!JsonUtil::AddString(jsonObj, "timestamp", event.timestamp)) {
264 SGLOGE("AddString timestamp failed");
265 cJSON_Delete(jsonObj);
266 jsonObj = nullptr;
267 return metadata;
268 }
269 auto charPtr = cJSON_PrintUnformatted(jsonObj);
270 if (charPtr == nullptr) {
271 SGLOGE("cJSON_PrintUnformatted nullptr");
272 cJSON_Delete(jsonObj);
273 jsonObj = nullptr;
274 return metadata;
275 }
276 metadata = charPtr;
277 cJSON_free(charPtr);
278 charPtr= nullptr;
279 cJSON_Delete(jsonObj);
280 jsonObj = nullptr;
281 return metadata;
282 }
283 } // namespace OHOS::Security::SecurityGuard