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