1 /*
2 * Copyright (c) 2023-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
16 #include "code_sign_utils.h"
17 #include <fstream>
18 #include <string>
19 #include <asm/unistd.h>
20 #include <cstdlib>
21 #include <cstdint>
22 #include <cstdio>
23 #include <iostream>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <fcntl.h>
27 #include <linux/fs.h>
28 #include <linux/fsverity.h>
29 #include <linux/stat.h>
30 #include <linux/types.h>
31
32 #include "cs_hisysevent.h"
33 #include "cs_hitrace.h"
34 #include "code_sign_helper.h"
35 #include "constants.h"
36 #include "directory_ex.h"
37 #include "extractor.h"
38 #include "file_helper.h"
39 #include "log.h"
40 #include "stat_utils.h"
41 #include "signer_info.h"
42 #include "rust_interface.h"
43 #include "data_size_report_adapter.h"
44 #include "elf_code_sign_block_v1.h"
45 #ifdef SUPPORT_BINARY_ENABLE
46 #include "elf_code_sign_block.h"
47 #endif
48
49 namespace OHOS {
50 namespace Security {
51 namespace CodeSign {
52 constexpr uint32_t DEFAULT_HASH_ALGORITHEM = FS_VERITY_HASH_ALG_SHA256;
53 constexpr uint32_t HASH_PAGE_SIZE = 4096;
54
55 #define NOT_SATISFIED_RETURN(CONDITION, ERROR_CODE, LOG_MESSAGE, ...) do { \
56 if (!(CONDITION)) { \
57 LOG_ERROR(LOG_MESSAGE, ##__VA_ARGS__); \
58 return (ERROR_CODE); \
59 } \
60 } while (0)
61
EnforceCodeSignForApp(const EntryMap & entryPath,const std::string & signatureFile)62 int32_t CodeSignUtils::EnforceCodeSignForApp(const EntryMap &entryPath,
63 const std::string &signatureFile)
64 {
65 LOG_INFO("Start to enforce");
66 // no files to enable, return directly
67 if (entryPath.empty()) {
68 return CS_SUCCESS;
69 }
70
71 NOT_SATISFIED_RETURN(CheckFilePathValid(signatureFile, Constants::ENABLE_SIGNATURE_FILE_BASE_PATH),
72 CS_ERR_FILE_PATH, "Signature file is invalid.");
73
74 // check whether fs-verity is supported by kernel
75 auto iter = entryPath.begin();
76 int32_t ret = CodeSignUtils::IsSupportFsVerity(iter->second);
77 if (ret != CS_SUCCESS) {
78 return ret;
79 }
80
81 std::unique_ptr<AbilityBase::Extractor> extractor = std::make_unique<AbilityBase::Extractor>(signatureFile);
82 std::vector<std::string> signatureFileList;
83 NOT_SATISFIED_RETURN(extractor->Init(), CS_ERR_EXTRACT_FILES, "Init extractor failed.");
84 // Get signature file entry name
85 extractor->GetSpecifiedTypeFiles(signatureFileList, Constants::FSV_SIG_SUFFIX);
86
87 for (const auto &pathPair: entryPath) {
88 const std::string &entryName = pathPair.first;
89 const std::string &targetFile = pathPair.second;
90 LOG_DEBUG("Enable entry %{public}s, path = %{public}s", entryName.c_str(), targetFile.c_str());
91 NOT_SATISFIED_RETURN(CheckFilePathValid(targetFile, Constants::ENABLE_APP_BASE_PATH),
92 CS_ERR_FILE_PATH, "App file is invalid.");
93
94 const std::string &signatureEntry = entryName + Constants::FSV_SIG_SUFFIX;
95 NOT_SATISFIED_RETURN(std::find(signatureFileList.begin(), signatureFileList.end(), signatureEntry) !=
96 signatureFileList.end(),
97 CS_ERR_NO_SIGNATURE, "Fail to find signature for %{public}s", entryName.c_str());
98
99 std::unique_ptr<uint8_t[]> signatureBuffer = nullptr;
100 size_t signatureSize;
101 NOT_SATISFIED_RETURN(extractor->ExtractToBufByName(signatureEntry, signatureBuffer, signatureSize),
102 CS_ERR_EXTRACT_FILES, "Extract signature failed.");
103
104 NOT_SATISFIED_RETURN(signatureSize < UINT32_MAX, CS_ERR_INVALID_SIGNATURE, "Signature is too long.");
105
106 ret = EnforceCodeSignForFile(targetFile, signatureBuffer.get(), static_cast<const uint32_t>(signatureSize));
107 if (ret != CS_SUCCESS) {
108 return ret;
109 }
110 }
111 LOG_INFO("Enforcing app complete");
112 return CS_SUCCESS;
113 }
114
IsSupportFsVerity(const std::string & path)115 int32_t CodeSignUtils::IsSupportFsVerity(const std::string &path)
116 {
117 struct statx stat = {};
118 if (Statx(AT_FDCWD, path.c_str(), 0, STATX_ALL, &stat) != 0) {
119 LOG_ERROR("Get attributes failed, errno = <%{public}d, %{public}s>",
120 errno, strerror(errno));
121 return CS_ERR_FILE_INVALID;
122 }
123 if (stat.stx_attributes_mask & STATX_ATTR_VERITY) {
124 return CS_SUCCESS;
125 }
126 LOG_INFO("Fs-verity is not supported.");
127 return CS_ERR_FSVREITY_NOT_SUPPORTED;
128 }
129
EnforceCodeSignForFile(const std::string & path,const ByteBuffer & signature)130 int32_t CodeSignUtils::EnforceCodeSignForFile(const std::string &path, const ByteBuffer &signature)
131 {
132 return EnforceCodeSignForFile(path, signature.GetBuffer(), signature.GetSize());
133 }
134
EnableCodeSignForFile(const std::string & path,const struct code_sign_enable_arg & arg)135 int32_t CodeSignUtils::EnableCodeSignForFile(const std::string &path, const struct code_sign_enable_arg &arg)
136 {
137 int32_t ret;
138 int32_t error;
139 int32_t fd = open(path.c_str(), O_RDONLY);
140 if (fd < 0) {
141 LOG_ERROR("Open file failed, path = %{public}s, errno = <%{public}d, %{public}s>",
142 path.c_str(), errno, strerror(errno));
143 return CS_ERR_FILE_OPEN;
144 }
145
146 do {
147 ret = CodeSignEnableMultiTask::IsFsVerityEnabled(fd);
148 if (ret == CS_SUCCESS) {
149 LOG_INFO("Fs-verity has been enabled.");
150 break;
151 } else if (ret == CS_ERR_FILE_INVALID) {
152 break;
153 }
154
155 StartTrace(HITRACE_TAG_ACCESS_CONTROL, CODE_SIGN_ENABLE_START);
156 if (!arg.cs_version) {
157 error = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
158 } else {
159 error = ioctl(fd, FS_IOC_ENABLE_CODE_SIGN, &arg);
160 }
161 FinishTrace(HITRACE_TAG_ACCESS_CONTROL);
162 if (error < 0) {
163 LOG_ERROR("Enable fs-verity failed, errno = <%{public}d, %{public}s>",
164 errno, strerror(errno));
165 ReportEnableError(path, errno);
166 ret = CS_ERR_ENABLE;
167 break;
168 }
169 ret = CS_SUCCESS;
170 } while (0);
171 close(fd);
172 LOG_INFO("Enforcing file complete, path = %{public}s, ret = %{public}d", path.c_str(), ret);
173 return ret;
174 }
175
ParseOwnerIdFromSignature(const ByteBuffer & sigbuffer,std::string & ownerID)176 int CodeSignUtils::ParseOwnerIdFromSignature(const ByteBuffer &sigbuffer, std::string &ownerID)
177 {
178 return SignerInfo::ParseOwnerIdFromSignature(sigbuffer, ownerID);
179 }
180
EnforceCodeSignForFile(const std::string & path,const uint8_t * signature,const uint32_t size)181 int32_t CodeSignUtils::EnforceCodeSignForFile(const std::string &path, const uint8_t *signature,
182 const uint32_t size)
183 {
184 std::string realPath;
185
186 if (signature == nullptr || size == 0) {
187 return CS_ERR_NO_SIGNATURE;
188 }
189 if (!OHOS::PathToRealPath(path, realPath)) {
190 return CS_ERR_FILE_PATH;
191 }
192
193 struct code_sign_enable_arg arg = {0};
194 arg.version = 1; // version of fs-verity, must be 1
195 arg.hash_algorithm = DEFAULT_HASH_ALGORITHEM;
196 arg.block_size = HASH_PAGE_SIZE;
197 arg.sig_size = size;
198 arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
199 return EnableCodeSignForFile(realPath, arg);
200 }
201
EnforceCodeSignForAppWithOwnerId(const std::string & ownerId,const std::string & path,const EntryMap & entryPathMap,FileType type,uint32_t flag)202 int32_t CodeSignUtils::EnforceCodeSignForAppWithOwnerId(const std::string &ownerId, const std::string &path,
203 const EntryMap &entryPathMap, FileType type, uint32_t flag)
204 {
205 return EnforceCodeSignForAppWithPluginId(ownerId, "", path, entryPathMap, type, flag);
206 }
207
EnforceCodeSignForAppWithPluginId(const std::string & ownerId,const std::string & pluginId,const std::string & path,const EntryMap & entryPathMap,FileType type,uint32_t flag)208 int32_t CodeSignUtils::EnforceCodeSignForAppWithPluginId(const std::string &ownerId, const std::string &pluginId,
209 const std::string &path, const EntryMap &entryPathMap, FileType type, uint32_t flag)
210 {
211 LOG_INFO("Start to enforce codesign FileType:%{public}u, entryPathMap size:%{public}zu, path = %{public}s, "
212 "flag = %{public}u", type, entryPathMap.size(), path.c_str(), flag);
213 if (type == FILE_ENTRY_ADD || type == FILE_ENTRY_ONLY || type == FILE_ALL) {
214 {
215 std::lock_guard<std::mutex> lock(storedEntryMapLock_);
216 storedEntryMap_.insert(entryPathMap.begin(), entryPathMap.end());
217 }
218 if (type == FILE_ENTRY_ADD) {
219 LOG_DEBUG("Add entryPathMap complete");
220 return CS_SUCCESS;
221 }
222 } else if (type >= FILE_TYPE_MAX) {
223 return CS_ERR_PARAM_INVALID;
224 }
225 std::lock_guard<std::mutex> lock(storedEntryMapLock_);
226 int ret = ProcessCodeSignBlock(ownerId, pluginId, path, type, flag);
227 if (ret != CS_SUCCESS) {
228 // retry once to make sure stability
229 ret = ProcessCodeSignBlock(ownerId, pluginId, path, type, flag);
230 }
231 storedEntryMap_.clear();
232 LOG_INFO("Enforcing done, ret = %{public}d", ret);
233 return ret;
234 }
235
ProcessCodeSignBlock(const std::string & ownerId,const std::string & pluginId,const std::string & path,FileType type,uint32_t flag)236 int32_t CodeSignUtils::ProcessCodeSignBlock(const std::string &ownerId, const std::string &pluginId,
237 const std::string &path, FileType type, uint32_t flag)
238 {
239 std::string realPath;
240 if (!OHOS::PathToRealPath(path, realPath)) {
241 return CS_ERR_FILE_PATH;
242 }
243 int32_t ret;
244 CodeSignHelper codeSignHelper;
245 ret = codeSignHelper.ParseCodeSignBlock(realPath, storedEntryMap_, type);
246 if (ret != CS_SUCCESS) {
247 return HandleCodeSignBlockFailure(realPath, ret);
248 }
249 ret = codeSignHelper.ProcessMultiTask(ownerId, pluginId, path, EnableCodeSignForFile, flag);
250 return ret;
251 }
252
HandleCodeSignBlockFailure(const std::string & realPath,int32_t ret)253 int32_t CodeSignUtils::HandleCodeSignBlockFailure(const std::string &realPath, int32_t ret)
254 {
255 if ((ret == CS_CODE_SIGN_NOT_EXISTS) && InPermissiveMode()) {
256 LOG_DEBUG("Code sign not exists");
257 return CS_SUCCESS;
258 }
259 ReportParseCodeSig(realPath, ret);
260 return ret;
261 }
262
EnforceCodeSignForApp(const std::string & path,const EntryMap & entryPathMap,FileType type,uint32_t flag)263 int32_t CodeSignUtils::EnforceCodeSignForApp(const std::string &path, const EntryMap &entryPathMap,
264 FileType type, uint32_t flag)
265 {
266 return EnforceCodeSignForAppWithOwnerId("", path, entryPathMap, type, flag);
267 }
268
EnableKeyInProfile(const std::string & bundleName,const ByteBuffer & profileBuffer)269 int32_t CodeSignUtils::EnableKeyInProfile(const std::string &bundleName, const ByteBuffer &profileBuffer)
270 {
271 int ret = EnableKeyInProfileByRust(bundleName.c_str(), profileBuffer.GetBuffer(), profileBuffer.GetSize());
272 if (ret == CS_SUCCESS) {
273 ReportUserDataSize();
274 return ret;
275 }
276 LOG_ERROR("Enable key in profile failed. ret = %{public}d", ret);
277 return CS_ERR_PROFILE;
278 }
279
RemoveKeyInProfile(const std::string & bundleName)280 int32_t CodeSignUtils::RemoveKeyInProfile(const std::string &bundleName)
281 {
282 int ret = RemoveKeyInProfileByRust(bundleName.c_str());
283 if (ret == CS_SUCCESS) {
284 return ret;
285 }
286 LOG_ERROR("Remove key in profile failed. ret = %{public}d", ret);
287 return CS_ERR_PROFILE;
288 }
289
290 #ifdef SUPPORT_BINARY_ENABLE
EnableKey(const CertPathInfo & info)291 int32_t CodeSignUtils::EnableKey(const CertPathInfo &info)
292 {
293 return static_cast<int32_t>(AddCertPath(info));
294 }
295
RemoveKey(const CertPathInfo & info)296 int32_t CodeSignUtils::RemoveKey(const CertPathInfo &info)
297 {
298 return static_cast<int32_t>(RemoveCertPath(info));
299 }
300 #endif
301
EnforceCodeSignForFile(const std::string & path)302 int32_t CodeSignUtils::EnforceCodeSignForFile(const std::string &path)
303 {
304 LOG_DEBUG("Start to enforce elf file, path = %{public}s", path.c_str());
305 std::string realPath;
306 if (!OHOS::PathToRealPath(path, realPath)) {
307 return CS_ERR_FILE_PATH;
308 }
309 ElfCodeSignBlockV1 elfCodeSignBlockV1;
310 int32_t ret = elfCodeSignBlockV1.EnforceCodeSign(realPath, EnableCodeSignForFile);
311 if (ret != CS_ERR_BLOCK_MAGIC) {
312 LOG_INFO("Enforcing elf file complete. ret = %{public}d", ret);
313 return ret;
314 }
315 ret = CS_CODE_SIGN_NOT_EXISTS;
316 #ifdef SUPPORT_BINARY_ENABLE
317 ElfCodeSignBlock elfCodeSignBlock;
318 ret = elfCodeSignBlock.EnforceCodeSign(realPath, EnableCodeSignForFile);
319 #endif
320 LOG_INFO("Enforcing elf file complete. ret = %{public}d", ret);
321 return ret;
322 }
323
InPermissiveMode()324 bool CodeSignUtils::InPermissiveMode()
325 {
326 #ifdef SUPPORT_PERMISSIVE_MODE
327 // defaults to on if file does not exsit
328 std::ifstream file(Constants::XPM_DEBUG_FS_MODE_PATH);
329 if (!file.is_open()) {
330 return false;
331 }
332
333 std::string content;
334 file >> content;
335 file.close();
336
337 if (content == Constants::PERMISSIVE_CODE_SIGN_MODE) {
338 LOG_DEBUG("Permissive mode is on.");
339 return true;
340 }
341 return false;
342 #else
343 return false;
344 #endif
345 }
346
IsSupportOHCodeSign()347 bool CodeSignUtils::IsSupportOHCodeSign()
348 {
349 #ifdef SUPPORT_OH_CODE_SIGN
350 return true;
351 #else
352 return false;
353 #endif
354 }
355 }
356 }
357 }
358