• 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 "provision/provision_verify.h"
17 
18 #include <algorithm>
19 
20 #include "nlohmann/json.hpp"
21 
22 #ifndef STANDARD_SYSTEM
23 #include "ohos_account_kits.h"
24 #else
25 #include "parameter.h"
26 #include "sysparam_errno.h"
27 #endif // STANDARD_SYSTEM
28 
29 #include "common/hap_verify_log.h"
30 #include "init/device_type_manager.h"
31 
32 using namespace std;
33 using namespace nlohmann;
34 
35 namespace {
36 const string KEY_VERSION_CODE = "version-code";
37 const string KEY_VERSION_NAME = "version-name";
38 const string KEY_UUID = "uuid";
39 const string KEY_TYPE = "type";
40 const string KEY_APP_DIST_TYPE = "app-distribution-type";
41 const string KEY_BUNDLE_INFO = "bundle-info";
42 const string KEY_DEVELOPER_ID = "developer-id";
43 const string KEY_DEVELOPMENT_CERTIFICATE = "development-certificate";
44 const string KEY_DISTRIBUTION_CERTIFICATE = "distribution-certificate";
45 const string KEY_BUNDLE_NAME = "bundle-name";
46 const string KEY_APL = "apl";
47 const string KEY_APP_FEATURE = "app-feature";
48 const string KEY_ACLS = "acls";
49 const string KEY_ALLOWED_ACLS = "allowed-acls";
50 const string KEY_PERMISSIONS = "permissions";
51 const string KEY_RESTRICTED_PERMISSIONS = "restricted-permissions";
52 const string KEY_RESTRICTED_CAPABILITIES = "restricted-capabilities";
53 const string KEY_DEBUG_INFO = "debug-info";
54 const string KEY_DEVICE_ID_TYPE = "device-id-type";
55 const string KEY_DEVICE_IDS = "device-ids";
56 const string KEY_ISSUER = "issuer";
57 const string KEY_APP_PRIVILEGE_CAPABILITIES = "app-privilege-capabilities";
58 const string KEY_DEVELOPMENT_MODE = "const.product.developmentmode";
59 const string VALUE_TYPE_RELEASE = "release";
60 const string VALUE_DIST_TYPE_APP_GALLERY = "app_gallery";
61 const string VALUE_DIST_TYPE_ENTERPRISE = "enterprise";
62 const string VALUE_DIST_TYPE_OS_INTEGRATION = "os_integration";
63 const string VALUE_DIST_TYPE_CROWDTESTING = "crowdtesting";
64 const string VALUE_DEVICE_ID_TYPE_UDID = "udid";
65 
66 // reserved field
67 const string KEY_BASEAPP_INFO = "baseapp-info";
68 const string KEY_PACKAGE_NAME = "package-name";
69 const string KEY_PACKAGE_CERT = "package-cert";
70 
71 const string GENERIC_BUNDLE_NAME = ".*";
72 const string VALUE_DEVELOPMENT_MODE = "1";
73 
74 const int32_t MAXIMUM_NUM_DEVICES = 100;
75 const int32_t VERSION_CODE_TWO = 2;
76 const int32_t DEVELOPMENT_MODE_LENGTH = 2;
77 
GetStringIfExist(const json & obj,const string & key,string & out)78 inline void GetStringIfExist(const json& obj, const string& key, string& out)
79 {
80     if (obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_string()) {
81         obj[key.c_str()].get_to(out);
82     }
83 }
84 
GetInt32IfExist(const json & obj,const string & key,int32_t & out)85 inline void GetInt32IfExist(const json& obj, const string& key, int32_t& out)
86 {
87     if (obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_number_integer()) {
88         obj[key.c_str()].get_to(out);
89     }
90 }
91 
GetStringArrayIfExist(const json & obj,const string & key,vector<string> & out)92 inline void GetStringArrayIfExist(const json& obj, const string& key, vector<string>& out)
93 {
94     if (obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_array()) {
95         for (auto& item : obj[key.c_str()]) {
96             if (item.is_string()) {
97                 out.push_back(item.get<string>());
98             }
99         }
100     }
101 }
102 
IsObjectExist(const json & obj,const string & key)103 inline bool IsObjectExist(const json& obj, const string& key)
104 {
105     return obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_object();
106 }
107 } // namespace
108 
109 namespace OHOS {
110 namespace Security {
111 namespace Verify {
ParseType(const json & obj,ProvisionInfo & out)112 void ParseType(const json& obj, ProvisionInfo& out)
113 {
114     string type;
115     GetStringIfExist(obj, KEY_TYPE, type);
116     /* If not release, then it's debug */
117     out.type = (type == VALUE_TYPE_RELEASE) ? ProvisionType::RELEASE : ProvisionType::DEBUG;
118 }
119 
ParseAppDistType(const json & obj,ProvisionInfo & out)120 void ParseAppDistType(const json& obj, ProvisionInfo& out)
121 {
122     string distType;
123     GetStringIfExist(obj, KEY_APP_DIST_TYPE, distType);
124     if (distType == VALUE_DIST_TYPE_APP_GALLERY) {
125         out.distributionType = AppDistType::APP_GALLERY;
126     } else if (distType == VALUE_DIST_TYPE_ENTERPRISE) {
127         out.distributionType = AppDistType::ENTERPRISE;
128     } else if (distType == VALUE_DIST_TYPE_OS_INTEGRATION) {
129         out.distributionType = AppDistType::OS_INTEGRATION;
130     } else if (distType == VALUE_DIST_TYPE_CROWDTESTING) {
131         out.distributionType = AppDistType::CROWDTESTING;
132     } else {
133         out.distributionType = AppDistType::NONE_TYPE;
134     }
135 }
136 
ParseBundleInfo(const json & obj,ProvisionInfo & out)137 void ParseBundleInfo(const json& obj, ProvisionInfo& out)
138 {
139     if (IsObjectExist(obj, KEY_BUNDLE_INFO)) {
140         const auto& bundleInfo = obj[KEY_BUNDLE_INFO];
141         GetStringIfExist(bundleInfo, KEY_DEVELOPER_ID, out.bundleInfo.developerId);
142         GetStringIfExist(bundleInfo, KEY_DEVELOPMENT_CERTIFICATE, out.bundleInfo.developmentCertificate);
143         GetStringIfExist(bundleInfo, KEY_DISTRIBUTION_CERTIFICATE, out.bundleInfo.distributionCertificate);
144         GetStringIfExist(bundleInfo, KEY_BUNDLE_NAME, out.bundleInfo.bundleName);
145         GetStringIfExist(bundleInfo, KEY_APL, out.bundleInfo.apl);
146         GetStringIfExist(bundleInfo, KEY_APP_FEATURE, out.bundleInfo.appFeature);
147     }
148 }
149 
ParseAcls(const json & obj,ProvisionInfo & out)150 void ParseAcls(const json& obj, ProvisionInfo& out)
151 {
152     if (IsObjectExist(obj, KEY_ACLS)) {
153         const auto& acls = obj[KEY_ACLS];
154         GetStringArrayIfExist(acls, KEY_ALLOWED_ACLS, out.acls.allowedAcls);
155     }
156 }
157 
ParsePermissions(const json & obj,ProvisionInfo & out)158 void ParsePermissions(const json& obj, ProvisionInfo& out)
159 {
160     if (IsObjectExist(obj, KEY_PERMISSIONS)) {
161         const auto& permissions = obj[KEY_PERMISSIONS];
162         GetStringArrayIfExist(permissions, KEY_RESTRICTED_PERMISSIONS, out.permissions.restrictedPermissions);
163         GetStringArrayIfExist(permissions, KEY_RESTRICTED_CAPABILITIES, out.permissions.restrictedCapabilities);
164     }
165 }
166 
ParseDebugInfo(const json & obj,ProvisionInfo & out)167 void ParseDebugInfo(const json& obj, ProvisionInfo& out)
168 {
169     if (IsObjectExist(obj, KEY_DEBUG_INFO)) {
170         GetStringIfExist(obj[KEY_DEBUG_INFO], KEY_DEVICE_ID_TYPE, out.debugInfo.deviceIdType);
171         GetStringArrayIfExist(obj[KEY_DEBUG_INFO], KEY_DEVICE_IDS, out.debugInfo.deviceIds);
172     }
173 }
174 
ParseMetadata(const json & obj,ProvisionInfo & out)175 void ParseMetadata(const json& obj, ProvisionInfo& out)
176 {
177     if (IsObjectExist(obj, KEY_BASEAPP_INFO)) {
178         const auto& baseAppInfo = obj[KEY_BASEAPP_INFO];
179         Metadata metadata;
180         metadata.name = KEY_PACKAGE_NAME;
181         GetStringIfExist(baseAppInfo, KEY_PACKAGE_NAME, metadata.value);
182         out.metadatas.emplace_back(metadata);
183         metadata.name = KEY_PACKAGE_CERT;
184         GetStringIfExist(baseAppInfo, KEY_PACKAGE_CERT, metadata.value);
185         out.metadatas.emplace_back(metadata);
186     }
187 }
188 
from_json(const json & obj,ProvisionInfo & out)189 void from_json(const json& obj, ProvisionInfo& out)
190 {
191     if (!obj.is_object()) {
192         return;
193     }
194     GetInt32IfExist(obj, KEY_VERSION_CODE, out.versionCode);
195     GetStringIfExist(obj, KEY_VERSION_NAME, out.versionName);
196     GetStringIfExist(obj, KEY_UUID, out.uuid);
197     ParseType(obj, out);
198     ParseAppDistType(obj, out);
199     ParseBundleInfo(obj, out);
200     ParseAcls(obj, out);
201     ParsePermissions(obj, out);
202     ParseDebugInfo(obj, out);
203     GetStringIfExist(obj, KEY_ISSUER, out.issuer);
204     GetStringArrayIfExist(obj, KEY_APP_PRIVILEGE_CAPABILITIES, out.appPrivilegeCapabilities);
205     ParseMetadata(obj, out);
206 }
207 
208 #define RETURN_IF_STRING_IS_EMPTY(str, msg) \
209     if (str.empty()) {                      \
210         HAPVERIFY_LOG_ERROR(LABEL, msg);    \
211         return PROVISION_INVALID;           \
212     }
213 
214 #define RETURN_IF_INT_IS_NON_POSITIVE(num, msg) \
215     if (num <= 0) {                             \
216         HAPVERIFY_LOG_ERROR(LABEL, msg);        \
217         return PROVISION_INVALID;               \
218     }
219 
ParseProvision(const string & appProvision,ProvisionInfo & info)220 AppProvisionVerifyResult ParseProvision(const string& appProvision, ProvisionInfo& info)
221 {
222     json obj = json::parse(appProvision, nullptr, false);
223     if (!obj.is_structured()) {
224         HAPVERIFY_LOG_ERROR(LABEL, "Parsing appProvision failed. json: %{public}s", appProvision.c_str());
225         return PROVISION_INVALID;
226     }
227     obj.get_to(info);
228 
229     RETURN_IF_INT_IS_NON_POSITIVE(info.versionCode, "Tag version code is empty.")
230     RETURN_IF_STRING_IS_EMPTY(info.versionName, "Tag version name is empty.")
231     RETURN_IF_STRING_IS_EMPTY(info.uuid, "Tag uuid is empty.")
232     RETURN_IF_STRING_IS_EMPTY(info.bundleInfo.developerId, "Tag developer-id is empty.")
233     if (info.type == ProvisionType::DEBUG) {
234         RETURN_IF_STRING_IS_EMPTY(info.bundleInfo.developmentCertificate, "Tag development-certificate is empty.")
235     } else if (info.type == ProvisionType::RELEASE) {
236         RETURN_IF_INT_IS_NON_POSITIVE(info.distributionType, "Tag app-distribution-type is empty.")
237         RETURN_IF_STRING_IS_EMPTY(info.bundleInfo.distributionCertificate, "Tag distribution-certificate is empty.")
238     }
239     RETURN_IF_STRING_IS_EMPTY(info.bundleInfo.bundleName, "Tag bundle-name is empty.")
240     if (info.bundleInfo.bundleName == GENERIC_BUNDLE_NAME) {
241         HAPVERIFY_LOG_DEBUG(LABEL, "generic package name: %{public}s, is used.", GENERIC_BUNDLE_NAME.c_str());
242     }
243     if (info.versionCode >= VERSION_CODE_TWO) {
244         RETURN_IF_STRING_IS_EMPTY(info.bundleInfo.apl, "Tag apl is empty.");
245     }
246     RETURN_IF_STRING_IS_EMPTY(info.bundleInfo.appFeature, "Tag app-feature is empty.")
247 
248     return PROVISION_OK;
249 }
250 
CheckDeviceID(const std::vector<std::string> & deviceIds,const string & deviceId)251 inline bool CheckDeviceID(const std::vector<std::string>& deviceIds, const string& deviceId)
252 {
253     auto iter = find(deviceIds.begin(), deviceIds.end(), deviceId);
254     if (iter == deviceIds.end()) {
255         DeviceTypeManager& deviceTypeManager = DeviceTypeManager::GetInstance();
256         if (!deviceTypeManager.GetDeviceTypeInfo()) {
257             HAPVERIFY_LOG_ERROR(LABEL, "current device is not authorized");
258             return false;
259         }
260         HAPVERIFY_LOG_INFO(LABEL, "current device is a debug device");
261     }
262     return true;
263 }
264 
CheckDeviceID(ProvisionInfo & info)265 AppProvisionVerifyResult CheckDeviceID(ProvisionInfo& info)
266 {
267     // Checking device ids
268     if (info.debugInfo.deviceIds.empty()) {
269         HAPVERIFY_LOG_ERROR(LABEL, "device-id list is empty.");
270         return PROVISION_DEVICE_UNAUTHORIZED;
271     }
272 
273     if (info.debugInfo.deviceIds.size() > MAXIMUM_NUM_DEVICES) {
274         HAPVERIFY_LOG_ERROR(LABEL, "No. of device IDs in list exceed maximum number %{public}d", MAXIMUM_NUM_DEVICES);
275         return PROVISION_NUM_DEVICE_EXCEEDED;
276     }
277 
278     if (info.debugInfo.deviceIdType != VALUE_DEVICE_ID_TYPE_UDID) {
279         HAPVERIFY_LOG_ERROR(LABEL, "type of device ID is not supported.");
280         return PROVISION_UNSUPPORTED_DEVICE_TYPE;
281     }
282 
283     string deviceId;
284 #ifndef STANDARD_SYSTEM
285     int32_t ret = OHOS::AccountSA::OhosAccountKits::GetInstance().GetUdid(deviceId);
286     if (ret != 0) {
287         HAPVERIFY_LOG_ERROR(LABEL, "obtaining current device id failed (%{public}d).", ret);
288         return PROVISION_DEVICE_UNAUTHORIZED;
289     }
290 #else
291     char udid[DEV_UUID_LEN] = {0};
292     int ret = GetDevUdid(udid, sizeof(udid));
293     if (ret != EC_SUCCESS) {
294         HAPVERIFY_LOG_ERROR(LABEL, "obtaining current device id failed (%{public}d).", static_cast<int>(ret));
295         return PROVISION_DEVICE_UNAUTHORIZED;
296     }
297     deviceId = std::string(udid, sizeof(udid) - 1);
298 #endif // STANDARD_SYSTEM
299     if (deviceId.empty()) {
300         HAPVERIFY_LOG_ERROR(LABEL, "device-id of current device is empty.");
301         return PROVISION_DEVICE_UNAUTHORIZED;
302     }
303 
304     if (!CheckDeviceID(info.debugInfo.deviceIds, deviceId)) {
305         return PROVISION_DEVICE_UNAUTHORIZED;
306     }
307     return PROVISION_OK;
308 }
309 
ParseAndVerify(const string & appProvision,ProvisionInfo & info)310 AppProvisionVerifyResult ParseAndVerify(const string& appProvision, ProvisionInfo& info)
311 {
312     HAPVERIFY_LOG_DEBUG(LABEL, "Enter HarmonyAppProvision Verify");
313     AppProvisionVerifyResult ret = ParseProvision(appProvision, info);
314     if (ret != PROVISION_OK) {
315         return ret;
316     }
317     const char *key = KEY_DEVELOPMENT_MODE.data();
318     char developmentMode[DEVELOPMENT_MODE_LENGTH] = {0};
319     GetParameter(key, nullptr, developmentMode, sizeof(developmentMode));
320     const char *value = VALUE_DEVELOPMENT_MODE.data();
321     bool isDevelopmentMode = (strcmp(developmentMode, value) == 0) ? true : false;
322     HAPVERIFY_LOG_DEBUG(LABEL, "Current development mode is %{public}d", isDevelopmentMode);
323     if (info.type == ProvisionType::DEBUG && !isDevelopmentMode) {
324         ret = CheckDeviceID(info);
325         if (ret != PROVISION_OK) {
326             return ret;
327         }
328     }
329     HAPVERIFY_LOG_DEBUG(LABEL, "Leave HarmonyAppProvision Verify");
330     return PROVISION_OK;
331 }
332 
ParseProfile(const std::string & appProvision,ProvisionInfo & info)333 AppProvisionVerifyResult ParseProfile(const std::string& appProvision, ProvisionInfo& info)
334 {
335     json obj = json::parse(appProvision, nullptr, false);
336     if (!obj.is_structured()) {
337         HAPVERIFY_LOG_ERROR(LABEL, "Parsing appProvision failed. json: %{public}s", appProvision.c_str());
338         return PROVISION_INVALID;
339     }
340     obj.get_to(info);
341     return PROVISION_OK;
342 }
343 } // namespace Verify
344 } // namespace Security
345 } // namespace OHOS
346