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
IsFsVerityEnabled(int fd)125 int32_t CodeSignUtils::IsFsVerityEnabled(int fd)
126 {
127 unsigned int flags;
128 int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
129 if (ret < 0) {
130 LOG_ERROR("Get verity flags by ioctl failed. errno = <%{public}d, %{public}s>",
131 errno, strerror(errno));
132 return CS_ERR_FILE_INVALID;
133 }
134 if (flags & FS_VERITY_FL) {
135 return CS_SUCCESS;
136 }
137 return CS_ERR_FSVERITY_NOT_ENABLED;
138 }
139
EnforceCodeSignForFile(const std::string & path,const ByteBuffer & signature)140 int32_t CodeSignUtils::EnforceCodeSignForFile(const std::string &path, const ByteBuffer &signature)
141 {
142 return EnforceCodeSignForFile(path, signature.GetBuffer(), signature.GetSize());
143 }
144
EnableCodeSignForFile(const std::string & path,const struct code_sign_enable_arg & arg)145 int32_t CodeSignUtils::EnableCodeSignForFile(const std::string &path, const struct code_sign_enable_arg &arg)
146 {
147 int32_t ret;
148 int32_t error;
149 int32_t fd = open(path.c_str(), O_RDONLY);
150 if (fd < 0) {
151 LOG_ERROR("Open file failed, path = %{public}s, errno = <%{public}d, %{public}s>",
152 path.c_str(), errno, strerror(errno));
153 return CS_ERR_FILE_OPEN;
154 }
155
156 do {
157 ret = IsFsVerityEnabled(fd);
158 if (ret == CS_SUCCESS) {
159 LOG_INFO("Fs-verity has been enabled.");
160 break;
161 } else if (ret == CS_ERR_FILE_INVALID) {
162 break;
163 }
164
165 StartTrace(HITRACE_TAG_ACCESS_CONTROL, CODE_SIGN_ENABLE_START);
166 if (!arg.cs_version) {
167 error = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
168 } else {
169 error = ioctl(fd, FS_IOC_ENABLE_CODE_SIGN, &arg);
170 }
171 FinishTrace(HITRACE_TAG_ACCESS_CONTROL);
172 if (error < 0) {
173 LOG_ERROR("Enable fs-verity failed, errno = <%{public}d, %{public}s>",
174 errno, strerror(errno));
175 ReportEnableError(path, errno);
176 ret = CS_ERR_ENABLE;
177 break;
178 }
179 ret = CS_SUCCESS;
180 } while (0);
181 close(fd);
182 LOG_INFO("Enforcing file complete and ret = %{public}d", ret);
183 return ret;
184 }
185
ParseOwnerIdFromSignature(const ByteBuffer & sigbuffer,std::string & ownerID)186 int CodeSignUtils::ParseOwnerIdFromSignature(const ByteBuffer &sigbuffer, std::string &ownerID)
187 {
188 return SignerInfo::ParseOwnerIdFromSignature(sigbuffer, ownerID);
189 }
190
EnforceCodeSignForFile(const std::string & path,const uint8_t * signature,const uint32_t size)191 int32_t CodeSignUtils::EnforceCodeSignForFile(const std::string &path, const uint8_t *signature,
192 const uint32_t size)
193 {
194 std::string realPath;
195
196 if (signature == nullptr || size == 0) {
197 return CS_ERR_NO_SIGNATURE;
198 }
199 if (!OHOS::PathToRealPath(path, realPath)) {
200 return CS_ERR_FILE_PATH;
201 }
202
203 struct code_sign_enable_arg arg = {0};
204 arg.version = 1; // version of fs-verity, must be 1
205 arg.hash_algorithm = DEFAULT_HASH_ALGORITHEM;
206 arg.block_size = HASH_PAGE_SIZE;
207 arg.sig_size = size;
208 arg.sig_ptr = reinterpret_cast<uintptr_t>(signature);
209 return EnableCodeSignForFile(realPath, arg);
210 }
211
EnforceCodeSignForAppWithOwnerId(const std::string & ownerId,const std::string & path,const EntryMap & entryPathMap,FileType type)212 int32_t CodeSignUtils::EnforceCodeSignForAppWithOwnerId(const std::string &ownerId, const std::string &path,
213 const EntryMap &entryPathMap, FileType type)
214 {
215 LOG_INFO("Start to enforce codesign FileType:%{public}d, entryPathMap size:%{public}u, path = %{public}s",
216 type, static_cast<uint32_t>(entryPathMap.size()), path.c_str());
217 if (type == FILE_ENTRY_ADD || type == FILE_ENTRY_ONLY || type == FILE_ALL) {
218 {
219 std::lock_guard<std::mutex> lock(storedEntryMapLock_);
220 storedEntryMap_.insert(entryPathMap.begin(), entryPathMap.end());
221 }
222 if (type == FILE_ENTRY_ADD) {
223 LOG_DEBUG("Add entryPathMap complete");
224 return CS_SUCCESS;
225 }
226 } else if (type >= FILE_TYPE_MAX) {
227 return CS_ERR_PARAM_INVALID;
228 }
229 return ProcessCodeSignBlock(ownerId, path, type);
230 }
231
ProcessCodeSignBlock(const std::string & ownerId,const std::string & path,FileType type)232 int32_t CodeSignUtils::ProcessCodeSignBlock(const std::string &ownerId, const std::string &path, FileType type)
233 {
234 std::string realPath;
235 if (!OHOS::PathToRealPath(path, realPath)) {
236 return CS_ERR_FILE_PATH;
237 }
238 int32_t ret;
239 CodeSignHelper codeSignHelper;
240 {
241 std::lock_guard<std::mutex> lock(storedEntryMapLock_);
242 ret = codeSignHelper.ParseCodeSignBlock(realPath, storedEntryMap_, type);
243 storedEntryMap_.clear();
244 }
245 if (ret != CS_SUCCESS) {
246 return HandleCodeSignBlockFailure(realPath, ret);
247 }
248 return codeSignHelper.ProcessMultiTask(ownerId, path, EnableCodeSignForFile);
249 }
250
HandleCodeSignBlockFailure(const std::string & realPath,int32_t ret)251 int32_t CodeSignUtils::HandleCodeSignBlockFailure(const std::string &realPath, int32_t ret)
252 {
253 if ((ret == CS_CODE_SIGN_NOT_EXISTS) && InPermissiveMode()) {
254 LOG_DEBUG("Code sign not exists");
255 return CS_SUCCESS;
256 }
257 ReportParseCodeSig(realPath, ret);
258 return ret;
259 }
260
EnforceCodeSignForApp(const std::string & path,const EntryMap & entryPathMap,FileType type)261 int32_t CodeSignUtils::EnforceCodeSignForApp(const std::string &path, const EntryMap &entryPathMap, FileType type)
262 {
263 return EnforceCodeSignForAppWithOwnerId("", path, entryPathMap, type);
264 }
265
EnableKeyInProfile(const std::string & bundleName,const ByteBuffer & profileBuffer)266 int32_t CodeSignUtils::EnableKeyInProfile(const std::string &bundleName, const ByteBuffer &profileBuffer)
267 {
268 int ret = EnableKeyInProfileByRust(bundleName.c_str(), profileBuffer.GetBuffer(), profileBuffer.GetSize());
269 if (ret == CS_SUCCESS) {
270 return ret;
271 }
272 LOG_ERROR("Enable key in profile failed. errno = <%{public}d, %{public}s>", errno, strerror(errno));
273 return CS_ERR_PROFILE;
274 }
275
RemoveKeyInProfile(const std::string & bundleName)276 int32_t CodeSignUtils::RemoveKeyInProfile(const std::string &bundleName)
277 {
278 int ret = RemoveKeyInProfileByRust(bundleName.c_str());
279 if (ret == CS_SUCCESS) {
280 return ret;
281 }
282 LOG_ERROR("Remove key in profile failed. errno = <%{public}d, %{public}s>", errno, strerror(errno));
283 return CS_ERR_PROFILE;
284 }
285
InPermissiveMode()286 bool CodeSignUtils::InPermissiveMode()
287 {
288 #ifdef SUPPORT_PERMISSIVE_MODE
289 // defaults to on if file does not exsit
290 std::ifstream file(Constants::XPM_DEBUG_FS_MODE_PATH);
291 if (!file.is_open()) {
292 return false;
293 }
294
295 std::string content;
296 file >> content;
297 file.close();
298
299 if (content == Constants::PERMISSIVE_CODE_SIGN_MODE) {
300 LOG_DEBUG("Permissive mode is on.");
301 return true;
302 }
303 return false;
304 #else
305 return false;
306 #endif
307 }
308
IsSupportOHCodeSign()309 bool CodeSignUtils::IsSupportOHCodeSign()
310 {
311 #ifdef SUPPORT_OH_CODE_SIGN
312 return true;
313 #else
314 return false;
315 #endif
316 }
317 }
318 }
319 }
320