1 /*
2 * Copyright (C) 2021-2022 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 "ticket/ticket_verify.h"
17
18 #include <algorithm>
19 #include <regex>
20
21 #ifndef STANDARD_SYSTEM
22 #include "ohos_account_kits.h"
23 #else
24 #include "parameter.h"
25 #include "sysparam_errno.h"
26 #endif // STANDARD_SYSTEM
27
28 #include "common/hap_byte_buffer.h"
29 #include "common/hap_verify_log.h"
30 #include "common/random_access_file.h"
31 #include "init/trusted_ticket_manager.h"
32 #include "util/hap_cert_verify_openssl_utils.h"
33 #include "util/hap_verify_openssl_utils.h"
34 #include "util/pkcs7_context.h"
35
36 namespace {
37 const int32_t TICKET_MAX_SIZE = 18432;
38 const std::string TICKET_FILE_PATH = "/data/update/ticket/";
39 const std::string VALUE_DEVICE_TYPE_UDID = "udid";
40 } // namespace
41
42 namespace OHOS {
43 namespace Security {
44 namespace Verify {
CheckTicketFilePath(const std::string & filePath,std::string & standardFilePath)45 bool CheckTicketFilePath(const std::string& filePath, std::string& standardFilePath)
46 {
47 char path[PATH_MAX + 1] = { 0x00 };
48 if (filePath.size() > PATH_MAX || realpath(filePath.c_str(), path) == nullptr) {
49 HAPVERIFY_LOG_ERROR("filePath is not a standard path");
50 return false;
51 }
52 standardFilePath = std::string(path);
53 return true;
54 }
55
CheckPermissions(std::vector<std::string> ticketPermissions,std::vector<std::string> profilePermissions)56 bool CheckPermissions(std::vector<std::string> ticketPermissions, std::vector<std::string> profilePermissions)
57 {
58 for (auto ticket : ticketPermissions) {
59 auto iter = find(profilePermissions.begin(), profilePermissions.end(), ticket);
60 if (iter == profilePermissions.end()) {
61 return false;
62 }
63 }
64 return true;
65 }
66
CheckDevice(const std::vector<std::string> & deviceIds,const std::string & deviceId)67 bool CheckDevice(const std::vector<std::string>& deviceIds, const std::string& deviceId)
68 {
69 auto iter = find(deviceIds.begin(), deviceIds.end(), deviceId);
70 if (iter == deviceIds.end()) {
71 return false;
72 }
73 return true;
74 }
75
CheckDevice(ProvisionInfo & info)76 AppProvisionVerifyResult CheckDevice(ProvisionInfo& info)
77 {
78 // Checking device ids
79 if (info.debugInfo.deviceIds.empty()) {
80 HAPVERIFY_LOG_ERROR("device-id list is empty.");
81 return PROVISION_DEVICE_UNAUTHORIZED;
82 }
83
84 HAPVERIFY_LOG_DEBUG("number of device ids in list: %{public}u",
85 static_cast<uint32_t>(info.debugInfo.deviceIds.size()));
86
87 if (info.debugInfo.deviceIdType != VALUE_DEVICE_TYPE_UDID) {
88 HAPVERIFY_LOG_ERROR("type of device ID is not supported.");
89 return PROVISION_UNSUPPORTED_DEVICE_TYPE;
90 }
91
92 std::string deviceId;
93 #ifndef STANDARD_SYSTEM
94 int32_t ret = OHOS::AccountSA::OhosAccountKits::GetInstance().GetUdid(deviceId);
95 if (ret != 0) {
96 HAPVERIFY_LOG_ERROR("obtaining current device id failed (%{public}d).", ret);
97 return PROVISION_DEVICE_UNAUTHORIZED;
98 }
99 #else
100 char udid[DEV_UUID_LEN] = {0};
101 int32_t ret = GetDevUdid(udid, sizeof(udid));
102 if (ret != EC_SUCCESS) {
103 HAPVERIFY_LOG_ERROR("obtaining current device id failed (%{public}d).", static_cast<int>(ret));
104 return PROVISION_DEVICE_UNAUTHORIZED;
105 }
106 deviceId = std::string(udid, sizeof(udid) - 1);
107 #endif // STANDARD_SYSTEM
108 if (deviceId.empty()) {
109 HAPVERIFY_LOG_ERROR("device-id of current device is empty.");
110 return PROVISION_DEVICE_UNAUTHORIZED;
111 }
112
113 if (!CheckDevice(info.debugInfo.deviceIds, deviceId)) {
114 return PROVISION_DEVICE_UNAUTHORIZED;
115 }
116 return PROVISION_OK;
117 }
118
CompareTicketAndProfile(const ProvisionInfo & ticketInfo,const ProvisionInfo & profileInfo)119 int32_t CompareTicketAndProfile(const ProvisionInfo& ticketInfo, const ProvisionInfo& profileInfo)
120 {
121 if (ticketInfo.bundleInfo.bundleName != profileInfo.bundleInfo.bundleName) {
122 HAPVERIFY_LOG_ERROR("ticket bundlename doesn't match");
123 return TICKET_NOT_MATCH;
124 }
125
126 if (ticketInfo.type != profileInfo.type) {
127 return TICKET_NOT_MATCH;
128 }
129
130 if (ticketInfo.type == DEBUG) {
131 if (ticketInfo.bundleInfo.developmentCertificate != profileInfo.bundleInfo.developmentCertificate) {
132 HAPVERIFY_LOG_ERROR("ticket developmentCertificate doesn't match");
133 return TICKET_NOT_MATCH;
134 }
135 } else {
136 if (ticketInfo.bundleInfo.distributionCertificate != profileInfo.bundleInfo.distributionCertificate) {
137 HAPVERIFY_LOG_ERROR("ticket distributionCertificate doesn't match");
138 return TICKET_NOT_MATCH;
139 }
140 }
141
142 if (!ticketInfo.permissions.restrictedCapabilities.empty()) {
143 if (!CheckPermissions(ticketInfo.permissions.restrictedCapabilities,
144 profileInfo.permissions.restrictedCapabilities)) {
145 HAPVERIFY_LOG_ERROR("ticket restrictedCapabilities doesn't match");
146 return TICKET_PERMISSION_ERROR;
147 }
148 }
149
150 if (!ticketInfo.permissions.restrictedPermissions.empty()) {
151 if (!CheckPermissions(ticketInfo.permissions.restrictedPermissions,
152 profileInfo.permissions.restrictedPermissions)) {
153 HAPVERIFY_LOG_ERROR("ticket restrictedPermissions doesn't match");
154 return TICKET_PERMISSION_ERROR;
155 }
156 }
157 return TICKET_OK;
158 }
159
VerifyTicketSignature(HapByteBuffer & ticketBlock,Pkcs7Context & pkcs7Context,std::string & ticket)160 bool VerifyTicketSignature(HapByteBuffer& ticketBlock, Pkcs7Context& pkcs7Context, std::string& ticket)
161 {
162 const unsigned char* pkcs7Block = reinterpret_cast<const unsigned char*>(ticketBlock.GetBufferPtr());
163 uint32_t pkcs7Len = static_cast<unsigned int>(ticketBlock.GetCapacity());
164 if (!HapVerifyOpensslUtils::ParsePkcs7Package(pkcs7Block, pkcs7Len, pkcs7Context)) {
165 HAPVERIFY_LOG_ERROR("Parse ticket pkcs7 failed");
166 return false;
167 }
168 ticket = std::string(pkcs7Context.content.GetBufferPtr(), pkcs7Context.content.GetCapacity());
169
170 if (!HapVerifyOpensslUtils::GetCertChains(pkcs7Context.p7, pkcs7Context)) {
171 HAPVERIFY_LOG_ERROR("GetCertChains from ticket pkcs7 failed");
172 return false;
173 }
174
175 if (!HapVerifyOpensslUtils::VerifyPkcs7(pkcs7Context)) {
176 HAPVERIFY_LOG_ERROR("verify ticket signature failed");
177 return false;
178 }
179
180 std::string certSubject;
181 if (!HapCertVerifyOpensslUtils::GetSubjectFromX509(pkcs7Context.certChains[0][0], certSubject)) {
182 HAPVERIFY_LOG_ERROR("Get info of sign cert from ticket failed");
183 return false;
184 }
185
186 TrustedTicketManager& trustedTicketSourceManager = TrustedTicketManager::GetInstance();
187 pkcs7Context.matchResult = trustedTicketSourceManager.IsTrustedSource(certSubject, pkcs7Context.certIssuer,
188 pkcs7Context.certChains[0].size());
189 if (pkcs7Context.matchResult.matchState == DO_NOT_MATCH) {
190 HAPVERIFY_LOG_ERROR("Ticket signature is not trusted source, subject: %{private}s, issuer: %{public}s",
191 certSubject.c_str(), pkcs7Context.certIssuer.c_str());
192 return false;
193 }
194 HAPVERIFY_LOG_INFO("Ticket subject: %{private}s, issuer: %{public}s",
195 certSubject.c_str(), pkcs7Context.certIssuer.c_str());
196 return true;
197 }
198
TicketParseAndVerify(const std::string & ticket,ProvisionInfo & ticketInfo,const ProvisionInfo & profileInfo)199 int32_t TicketParseAndVerify(const std::string& ticket, ProvisionInfo& ticketInfo,
200 const ProvisionInfo& profileInfo)
201 {
202 if (ParseProvision(ticket, ticketInfo) != PROVISION_OK) {
203 return TICKET_PARSE_FAIL;
204 }
205 int32_t ret = CompareTicketAndProfile(ticketInfo, profileInfo);
206 if (ret != TICKET_OK) {
207 return ret;
208 }
209 if (CheckDevice(ticketInfo) != PROVISION_OK) {
210 return TICKET_DEVICE_INVALID;
211 }
212 return TICKET_OK;
213 }
214
VerifyTicket(const std::string & filePath,const ProvisionInfo & profileInfo)215 int32_t VerifyTicket(const std::string& filePath, const ProvisionInfo& profileInfo)
216 {
217 HAPVERIFY_LOG_DEBUG("Enter Ticket Verify");
218 RandomAccessFile ticketFile;
219 if (!ticketFile.Init(filePath)) {
220 HAPVERIFY_LOG_ERROR("open %{public}s failed", filePath.c_str());
221 return OPEN_TICKET_FILE_ERROR;
222 }
223 long long fileLength = ticketFile.GetLength();
224 if (fileLength > TICKET_MAX_SIZE) {
225 HAPVERIFY_LOG_ERROR("file length %{public}lld is too larger", fileLength);
226 return OPEN_TICKET_FILE_ERROR;
227 }
228 int32_t fileLen = static_cast<int>(fileLength);
229 HapByteBuffer ticketBlock(fileLen);
230 long long ret = ticketFile.ReadFileFullyFromOffset(ticketBlock, 0);
231 if (ret < 0) {
232 HAPVERIFY_LOG_ERROR("read data from ticket error: %{public}lld", ret);
233 return TICKET_READ_FAIL;
234 }
235
236 Pkcs7Context pkcs7Context;
237 std::string ticket;
238 if (!VerifyTicketSignature(ticketBlock, pkcs7Context, ticket)) {
239 HAPVERIFY_LOG_ERROR("verify ticket signature failed");
240 return SIGNATURE_VERIFY_FAIL;
241 }
242
243 ProvisionInfo ticketInfo;
244 int32_t ticketRet = TicketParseAndVerify(ticket, ticketInfo, profileInfo);
245 if (ticketRet != TICKET_OK) {
246 HAPVERIFY_LOG_ERROR("ticket parse failed, error: %{public}d", static_cast<int>(ticketRet));
247 return ticketRet;
248 }
249 HAPVERIFY_LOG_DEBUG("Leave Ticket Verify");
250 return TICKET_VERIFY_SUCCESS;
251 }
252
CheckTicketSource(const ProvisionInfo & profileInfo)253 bool CheckTicketSource(const ProvisionInfo& profileInfo)
254 {
255 std::string ticketfilepath = TICKET_FILE_PATH + profileInfo.bundleInfo.bundleName + ".p7b";
256 std::string standardFilePath;
257 if (!CheckTicketFilePath(ticketfilepath, standardFilePath)) {
258 return false;
259 }
260
261 int32_t ret = VerifyTicket(standardFilePath, profileInfo);
262 if (ret != TICKET_VERIFY_SUCCESS) {
263 HAPVERIFY_LOG_ERROR("ticket verify failed, result: %{public}d", ret);
264 return false;
265 }
266 HAPVERIFY_LOG_INFO("Ticket verify success");
267 return true;
268 }
269 } // namespace Verify
270 } // namespace Security
271 } // namespace OHOS
272