• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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