• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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