1 /*
2 * Copyright (c) 2023 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 "module_file.h"
17
18 #include <dlfcn.h>
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 #include <unordered_set>
23
24 #include "directory_ex.h"
25 #include "module_utils.h"
26 #include "json_node.h"
27 #include "log/log.h"
28 #include "module_constants.h"
29 #include "module_zip_helper.h"
30 #include "package/package.h"
31 #include "scope_guard.h"
32 #include "securec.h"
33 #include "string_ex.h"
34 #include "unique_fd.h"
35 #include "utils.h"
36
37 #ifdef SUPPORT_HVB
38 #include "hvb.h"
39 #include "hvb_footer.h"
40 #include "module_hvb_ops.h"
41 #endif
42
43 namespace OHOS {
44 namespace SysInstaller {
45 using namespace Updater;
46 using std::string;
47
48 namespace {
49 constexpr const char *VERSION_DELIMITER = ".";
50 constexpr size_t API_VERSION_INDEX = 0;
51 constexpr size_t VERSION_CODE_INDEX = 1;
52 constexpr size_t PATCH_VERSION_INDEX = 2;
53 constexpr size_t VERSION_VECTOR_SIZE = 3;
54 constexpr size_t PACKINFO_VERSION_VECTOR_SIZE = 5;
55
56 struct FsMagic {
57 const char *type;
58 int32_t offset;
59 int16_t len;
60 const char *magic;
61 };
62 constexpr const FsMagic FS_TYPES[] = {{"f2fs", 1024, 4, "\x10\x20\xF5\xF2"},
63 {"ext4", 1080, 2, "\x53\xEF"}};
64
RetrieveFsType(int fd,uint32_t imageOffset)65 const char *RetrieveFsType(int fd, uint32_t imageOffset)
66 {
67 for (const auto &fs : FS_TYPES) {
68 uint8_t buf[fs.len];
69 if (!ReadFullyAtOffset(fd, buf, fs.len, imageOffset + fs.offset)) {
70 LOG(ERROR) << "Couldn't read filesystem magic";
71 return nullptr;
72 }
73 if (memcmp(buf, fs.magic, fs.len) == 0) {
74 return fs.type;
75 }
76 }
77 LOG(ERROR) << "Couldn't find filesystem magic";
78 return nullptr;
79 }
80
ParseImageStat(const string & path,ImageStat & imageStat)81 bool ParseImageStat(const string &path, ImageStat &imageStat)
82 {
83 string realPath = GetRealPath(path);
84 if (realPath.empty() || !Utils::IsFileExist(realPath)) {
85 LOG(ERROR) << "Invalid path " << path;
86 return false;
87 }
88 struct stat buffer;
89 if (stat(realPath.c_str(), &buffer) != 0) {
90 LOG(ERROR) << "stat file " << path << " failed.";
91 return false;
92 }
93 imageStat.imageOffset = 0;
94 imageStat.imageSize = static_cast<uint32_t>(buffer.st_size);
95
96 UniqueFd fd(open(realPath.c_str(), O_RDONLY | O_CLOEXEC));
97 if (fd.Get() == -1) {
98 LOG(ERROR) << "Failed to open package " << path << ": I/O error";
99 return false;
100 }
101 const char *fsTypePtr = RetrieveFsType(fd.Get(), imageStat.imageOffset);
102 if (fsTypePtr == nullptr) {
103 LOG(ERROR) << "Failed to get fs type " << path;
104 return false;
105 }
106 errno_t ret = strcpy_s(imageStat.fsType, FS_TYPE_MAX_SIZE, fsTypePtr);
107 if (ret != EOK) {
108 LOG(ERROR) << "Failed to copy fs type " << fsTypePtr;
109 return false;
110 }
111 return true;
112 }
113
ParseHmpVersionInfo(const JsonNode & package,ModulePackageInfo & versionInfo)114 bool ParseHmpVersionInfo(const JsonNode &package, ModulePackageInfo &versionInfo)
115 {
116 std::optional<string> name = package["name"].As<string>();
117 if (!name.has_value()) {
118 LOG(ERROR) << "Hmpinfo: Failed to get name val";
119 return false;
120 }
121 std::optional<string> version = package["version"].As<string>();
122 if (!version.has_value()) {
123 LOG(ERROR) << "Hmpinfo: Failed to get version val";
124 return false;
125 }
126 std::optional<string> hotApply = package["hotApply"].As<string>();
127 if (!hotApply.has_value()) {
128 LOG(ERROR) << "Hmpinfo: Failed to get hotApply val";
129 return false;
130 }
131 versionInfo.hmpName = name.value();
132 versionInfo.version = version.value();
133 versionInfo.hotApply = Utils::String2Int<int>(hotApply.value(), Utils::N_DEC);
134
135 if (versionInfo.type == HMP_APP_TYPE || versionInfo.type == HMP_MIX_TYPE) {
136 std::optional<string> apiVersion = package[HMP_API_VERSION].As<string>();
137 if (!apiVersion.has_value()) {
138 LOG(ERROR) << "Hmpinfo: Failed to get apiVersion val";
139 return false;
140 }
141 versionInfo.apiVersion = Utils::String2Int<int>(apiVersion.value(), Utils::N_DEC);
142 } else if (versionInfo.type == HMP_SA_TYPE ||
143 versionInfo.type == HMP_SA_TYPE_OLD ||
144 versionInfo.type == HMP_MIX_TYPE) {
145 std::optional<string> saSdkVersion = package[HMP_SA_SDK_VERSION].As<string>();
146 if (!saSdkVersion.has_value()) {
147 LOG(ERROR) << "Hmpinfo: Failed to get saSdkVersion val";
148 return false;
149 }
150 } else {
151 LOG(ERROR) << "Hmpinfo: Invalid type: " << versionInfo.type;
152 return false;
153 }
154 return true;
155 }
156
ParseSaVersion(const string & versionStr,SaInfo & info)157 bool ParseSaVersion(const string &versionStr, SaInfo &info)
158 {
159 std::vector<string> versionVec;
160 SplitStr(versionStr, VERSION_DELIMITER, versionVec);
161 if (versionVec.size() != VERSION_VECTOR_SIZE) {
162 LOG(ERROR) << "SaVersion: Invalid version: " << versionStr;
163 return false;
164 }
165 info.version.apiVersion = static_cast<uint32_t>(std::stoi(versionVec.at(API_VERSION_INDEX)));
166 info.version.versionCode = static_cast<uint32_t>(std::stoi(versionVec.at(VERSION_CODE_INDEX)));
167 info.version.patchVersion = static_cast<uint32_t>(std::stoi(versionVec.at(PATCH_VERSION_INDEX)));
168 return true;
169 }
170
ParseSaList(const JsonNode & package,ModulePackageInfo & versionInfo)171 bool ParseSaList(const JsonNode &package, ModulePackageInfo &versionInfo)
172 {
173 const auto &saListJson = package["saList"];
174 for (const auto &saInfo : saListJson) {
175 std::optional<string> saInfoStr = saInfo.get().As<string>();
176 if (!saInfoStr.has_value()) {
177 LOG(ERROR) << "SaList: Failed to get saInfoStr val";
178 return false;
179 }
180 std::vector<string> saInfoVec;
181 SplitStr(saInfoStr.value(), " ", saInfoVec);
182 if (saInfoVec.size() != 3) { // 3: name id version
183 LOG(ERROR) << "SaList: Invalid saInfoStr: " << saInfoStr.value();
184 return false;
185 }
186 SaInfo &infoTmp = versionInfo.saInfoList.emplace_back();
187 infoTmp.saName = saInfoVec.at(0); // 0:index of name
188 infoTmp.saId = static_cast<int32_t>(std::stoi(saInfoVec.at(1))); // 1:index of saId
189 if (!ParseSaVersion(saInfoVec.at(2), infoTmp)) { // 2:index of version
190 return false;
191 }
192 }
193 return true;
194 }
195
ParseBundleList(const JsonNode & package,ModulePackageInfo & versionInfo)196 bool ParseBundleList(const JsonNode &package, ModulePackageInfo &versionInfo)
197 {
198 const auto &bundleListJson = package["bundleList"];
199 for (const auto &bundleInfo : bundleListJson) {
200 const auto &bundleInfoStr = bundleInfo.get().Key();
201 if (!bundleInfoStr.has_value()) {
202 LOG(ERROR) << "BundleList: Failed to get bundleInfo val";
203 return false;
204 }
205 std::vector<string> bundleInfoVec;
206 SplitStr(bundleInfoStr.value(), " ", bundleInfoVec);
207 if (bundleInfoVec.size() != 2) { // 2: name version
208 LOG(ERROR) << "BundleList: Invalid bundleInfoStr: " << bundleInfoStr.value();
209 return false;
210 }
211 BundleInfo &infoTmp = versionInfo.bundleInfoList.emplace_back();
212 infoTmp.bundleName = bundleInfoVec.at(0); // 0:index of bundleName
213 infoTmp.bundleVersion = bundleInfoVec.at(1); // 1:index of bundleVersion
214 }
215 return true;
216 }
217
ParseModuleInfo(const string & packInfo,ModulePackageInfo & versionInfo)218 bool ParseModuleInfo(const string &packInfo, ModulePackageInfo &versionInfo)
219 {
220 JsonNode root(packInfo);
221 const JsonNode &type = root["type"];
222 std::optional<string> hmpType = type.As<string>();
223 if (!hmpType.has_value()) {
224 LOG(ERROR) << "HmpInfo: Failed to get type val";
225 return false;
226 }
227 versionInfo.type = hmpType.value();
228
229 const JsonNode &package = root["package"];
230 if (!ParseHmpVersionInfo(package, versionInfo)) {
231 return false;
232 }
233 // parse sa info
234 if (versionInfo.type == HMP_SA_TYPE || versionInfo.type == HMP_SA_TYPE_OLD ||
235 versionInfo.type == HMP_MIX_TYPE) {
236 if (!ParseSaList(package, versionInfo)) {
237 return false;
238 }
239 }
240 // parse bundle info
241 if (versionInfo.type == HMP_APP_TYPE || versionInfo.type == HMP_MIX_TYPE) {
242 if (!ParseBundleList(package, versionInfo)) {
243 return false;
244 }
245 }
246 return true;
247 }
248
249 // avp a= v>= p>=
CompareSaVersion(const SaVersion & smaller,const SaVersion & bigger)250 bool CompareSaVersion(const SaVersion &smaller, const SaVersion &bigger)
251 {
252 if (smaller.apiVersion != bigger.apiVersion) {
253 return false;
254 }
255 if (smaller.versionCode > bigger.versionCode) {
256 return false;
257 }
258 return bigger.patchVersion >= smaller.patchVersion;
259 }
260
CompareSaListVersion(const std::list<SaInfo> & smallList,const std::list<SaInfo> & bigList)261 bool CompareSaListVersion(const std::list<SaInfo> &smallList, const std::list<SaInfo> &bigList)
262 {
263 if (smallList.size() != bigList.size()) {
264 LOG(ERROR) << "smallList size: " << smallList.size() << " not equal to big: " << bigList.size();
265 return false;
266 }
267 std::unordered_map<int32_t, SaInfo> saMap {};
268 for (const auto &info : bigList) {
269 saMap.emplace(info.saId, info);
270 }
271 for (const auto &info : smallList) {
272 auto saIter = saMap.find(info.saId);
273 if (saIter == saMap.end()) {
274 LOG(ERROR) << info.saId << "not found when compare saList";
275 return false;
276 }
277 if (!CompareSaVersion(info.version, saIter->second.version)) {
278 return false;
279 }
280 }
281 return true;
282 }
283
CompareBundleList(const std::list<BundleInfo> & smallList,const std::list<BundleInfo> & bigList)284 bool CompareBundleList(const std::list<BundleInfo> &smallList, const std::list<BundleInfo> &bigList)
285 {
286 if (smallList.size() != bigList.size()) {
287 LOG(ERROR) << "Bundle smallList size: " << smallList.size() << " not equal to big: " << bigList.size();
288 return false;
289 }
290 std::unordered_set<std::string> bundleSet {};
291 for (const auto &info : bigList) {
292 bundleSet.insert(info.bundleName);
293 }
294 for (const auto &info : smallList) {
295 auto bundleIter = bundleSet.find(info.bundleName);
296 if (bundleIter == bundleSet.end()) {
297 LOG(ERROR) << info.bundleName << " not found when compare bundleList";
298 return false;
299 }
300 }
301 return true;
302 }
303 } // namespace
304
ExtractZipFile(ModuleZipHelper & helper,const string & fileName,string & buf)305 bool ExtractZipFile(ModuleZipHelper &helper, const string &fileName, string &buf)
306 {
307 if (!helper.LocateFile(fileName)) {
308 LOG(ERROR) << "Could not find " << fileName;
309 return false;
310 }
311 if (!helper.GetFileContent(buf)) {
312 LOG(ERROR) << "Failed to get content of " << fileName;
313 return false;
314 }
315 return true;
316 }
317
318 // MSFB M= S= F= B>
CompareHmpVersion(const std::vector<string> & smallVersion,const std::vector<string> & bigVersion)319 bool CompareHmpVersion(const std::vector<string> &smallVersion, const std::vector<string> &bigVersion)
320 {
321 if (smallVersion.size() != PACKINFO_VERSION_VECTOR_SIZE || bigVersion.size() != PACKINFO_VERSION_VECTOR_SIZE) {
322 LOG(ERROR) << "invalid smallVersion " << smallVersion.size() << " invalid bigVersion " << bigVersion.size();
323 return false;
324 }
325 if (smallVersion[0] != bigVersion[0]) {
326 LOG(ERROR) << "pre " << smallVersion[0] << " not same as pkg " << bigVersion[0];
327 return false;
328 }
329
330 if (std::stoi(smallVersion.at(1)) == std::stoi(bigVersion.at(1)) && // 1:index of M
331 std::stoi(smallVersion.at(2)) == std::stoi(bigVersion.at(2)) && // 2:index of S
332 std::stoi(smallVersion.at(3)) == std::stoi(bigVersion.at(3)) && // 3:index of F
333 std::stoi(smallVersion.at(4)) < std::stoi(bigVersion.at(4))) { // 4:index of B
334 return true;
335 }
336 return false;
337 }
338
339 // MSFB M= S= F<=
CompareSaSdkVersion(const std::vector<string> & smallVersion,const std::vector<string> & bigVersion)340 bool CompareSaSdkVersion(const std::vector<string> &smallVersion, const std::vector<string> &bigVersion)
341 {
342 if (smallVersion.size() != PACKINFO_VERSION_VECTOR_SIZE || bigVersion.size() != PACKINFO_VERSION_VECTOR_SIZE) {
343 LOG(ERROR) << "invalid smallSaSdk " << smallVersion.size() << " ;invalid bigSaSdk " << bigVersion.size();
344 return false;
345 }
346 if (smallVersion[0] != bigVersion[0]) {
347 LOG(ERROR) << "pre " << smallVersion[0] << " not same as pkg " << bigVersion[0];
348 return false;
349 }
350
351 if (std::stoi(smallVersion.at(1)) == std::stoi(bigVersion.at(1)) && // 1:index of M
352 std::stoi(smallVersion.at(2)) == std::stoi(bigVersion.at(2)) && // 2:index of S
353 std::stoi(smallVersion.at(3)) >= std::stoi(bigVersion.at(3))) { // 3:index of F
354 return true;
355 }
356 return false;
357 }
358
ParseVersion(const string & version,const string & split,std::vector<string> & versionVec)359 bool ParseVersion(const string &version, const string &split, std::vector<string> &versionVec)
360 {
361 size_t index = version.rfind(split);
362 if (index == std::string::npos) {
363 LOG(ERROR) << "ParseVersion failed " << version;
364 return false;
365 }
366 versionVec.emplace_back(version.substr(0, index));
367 std::string versionNumber = version.substr(index + split.length());
368
369 std::vector<std::string> tmpVersionVec = Utils::SplitString(versionNumber, "."); // xxx-d01_4.10.0.1
370 if (tmpVersionVec.size() != 4) { // 4: version number size
371 LOG(ERROR) << version << " is not right";
372 return false;
373 }
374 versionVec.insert(versionVec.end(), tmpVersionVec.begin(), tmpVersionVec.end());
375 return true;
376 }
377
VerifyModulePackageSign(const std::string & path)378 __attribute__((weak)) int32_t VerifyModulePackageSign(const std::string &path)
379 {
380 LOG(INFO) << "VerifyModulePackageSign " << path;
381 return VerifyPackage(path.c_str(), Utils::GetCertName().c_str(), "", nullptr, 0);
382 }
383
Open(const string & path)384 std::unique_ptr<ModuleFile> ModuleFile::Open(const string &path)
385 {
386 ModuleZipHelper helper(path);
387 if (!helper.IsValid()) {
388 LOG(ERROR) << "Failed to open file " << path;
389 return nullptr;
390 }
391
392 string moduleInfo;
393 if (!ExtractZipFile(helper, PACK_INFO_NAME, moduleInfo)) {
394 LOG(ERROR) << "Failed to extract " << PACK_INFO_NAME << " from package " << path;
395 return nullptr;
396 }
397 ModulePackageInfo versionInfo;
398 if (!ParseModuleInfo(moduleInfo, versionInfo)) {
399 LOG(ERROR) << "Failed to parse version info of package " << path;
400 return nullptr;
401 }
402
403 ImageStat tmpStat;
404 std::optional<ImageStat> imageStat;
405 string imagePath = ExtractFilePath(path) + IMG_FILE_NAME;
406 if (ParseImageStat(imagePath, tmpStat)) {
407 imageStat = std::move(tmpStat);
408 } else if (!StartsWith(imagePath, MODULE_PREINSTALL_DIR)) {
409 LOG(ERROR) << "Update package without image " << imagePath;
410 return nullptr;
411 }
412
413 return std::make_unique<ModuleFile>(path, versionInfo, imageStat);
414 }
415
CompareVersion(const ModuleFile & newFile,const ModuleFile & oldFile)416 bool ModuleFile::CompareVersion(const ModuleFile &newFile, const ModuleFile &oldFile)
417 {
418 if (newFile.GetPath() == oldFile.GetPath()) {
419 return true;
420 }
421 std::vector<string> newVersion {};
422 std::vector<string> oldVersion {};
423 // version: xxx-d01 M.S.F.B
424 if (!ParseVersion(newFile.GetVersionInfo().version, " ", newVersion) ||
425 !ParseVersion(oldFile.GetVersionInfo().version, " ", oldVersion)) {
426 LOG(ERROR) << "when compare version, parse version failed.";
427 return false;
428 }
429
430 if (!CompareHmpVersion(oldVersion, newVersion)) {
431 LOG(ERROR) << "old hmp version is higher.";
432 return false;
433 }
434 if (!CompareSaListVersion(oldFile.GetVersionInfo().saInfoList, newFile.GetVersionInfo().saInfoList)) {
435 LOG(ERROR) << "old hmp sa version is higher.";
436 return false;
437 }
438 if (!CompareBundleList(oldFile.GetVersionInfo().bundleInfoList, newFile.GetVersionInfo().bundleInfoList)) {
439 LOG(ERROR) << "new hmp bundle list do not meet expectation.";
440 return false;
441 }
442 return true;
443 }
444
VerifyModuleVerity()445 bool ModuleFile::VerifyModuleVerity()
446 {
447 #ifdef SUPPORT_HVB
448 if (vd_ != nullptr) {
449 LOG(INFO) << "already verified verity";
450 return true;
451 }
452 struct hvb_buf pubkey;
453 vd_ = hvb_init_verified_data();
454 if (vd_ == nullptr) {
455 LOG(ERROR) << "init verified data failed";
456 return false;
457 }
458 ON_SCOPE_EXIT(clear) {
459 ClearVerifiedData();
460 };
461 string imagePath = ExtractFilePath(GetPath()) + IMG_FILE_NAME;
462 enum hvb_errno ret = footer_init_desc(ModuleHvbGetOps(), imagePath.c_str(), nullptr, &pubkey, vd_);
463 if (ret != HVB_OK) {
464 LOG(ERROR) << "hvb verify failed err=" << ret;
465 return false;
466 }
467
468 if (!EnhancedVerifyModule(pubkey.addr, pubkey.size)) {
469 LOG(ERROR) << "EnhancedVerifyModule failed";
470 return false;
471 }
472 CANCEL_SCOPE_EXIT_GUARD(clear);
473 return true;
474 #else
475 LOG(INFO) << "do not support hvb";
476 return true;
477 #endif
478 }
479
EnhancedVerifyModule(const uint8_t * addr,uint64_t size)480 __attribute__((weak)) bool EnhancedVerifyModule(const uint8_t *addr, uint64_t size)
481 {
482 LOG(INFO) << "EnhancedVerifyModule success";
483 return true;
484 }
485
ClearVerifiedData()486 void ModuleFile::ClearVerifiedData()
487 {
488 #ifdef SUPPORT_HVB
489 if (vd_ != nullptr) {
490 hvb_chain_verify_data_free(vd_);
491 vd_ = nullptr;
492 }
493 #endif
494 }
495
GetHmpPackageType(void) const496 HmpInstallType ModuleFile::GetHmpPackageType(void) const
497 {
498 LOG(INFO) << "Hmp type is " << versionInfo_.type;
499 if (versionInfo_.type == HMP_APP_TYPE) {
500 return COLD_APP_TYPE;
501 }
502 if (versionInfo_.type == HMP_MIX_TYPE) {
503 return COLD_MIX_TYPE;
504 }
505 return COLD_SA_TYPE;
506 }
507 } // namespace SysInstaller
508 } // namespace OHOS