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