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_enable_multi_task.h"
17 
18 #include <fcntl.h>
19 #include <linux/fs.h>
20 #include <linux/stat.h>
21 #include <linux/types.h>
22 #include <sys/ioctl.h>
23 #include <sys/types.h>
24 
25 #include "byte_buffer.h"
26 #include "cs_hisysevent.h"
27 #include "errcode.h"
28 #include "log.h"
29 #include "signer_info.h"
30 #include "stat_utils.h"
31 
32 namespace OHOS {
33 namespace Security {
34 namespace CodeSign {
35 constexpr uint32_t CODE_SIGN_TASK_TIMEOUT_MS = 300000;
36 constexpr uint32_t DEFAULT_THREADS_NUM = 8;
37 
CodeSignEnableMultiTask()38 CodeSignEnableMultiTask::CodeSignEnableMultiTask(): enableCodeSignTaskWorker_("EnableCodeSign"), taskCallBack_(0)
39 {
40     LOG_INFO("Tasks init.");
41     enableCodeSignTaskWorker_.Start(DEFAULT_THREADS_NUM);
42 }
43 
~CodeSignEnableMultiTask()44 CodeSignEnableMultiTask::~CodeSignEnableMultiTask()
45 {
46     LOG_INFO("Tasks finish.");
47     enableCodeSignTaskWorker_.Stop();
48 }
49 
AddTaskData(const std::string & targetFile,const struct code_sign_enable_arg & arg)50 void CodeSignEnableMultiTask::AddTaskData(const std::string &targetFile, const struct code_sign_enable_arg &arg)
51 {
52     enableData_.push_back(std::pair<std::string, code_sign_enable_arg>(targetFile, arg));
53 }
54 
IsFsVerityEnabled(int fd)55 int32_t CodeSignEnableMultiTask::IsFsVerityEnabled(int fd)
56 {
57     unsigned int flags;
58     int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
59     if (ret < 0) {
60         LOG_ERROR("Get verity flags by ioctl failed. errno = <%{public}d, %{public}s>",
61             errno, strerror(errno));
62         return CS_ERR_FILE_INVALID;
63     }
64     if (flags & FS_VERITY_FL) {
65         return CS_SUCCESS;
66     }
67     return CS_ERR_FSVERITY_NOT_ENABLED;
68 }
69 
IsFsVerityEnabled(const std::string & path)70 int32_t CodeSignEnableMultiTask::IsFsVerityEnabled(const std::string &path)
71 {
72     int32_t fd = open(path.c_str(), O_RDONLY);
73     if (fd < 0) {
74         LOG_ERROR("Open file failed, path = %{public}s, errno = <%{public}d, %{public}s>",
75             path.c_str(), errno, strerror(errno));
76         return CS_ERR_FILE_OPEN;
77     }
78     int32_t ret = IsFsVerityEnabled(fd);
79     if (ret != CS_SUCCESS) {
80         LOG_INFO("Fs-verity is not enable for file = %{public}s.", path.c_str());
81     }
82     close(fd);
83     return ret;
84 }
85 
ExecuteEnableCodeSignTask(const std::string & ownerId,const std::string & path,CallbackFunc & func)86 int32_t CodeSignEnableMultiTask::ExecuteEnableCodeSignTask(const std::string &ownerId,
87     const std::string &path, CallbackFunc &func)
88 {
89     SortTaskData();
90 
91     LOG_INFO("Tasks num = %{public}zu", enableData_.size());
92     int32_t taskRet = CS_SUCCESS;
93     for (uint32_t i = 0; i < enableData_.size(); i++) {
94         LOG_DEBUG("index: %{public}d, name:%{public}s, %{public}lld",
95             i, enableData_[i].first.c_str(), enableData_[i].second.data_size);
96         ExecuteEnableCodeSignTask(i, taskRet, ownerId, path, func);
97     }
98 
99     std::unique_lock<std::mutex> lock(cvLock_);
100     auto waitStatus = taskfinish_.wait_for(lock, std::chrono::milliseconds(CODE_SIGN_TASK_TIMEOUT_MS),
101         [this]() { return this->enableData_.size() == this->taskCallBack_; });
102     if (!waitStatus) {
103         LOG_ERROR("enable code sign timeout, finished tasks = %{public}u", taskCallBack_);
104         return CS_ERR_ENABLE_TIMEOUT;
105     }
106     if (taskRet != CS_SUCCESS) {
107         return taskRet;
108     }
109     int32_t ret = CS_SUCCESS;
110     for (auto &data : enableData_) {
111         const std::string &filePath = data.first;
112         if (IsFsVerityEnabled(filePath) != CS_SUCCESS) {
113             ret = CS_ERR_FSVERITY_NOT_ENABLED;
114             ReportEnableError(filePath, ret);
115         }
116     }
117     return ret;
118 }
119 
SortTaskData()120 void CodeSignEnableMultiTask::SortTaskData()
121 {
122     auto compareFileDataSize = [](const std::pair<std::string, code_sign_enable_arg> &a,
123         const std::pair<std::string, code_sign_enable_arg> &b) {
124         return a.second.data_size > b.second.data_size;
125     };
126     sort(enableData_.begin(), enableData_.end(), compareFileDataSize);
127 }
128 
ExecuteEnableCodeSignTask(uint32_t & index,int32_t & taskRet,const std::string & ownerId,const std::string & path,CallbackFunc & func)129 void CodeSignEnableMultiTask::ExecuteEnableCodeSignTask(uint32_t &index, int32_t &taskRet,
130     const std::string &ownerId, const std::string &path, CallbackFunc &func)
131 {
132     auto enableCodeSignTask = [this, index, &ownerId, &path, &func, &taskRet]() {
133         LOG_DEBUG("ExecuteEnableCodeSignTask task called");
134         {
135             std::unique_lock<std::mutex> lock(cvLock_);
136             if (taskRet != CS_SUCCESS) {
137                 this->taskCallBack_++;
138                 if (this->taskCallBack_ == this->enableData_.size()) {
139                     this->taskfinish_.notify_one();
140                 }
141                 return;
142             }
143         }
144 
145         int32_t ret = CheckOwnerId(path, ownerId,
146             reinterpret_cast<const uint8_t *>(this->enableData_[index].second.sig_ptr),
147             this->enableData_[index].second.sig_size);
148         if (ret == CS_SUCCESS) {
149             ret = func(this->enableData_[index].first, this->enableData_[index].second);
150         }
151         LOG_DEBUG("Task return info index: %{public}d, ret: %{public}d", index, ret);
152 
153         std::unique_lock<std::mutex> lock(cvLock_);
154         if (taskRet == CS_SUCCESS) {
155             taskRet = ret;
156         }
157         this->taskCallBack_++;
158         if (this->taskCallBack_ == this->enableData_.size()) {
159             this->taskfinish_.notify_one();
160         }
161     };
162     enableCodeSignTaskWorker_.AddTask(enableCodeSignTask);
163 }
164 
CheckOwnerId(const std::string & path,const std::string & ownerId,const uint8_t * sigPtr,uint32_t sigSize)165 int32_t CodeSignEnableMultiTask::CheckOwnerId(const std::string &path, const std::string &ownerId,
166     const uint8_t *sigPtr, uint32_t sigSize)
167 {
168     if (ownerId.empty()) {
169         return CS_SUCCESS;
170     }
171 
172     int32_t ret;
173     ByteBuffer sigBuffer;
174     sigBuffer.CopyFrom(sigPtr, sigSize);
175     std::string retId;
176     ret = SignerInfo::ParseOwnerIdFromSignature(sigBuffer, retId);
177     if (ret != CS_SUCCESS) {
178         ReportInvalidOwner(path, ownerId, "invalid");
179         LOG_ERROR("get ownerId from signature failed, ret %{public}d", ret);
180     } else if (retId != ownerId) {
181         ret = CS_ERR_INVALID_OWNER_ID;
182         ReportInvalidOwner(path, ownerId, retId);
183         LOG_ERROR("invalid ownerId retId %{public}s ownerId %{public}s", retId.c_str(), ownerId.c_str());
184     }
185     return ret;
186 }
187 }
188 }
189 }