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