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