1 /*
2  * Copyright (c) 2024 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 #include <climits>
16 #include <filesystem>
17 #include <fstream>
18 #include <string>
19 #include <sys/mount.h>
20 #include "i18n_hilog.h"
21 #include "parameter.h"
22 #include "signature_verifier.h"
23 #include "utils.h"
24 
25 
26 namespace OHOS {
27 namespace Global {
28 namespace I18n {
29 
30 const std::string CUST_GLOBAL_CARRIER_DIR = "/system/etc/tzdata_distro/";
31 const std::string VERSION_FILE = "version.txt";
32 const std::string CERT_FILE = "CERT.ENC";
33 const std::string VERIFY_FILE = "CERT.SF";
34 const std::string MANIFEST_FILE = "MANIFEST.MF";
35 const std::string SUB_TYPE = "generic";
36 const std::string CFG_PATH =
37     "/data/service/el1/public/update/param_service/install/system/etc/TIMEZONE/generic/current/";
38 const std::string LOCALE_PATH = "/system/etc/TIMEZONE/generic/current/";
39 const std::string SAFE_PATH = "/data/service/el1/public/i18n/timezone/";
40 const std::string PUBKEY_PATH = "/system/etc/LIBPHONENUMBER/generic/";
41 const std::string PUBKEY_NAME = "hota_i18n_upgrade_v1.pem";
42 const std::string COTA_PARAM_TIMEZONE_KEY = "persist.global.tz_override";
43 const int FILE_NAME_INDEX = 6;
44 
45 std::vector<std::string> g_dataFiles = {};
46 
Init()47 bool Init()
48 {
49     std::string manifestPath = CFG_PATH + MANIFEST_FILE;
50     std::unique_ptr<char[]> resolvedPath = std::make_unique<char[]>(PATH_MAX);
51     if (realpath(manifestPath.c_str(), resolvedPath.get()) == nullptr) {
52         HILOG_ERROR_I18N("MANIFEST_FILE file path isn't exists.");
53         return false;
54     }
55     std::ifstream file(resolvedPath.get());
56     if (file.is_open()) {
57         std::string line;
58         while (std::getline(file, line)) {
59             if (line.find("Name: ") == 0) {
60                 g_dataFiles.push_back(line.substr(FILE_NAME_INDEX));
61             }
62         }
63         file.close();
64         return true;
65     } else {
66         return false;
67     }
68 }
69 
Mount()70 bool Mount()
71 {
72     if (!IsDirExist(CUST_GLOBAL_CARRIER_DIR.c_str()) ||
73         !IsDirExist(SAFE_PATH.c_str())) {
74         HILOG_ERROR_I18N("Mount: CUST_GLOBAL_CARRIER_DIR or CFG_PATH not exist");
75         return false;
76     }
77 
78     std::string cotaOpkeyVersionDir = SAFE_PATH;
79     std::string custOpkeyVersionDir = CUST_GLOBAL_CARRIER_DIR;
80 
81     if (mount(cotaOpkeyVersionDir.c_str(), custOpkeyVersionDir.c_str(), nullptr, MS_BIND, nullptr) != 0) {
82         HILOG_ERROR_I18N("Mount: fail to mount: opkey(%{public}s) errno(%{public}s)",
83             cotaOpkeyVersionDir.c_str(), strerror(errno));
84         return false;
85     }
86 
87     if (mount(nullptr, custOpkeyVersionDir.c_str(), nullptr, MS_REMOUNT | MS_BIND | MS_RDONLY, nullptr) != 0) {
88         HILOG_ERROR_I18N("Mount: fail to mount read only: opkey(%{public}s) errno(%{public}s)",
89             cotaOpkeyVersionDir.c_str(), strerror(errno));
90         if (umount(custOpkeyVersionDir.c_str()) != 0) {
91             HILOG_ERROR_I18N("Error during unmount: %{public}s\n", strerror(errno));
92         }
93         return false;
94     }
95 
96     HILOG_INFO_I18N("Mount: success ro bind cotaDir to custDir: opkey(%{public}s)", cotaOpkeyVersionDir.c_str());
97     return true;
98 }
99 
ensureDirectoryExists(const std::filesystem::path & dir_path)100 void ensureDirectoryExists(const std::filesystem::path& dir_path)
101 {
102     std::error_code ec;
103     if (!std::filesystem::exists(dir_path)) {
104         HILOG_ERROR_I18N("Directory does not exist: %{public}s", dir_path.c_str());
105         std::filesystem::create_directories(dir_path, ec);
106         if (ec) {
107             HILOG_ERROR_I18N("Failed to create directory: %{public}s, Error: %{public}s", dir_path.c_str(),
108                 ec.message().c_str());
109         }
110     }
111 }
112 
clearPath(const std::filesystem::path & dir_path)113 void clearPath(const std::filesystem::path& dir_path)
114 {
115     std::error_code ec;
116     for (const auto& entry : std::filesystem::directory_iterator(dir_path)) {
117         std::filesystem::remove_all(entry.path(), ec);
118         if (ec) {
119             HILOG_ERROR_I18N("clearPath: Error removing file: %{public}s, Error: %{public}s", entry.path().c_str(),
120                 ec.message().c_str());
121         }
122     }
123 }
124 
copySingleFile(const std::filesystem::path & src_path,const std::filesystem::path & dst_path)125 bool copySingleFile(const std::filesystem::path& src_path, const std::filesystem::path& dst_path)
126 {
127     if (!FileExist(src_path.string())) {
128         HILOG_ERROR_I18N("copySingleFile: Source file does not exist: %{public}s", src_path.c_str());
129         return false;
130     }
131 
132     ensureDirectoryExists(dst_path.parent_path());
133     HILOG_INFO_I18N("copySingleFile: copy file: %{public}s,  %{public}s", src_path.c_str(),  dst_path.c_str());
134     if (!FileCopy(src_path.string(), dst_path.string())) {
135         HILOG_ERROR_I18N("copySingleFile: Failed to copy file: %{public}s", src_path.c_str());
136         return false;
137     }
138 
139     return true;
140 }
141 
CopyDataFile()142 bool CopyDataFile()
143 {
144     HILOG_INFO_I18N("TimeZone CopyDataFile start");
145     if (!IsDirExist(SAFE_PATH.c_str())) {
146         HILOG_ERROR_I18N("TimeZone CopyDataFile: SAFE_PATH does not exist");
147         return false;
148     }
149 
150     std::filesystem::path safe_dir(SAFE_PATH);
151     clearPath(safe_dir);
152 
153     bool copySuccess = true;
154     for (const auto& file : g_dataFiles) {
155         std::filesystem::path src_path = std::filesystem::path(CFG_PATH) / file;
156         std::filesystem::path dst_path = std::filesystem::path(SAFE_PATH) / file;
157         if (!copySingleFile(src_path, dst_path)) {
158             HILOG_ERROR_I18N("CopyDataFile: Copy failed for %{public}s", file.c_str());
159             copySuccess = false;
160             break;
161         }
162     }
163 
164     if (!copySuccess) {
165         HILOG_ERROR_I18N("CopyDataFile error, clearing the safe directory...");
166         clearPath(safe_dir);
167     }
168 
169     return copySuccess;
170 }
171 
CheckIfUpdateNecessary()172 bool CheckIfUpdateNecessary()
173 {
174     std::string versionUpdate = SignatureVerifier::LoadFileVersion(CFG_PATH + VERSION_FILE);
175     std::string versionLocale = SignatureVerifier::LoadFileVersion(LOCALE_PATH + VERSION_FILE);
176     HILOG_INFO_I18N("CheckIfUpdateNecessary: Verify: versionUpdate(%{public}s) versionLocale(%{public}s)",
177         versionUpdate.c_str(), versionLocale.c_str());
178 
179     if (versionLocale.length() == 0 || versionUpdate.length() == 0) {
180         return false;
181     }
182     if (SignatureVerifier::CompareVersion(versionLocale, versionUpdate) <= 0) {
183         return false;
184     }
185     return true;
186 }
187 
CheckFileIntegrity()188 bool CheckFileIntegrity()
189 {
190     std::string certPath = CFG_PATH + CERT_FILE;
191     std::string verifyPath = CFG_PATH + VERIFY_FILE;
192     std::string pubkeyPath = PUBKEY_PATH + PUBKEY_NAME;
193     std::string manifestPath = CFG_PATH + MANIFEST_FILE;
194     if (!SignatureVerifier::VerifyCertFile(certPath, verifyPath, pubkeyPath, manifestPath)) {
195         HILOG_ERROR_I18N("CheckFileIntegrity: VerifyCertFile error");
196         return false;
197     }
198 
199     for (unsigned long i = 0; i < g_dataFiles.size(); i++) {
200         HILOG_ERROR_I18N("CheckFileIntegrity: file to verify (%{public}s)", g_dataFiles[i].c_str());
201         std::string filePath = CFG_PATH + g_dataFiles[i];
202         if (!FileExist(filePath.c_str())) {
203             HILOG_ERROR_I18N("CheckFileIntegrity: file not exist (%{public}s)", g_dataFiles[i].c_str());
204             return false;
205         }
206         if (!SignatureVerifier::VerifyParamFile(g_dataFiles[i], CFG_PATH, manifestPath)) {
207             HILOG_ERROR_I18N("CheckFileIntegrity: VerifyParamFile error");
208             return false;
209         }
210     }
211 
212     return true;
213 }
214 
UpdateTimeZone()215 void UpdateTimeZone()
216 {
217     if (!Init()) {
218         SetParameter(COTA_PARAM_TIMEZONE_KEY.c_str(), "false");
219         HILOG_INFO_I18N("UpdateTimeZone: init error");
220         return;
221     }
222     if (!Mount()) {
223         SetParameter(COTA_PARAM_TIMEZONE_KEY.c_str(), "false");
224         HILOG_INFO_I18N("UpdateTimeZone: mount error");
225         return;
226     }
227     if (!CheckIfUpdateNecessary()) {
228         SetParameter(COTA_PARAM_TIMEZONE_KEY.c_str(), "false");
229         HILOG_INFO_I18N("UpdateTimeZone: CheckIfUpdateNecessary error, no need to update");
230         return;
231     }
232     if (!CheckFileIntegrity()) {
233         SetParameter(COTA_PARAM_TIMEZONE_KEY.c_str(), "false");
234         HILOG_INFO_I18N("UpdateTimeZone: CheckFileIntegrity error, no need to update");
235         return;
236     }
237     if (!CopyDataFile()) {
238         SetParameter(COTA_PARAM_TIMEZONE_KEY.c_str(), "false");
239         HILOG_INFO_I18N("UpdateTimeZone: CopyDataFile error");
240         return;
241     }
242 
243     SetParameter(COTA_PARAM_TIMEZONE_KEY.c_str(), "true");
244     HILOG_INFO_I18N("UpdateTimeZone: UpdateTimeZone");
245 }
246 
247 }
248 }
249 }
250 
main(int argc,char * argv[])251 int main(int argc, char *argv[])
252 {
253     HILOG_INFO_I18N("hmos_timezone_mount: UpdateTimeZone start");
254     OHOS::Global::I18n::UpdateTimeZone();
255     return 0;
256 }