1 /*
2 * Copyright (c) 2021-2024 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 "netstack_common_utils.h"
17
18 #ifdef WINDOWS_PLATFORM
19 #include <winsock2.h>
20 #include <ws2tcpip.h>
21 #pragma comment(lib, "ws2_32.lib")
22 #else
23 #include <arpa/inet.h>
24 #endif
25
26 #include <algorithm>
27 #include <cctype>
28 #include <cerrno>
29 #include <regex>
30 #include <string>
31 #include <unistd.h>
32 #include <vector>
33 #include <fstream>
34 #include <sstream>
35 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
36 #include <filesystem>
37 #endif
38
39 #include "netstack_log.h"
40 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
41 #include "netstack_apipolicy_utils.h"
42 #include "netstack_bundle_utils.h"
43 #endif
44
45 constexpr int32_t INET_OPTION_SUC = 1;
46 constexpr size_t MAX_DISPLAY_NUM = 2;
47 constexpr int SHA256_BASE64_LEN = 44; // 32-byte base64 -> 44 bytes
48 constexpr int PINNED_PREFIX_LEN = 8; // strlen("sha256//")
49
50 namespace OHOS::NetStack::CommonUtils {
51 const std::regex IP_PATTERN{
52 "((2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)\\.){3}(2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)"};
53 const std::regex IP_MASK_PATTERN{
54 "((2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)\\.){3}(2([0-4]\\d|5[0-5])|1\\d\\d|[1-9]\\d|\\d)/"
55 "(3[0-2]|[1-2]\\d|\\d)"};
56 const std::regex IPV6_PATTERN{"([\\da-fA-F]{0,4}:){2,7}([\\da-fA-F]{0,4})"};
57 const std::regex IPV6_MASK_PATTERN{"([\\da-fA-F]{0,4}:){2,7}([\\da-fA-F]{0,4})/(1[0-2][0-8]|[1-9]\\d|[1-9])"};
58 static const std::string PROTOCOL_WSS = "wss";
59 std::mutex g_commonUtilsMutex;
60
Split(const std::string & str,const std::string & sep)61 std::vector<std::string> Split(const std::string &str, const std::string &sep)
62 {
63 std::string s = str;
64 std::vector<std::string> res;
65 while (!s.empty()) {
66 auto pos = s.find(sep);
67 if (pos == std::string::npos) {
68 res.emplace_back(s);
69 break;
70 }
71 res.emplace_back(s.substr(0, pos));
72 s = s.substr(pos + sep.size());
73 }
74 return res;
75 }
76
Split(const std::string & str,const std::string & sep,size_t size)77 std::vector<std::string> Split(const std::string &str, const std::string &sep, size_t size)
78 {
79 std::string s = str;
80 std::vector<std::string> res;
81 while (!s.empty()) {
82 if (res.size() + 1 == size) {
83 res.emplace_back(s);
84 break;
85 }
86
87 auto pos = s.find(sep);
88 if (pos == std::string::npos) {
89 res.emplace_back(s);
90 break;
91 }
92 res.emplace_back(s.substr(0, pos));
93 s = s.substr(pos + sep.size());
94 }
95 return res;
96 }
97
Strip(const std::string & str,char ch)98 std::string Strip(const std::string &str, char ch)
99 {
100 int64_t i = 0;
101 while (static_cast<size_t>(i) < str.size() && str[i] == ch) {
102 ++i;
103 }
104 int64_t j = static_cast<int64_t>(str.size()) - 1;
105 while (j > 0 && str[j] == ch) {
106 --j;
107 }
108 if (i >= 0 && static_cast<size_t>(i) < str.size() && j >= 0 && static_cast<size_t>(j) < str.size() &&
109 j - i + 1 > 0) {
110 return str.substr(i, j - i + 1);
111 }
112 return "";
113 }
114
ToLower(const std::string & s)115 std::string ToLower(const std::string &s)
116 {
117 std::string res = s;
118 std::transform(res.begin(), res.end(), res.begin(), tolower);
119 return res;
120 }
121
ToString(const std::list<std::string> & lists,char tab)122 std::string ToString(const std::list<std::string> &lists, char tab)
123 {
124 std::string str;
125 for (auto it = lists.begin(); it != lists.end(); ++it) {
126 if (it != lists.begin()) {
127 str.append(1, tab);
128 }
129 str.append(*it);
130 }
131 return str;
132 }
133
HasInternetPermission()134 bool HasInternetPermission()
135 {
136 #ifndef OH_CORE_NETSTACK_PERMISSION_CHECK
137 #ifdef FUZZ_TEST
138 return true;
139 #endif
140 int testSock = socket(AF_INET, SOCK_STREAM, 0);
141 if (testSock < 0 && errno == EPERM) {
142 NETSTACK_LOGE("make tcp testSock failed errno is %{public}d %{public}s", errno, strerror(errno));
143 return false;
144 }
145 if (testSock > 0) {
146 close(testSock);
147 }
148 return true;
149 #else
150 constexpr int inetGroup = 40002003; // 3003 in gateway shell.
151 int groupNum = getgroups(0, nullptr);
152 if (groupNum <= 0) {
153 NETSTACK_LOGE("no group of INTERNET permission");
154 return false;
155 }
156 auto groups = (gid_t *)malloc(groupNum * sizeof(gid_t));
157 if (groups == nullptr) {
158 NETSTACK_LOGE("INTERNET permission denied by malloc");
159 return false;
160 }
161 groupNum = getgroups(groupNum, groups);
162 for (int i = 0; i < groupNum; i++) {
163 if (groups[i] == inetGroup) {
164 free(groups);
165 return true;
166 }
167 }
168 free(groups);
169 NETSTACK_LOGE("INTERNET permission denied by group");
170 return false;
171 #endif
172 }
173
IsAtomicService(std::string & bundleName)174 bool IsAtomicService(std::string &bundleName)
175 {
176 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
177 return BundleUtils::IsAtomicService(bundleName);
178 #else
179 return false;
180 #endif
181 }
182
IsAllowedHostname(const std::string & bundleName,const std::string & domainType,const std::string & url)183 bool IsAllowedHostname(const std::string &bundleName, const std::string &domainType, const std::string &url)
184 {
185 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) && !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
186 if (bundleName.empty()) {
187 NETSTACK_LOGE("isAllowedHostnameForAtomicService bundleName is empty");
188 return true;
189 }
190 auto hostname = GetHostnameWithProtocolAndPortFromURL(url);
191 if (hostname.empty()) {
192 NETSTACK_LOGE("isAllowedHostnameForAtomicService url hostname is empty");
193 return true;
194 }
195 return ApiPolicyUtils::IsAllowedHostname(bundleName, domainType, hostname);
196 #else
197 return true;
198 #endif
199 }
200
EndsWith(const std::string & str,const std::string & suffix)201 bool EndsWith(const std::string &str, const std::string &suffix)
202 {
203 if (str.length() < suffix.length()) {
204 return false;
205 }
206 return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0;
207 }
208
Trim(std::string str)209 std::string Trim(std::string str)
210 {
211 size_t start = str.find_first_not_of(" \t\n\r");
212 size_t end = str.find_last_not_of(" \t\n\r");
213 if (start == std::string::npos || end == std::string::npos) {
214 return "";
215 } else {
216 return str.substr(start, end - start + 1);
217 }
218 }
219
IsMatch(const std::string & str,const std::string & patternStr)220 bool IsMatch(const std::string &str, const std::string &patternStr)
221 {
222 if (patternStr.empty()) {
223 return false;
224 }
225 if (patternStr == "*") {
226 return true;
227 }
228 if (!IsRegexValid(patternStr)) {
229 NETSTACK_LOGD("Invalid pattern");
230 return patternStr == str;
231 }
232 std::regex pattern(ReplaceCharacters(patternStr));
233 bool isMacth = patternStr != "" && std::regex_match(str, pattern);
234 if (isMacth) {
235 NETSTACK_LOGD("Match patternStr");
236 }
237 return isMacth;
238 }
239
InsertCharBefore(const std::string & input,const char from,const char preChar,const char nextChar)240 std::string InsertCharBefore(const std::string &input, const char from, const char preChar, const char nextChar)
241 {
242 std::string output = input;
243 char arr[] = {preChar, from};
244 unsigned long strSize = sizeof(arr) / sizeof(arr[0]);
245 std::string str(arr, strSize);
246 std::size_t pos = output.find(from);
247 std::size_t length = output.length();
248 while (pos <= length - 1 && pos != std::string::npos) {
249 char nextCharTemp = pos == length - 1 ? '\0' : output[pos + 1];
250 if (nextChar == '\0' || nextCharTemp == '\0' || nextCharTemp != nextChar) {
251 output.replace(pos, 1, str);
252 length += (strSize - 1);
253 }
254 pos = output.find(from, pos + strSize);
255 }
256 return output;
257 }
258
ReplaceCharacters(const std::string & input)259 std::string ReplaceCharacters(const std::string &input)
260 {
261 std::string output = InsertCharBefore(input, '*', '.', '\0');
262 output = InsertCharBefore(output, '.', '\\', '*');
263 return output;
264 }
265
IsRegexValid(const std::string & regex)266 bool IsRegexValid(const std::string ®ex)
267 {
268 if (Trim(regex).empty()) {
269 return false;
270 }
271 return regex_match(regex, std::regex("^[a-zA-Z0-9\\-_\\.*]+$"));
272 }
273
GetProtocolFromURL(const std::string & url)274 std::string GetProtocolFromURL(const std::string &url)
275 {
276 std::string delimiter = "://";
277 size_t pos = url.find(delimiter);
278 if (pos != std::string::npos) {
279 return url.substr(0, pos);
280 }
281 return "";
282 }
283
GetPortFromURL(const std::string & url)284 std::string GetPortFromURL(const std::string &url)
285 {
286 std::string delimiter = "://";
287 std::string protocol = GetProtocolFromURL(url);
288 std::string hostname = GetHostnameFromURL(url);
289 size_t start = protocol.empty() ? hostname.size() : protocol.size() + delimiter.size() + hostname.size();
290 size_t posStart = url.find_first_of(':', start);
291 if (posStart == std::string::npos) {
292 return "";
293 }
294 size_t posEnd = std::min({url.find('/', start), url.find('?', start)});
295 if (posEnd == std::string::npos) {
296 return url.substr(posStart + 1);
297 }
298 if (posStart > posEnd) {
299 return "";
300 }
301 return url.substr(posStart + 1, posEnd - posStart - 1);
302 }
303
GetHostnameFromURL(const std::string & url)304 std::string GetHostnameFromURL(const std::string &url)
305 {
306 if (url.empty()) {
307 return "";
308 }
309 std::string delimiter = "://";
310 std::string tempUrl = url;
311 std::replace(tempUrl.begin(), tempUrl.end(), '\\', '/');
312 size_t posStart = tempUrl.find(delimiter);
313 if (posStart != std::string::npos) {
314 posStart += delimiter.length();
315 } else {
316 posStart = 0;
317 }
318 size_t notSlash = tempUrl.find_first_not_of('/', posStart);
319 if (notSlash != std::string::npos) {
320 posStart = notSlash;
321 }
322 size_t posEnd = std::min({ tempUrl.find(':', posStart),
323 tempUrl.find('/', posStart), tempUrl.find('?', posStart) });
324 if (posEnd != std::string::npos) {
325 return tempUrl.substr(posStart, posEnd - posStart);
326 }
327 return tempUrl.substr(posStart);
328 }
329
GetHostnameWithProtocolAndPortFromURL(const std::string & url)330 std::string GetHostnameWithProtocolAndPortFromURL(const std::string& url)
331 {
332 std::string delimiter = "://";
333 std::string portDelimiter = ":";
334 auto hostname = GetHostnameFromURL(url);
335 if (!hostname.empty()) {
336 std::string protocol = GetProtocolFromURL(url);
337 if (!protocol.empty()) {
338 hostname = protocol + delimiter + hostname;
339 }
340 if (protocol != PROTOCOL_WSS) {
341 std::string port = GetPortFromURL(url);
342 if (!port.empty()) {
343 hostname += portDelimiter + port;
344 }
345 }
346 }
347 return hostname;
348 }
349
IsExcluded(const std::string & str,const std::string & exclusions,const std::string & split)350 bool IsExcluded(const std::string &str, const std::string &exclusions, const std::string &split)
351 {
352 if (Trim(exclusions).empty()) {
353 return false;
354 }
355 std::size_t start = 0;
356 std::size_t end = exclusions.find(split);
357 while (end != std::string::npos) {
358 if (end - start > 0 && IsMatch(str, Trim(exclusions.substr(start, end - start)))) {
359 return true;
360 }
361 start = end + 1;
362 end = exclusions.find(split, start);
363 }
364 return IsMatch(str, Trim(exclusions.substr(start)));
365 }
366
IsHostNameExcluded(const std::string & url,const std::string & exclusions,const std::string & split)367 bool IsHostNameExcluded(const std::string &url, const std::string &exclusions, const std::string &split)
368 {
369 std::string hostName = GetHostnameFromURL(url);
370 return IsExcluded(hostName, exclusions, split);
371 }
372
IsValidIPV4(const std::string & ip)373 bool IsValidIPV4(const std::string &ip)
374 {
375 return IsValidIP(ip, AF_INET);
376 }
377
IsValidIPV6(const std::string & ip)378 bool IsValidIPV6(const std::string &ip)
379 {
380 return IsValidIP(ip, AF_INET6);
381 }
382
IsValidIP(const std::string & ip,int af)383 bool IsValidIP(const std::string& ip, int af)
384 {
385 if (ip.empty()) {
386 return false;
387 }
388 #ifdef WINDOWS_PLATFORM
389 if (af == AF_INET6) {
390 struct sockaddr_in6 sa;
391 return inet_pton(af, ip.c_str(), &(sa.sin6_addr)) == INET_OPTION_SUC;
392 } else {
393 struct sockaddr_in sa;
394 return inet_pton(af, ip.c_str(), &(sa.sin_addr)) == INET_OPTION_SUC;
395 }
396 #else
397 if (af == AF_INET6) {
398 struct in6_addr addr;
399 return inet_pton(af, ip.c_str(), reinterpret_cast<void *>(&addr)) == INET_OPTION_SUC;
400 } else {
401 struct in_addr addr;
402 return inet_pton(af, ip.c_str(), reinterpret_cast<void *>(&addr)) == INET_OPTION_SUC;
403 }
404 #endif
405 }
406
MaskIpv4(std::string & maskedResult)407 std::string MaskIpv4(std::string &maskedResult)
408 {
409 int maxDisplayNum = MAX_DISPLAY_NUM;
410 for (char &i : maskedResult) {
411 if (i == '/') {
412 break;
413 }
414 if (maxDisplayNum > 0) {
415 if (i == '.') {
416 maxDisplayNum--;
417 }
418 } else {
419 if (i != '.') {
420 i = '*';
421 }
422 }
423 }
424 return maskedResult;
425 }
426
MaskIpv6(std::string & maskedResult)427 std::string MaskIpv6(std::string &maskedResult)
428 {
429 size_t colonCount = 0;
430 for (char &i : maskedResult) {
431 if (i == '/') {
432 break;
433 }
434 if (i == ':') {
435 colonCount++;
436 }
437
438 if (colonCount >= MAX_DISPLAY_NUM) {
439 if (i != ':') {
440 i = '*';
441 }
442 }
443 }
444 return maskedResult;
445 }
446
AnonymizeIp(std::string & input)447 std::string AnonymizeIp(std::string &input)
448 {
449 if (input.empty()) {
450 return input;
451 }
452 std::lock_guard<std::mutex> lock(g_commonUtilsMutex);
453 std::string maskedResult{input};
454 if (std::regex_match(maskedResult, IP_PATTERN) || std::regex_match(maskedResult, IP_MASK_PATTERN)) {
455 return MaskIpv4(maskedResult);
456 }
457 if (std::regex_match(maskedResult, IPV6_PATTERN) || std::regex_match(maskedResult, IPV6_MASK_PATTERN)) {
458 return MaskIpv6(maskedResult);
459 }
460 return input;
461 }
462
GetFileDataFromFilePath(const std::string & filePath,std::string & fileData)463 bool GetFileDataFromFilePath(const std::string& filePath, std::string& fileData)
464 {
465 #if !defined(IOS_PLATFORM) && !defined(ANDROID_PLATFORM)
466 std::error_code error;
467 auto path = std::filesystem::absolute(filePath, error);
468 if (error) {
469 NETSTACK_LOGE("Failed to obtain the absolute path: %{public}s", error.message().c_str());
470 return false;
471 }
472 std::ifstream file(path);
473 #else
474 std::ifstream file(filePath);
475 #endif
476 if (file.is_open()) {
477 std::stringstream buffer;
478 buffer << file.rdbuf();
479 file.close();
480 fileData = buffer.str();
481 return true;
482 } else {
483 NETSTACK_LOGE("Failed to obtain the file data stream.");
484 return false;
485 }
486 }
487
IsCertPubKeyInPinned(const std::string & certPubKeyDigest,const std::string & pinnedPubkey)488 bool IsCertPubKeyInPinned(const std::string &certPubKeyDigest, const std::string &pinnedPubkey)
489 {
490 auto begin = pinnedPubkey.find("sha256//");
491 if (begin != 0) {
492 NETSTACK_LOGE("pinnedPubkey format invalid, should start with sha256//");
493 return false;
494 }
495 while (begin < pinnedPubkey.size()) {
496 auto end = pinnedPubkey.find(";", begin);
497 if (end == std::string::npos) {
498 end = pinnedPubkey.size();
499 }
500 if (pinnedPubkey.find("sha256//", begin) != begin) {
501 NETSTACK_LOGE("pinnedPubkey format invalid, should be like sha256//[hash1];sha256//[hash2]");
502 begin = end + 1;
503 continue;
504 }
505 if (end - begin != PINNED_PREFIX_LEN + SHA256_BASE64_LEN) {
506 NETSTACK_LOGE("pinnedPubkey format invalid, hash length not match");
507 begin = end + 1;
508 continue;
509 }
510 std::string candidate = pinnedPubkey.substr(begin + PINNED_PREFIX_LEN, SHA256_BASE64_LEN);
511 if (candidate == certPubKeyDigest) {
512 return true;
513 }
514 begin = end + 1;
515 }
516 return false;
517 }
518 } // namespace OHOS::NetStack::CommonUtils