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)197 int32_t CodeSignUtils::EnforceCodeSignForAppWithOwnerId(const std::string &ownerId, const std::string &path,
198 const EntryMap &entryPathMap, FileType type)
199 {
200 LOG_INFO("Start to enforce codesign FileType:%{public}d, entryPathMap size:%{public}u, path = %{public}s",
201 type, static_cast<uint32_t>(entryPathMap.size()), path.c_str());
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);
216 if (ret != CS_SUCCESS) {
217 // retry once to make sure stability
218 ret = ProcessCodeSignBlock(ownerId, path, type);
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)225 int32_t CodeSignUtils::ProcessCodeSignBlock(const std::string &ownerId, const std::string &path, FileType type)
226 {
227 std::string realPath;
228 if (!OHOS::PathToRealPath(path, realPath)) {
229 return CS_ERR_FILE_PATH;
230 }
231 int32_t ret;
232 CodeSignHelper codeSignHelper;
233 ret = codeSignHelper.ParseCodeSignBlock(realPath, storedEntryMap_, type);
234 if (ret != CS_SUCCESS) {
235 return HandleCodeSignBlockFailure(realPath, ret);
236 }
237 ret = codeSignHelper.ProcessMultiTask(ownerId, path, EnableCodeSignForFile);
238 return ret;
239 }
240
HandleCodeSignBlockFailure(const std::string & realPath,int32_t ret)241 int32_t CodeSignUtils::HandleCodeSignBlockFailure(const std::string &realPath, int32_t ret)
242 {
243 if ((ret == CS_CODE_SIGN_NOT_EXISTS) && InPermissiveMode()) {
244 LOG_DEBUG("Code sign not exists");
245 return CS_SUCCESS;
246 }
247 ReportParseCodeSig(realPath, ret);
248 return ret;
249 }
250
EnforceCodeSignForApp(const std::string & path,const EntryMap & entryPathMap,FileType type)251 int32_t CodeSignUtils::EnforceCodeSignForApp(const std::string &path, const EntryMap &entryPathMap, FileType type)
252 {
253 return EnforceCodeSignForAppWithOwnerId("", path, entryPathMap, type);
254 }
255
EnableKeyInProfile(const std::string & bundleName,const ByteBuffer & profileBuffer)256 int32_t CodeSignUtils::EnableKeyInProfile(const std::string &bundleName, const ByteBuffer &profileBuffer)
257 {
258 int ret = EnableKeyInProfileByRust(bundleName.c_str(), profileBuffer.GetBuffer(), profileBuffer.GetSize());
259 if (ret == CS_SUCCESS) {
260 return ret;
261 }
262 LOG_ERROR("Enable key in profile failed. ret = %{public}d", ret);
263 return CS_ERR_PROFILE;
264 }
265
RemoveKeyInProfile(const std::string & bundleName)266 int32_t CodeSignUtils::RemoveKeyInProfile(const std::string &bundleName)
267 {
268 int ret = RemoveKeyInProfileByRust(bundleName.c_str());
269 if (ret == CS_SUCCESS) {
270 return ret;
271 }
272 LOG_ERROR("Remove key in profile failed. ret = %{public}d", ret);
273 return CS_ERR_PROFILE;
274 }
275
InPermissiveMode()276 bool CodeSignUtils::InPermissiveMode()
277 {
278 #ifdef SUPPORT_PERMISSIVE_MODE
279 // defaults to on if file does not exsit
280 std::ifstream file(Constants::XPM_DEBUG_FS_MODE_PATH);
281 if (!file.is_open()) {
282 return false;
283 }
284
285 std::string content;
286 file >> content;
287 file.close();
288
289 if (content == Constants::PERMISSIVE_CODE_SIGN_MODE) {
290 LOG_DEBUG("Permissive mode is on.");
291 return true;
292 }
293 return false;
294 #else
295 return false;
296 #endif
297 }
298
IsSupportOHCodeSign()299 bool CodeSignUtils::IsSupportOHCodeSign()
300 {
301 #ifdef SUPPORT_OH_CODE_SIGN
302 return true;
303 #else
304 return false;
305 #endif
306 }
307 }
308 }
309 }
310