1 /*
2 * Copyright (c) 2023-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 "sandbox_helper.h"
17
18 #include <dlfcn.h>
19 #include <iomanip>
20 #include <sstream>
21 #include <unordered_set>
22 #include <vector>
23 #include "log.h"
24 #include "json_utils.h"
25 #include "uri.h"
26
27 using namespace std;
28
29 namespace OHOS {
30 namespace AppFileService {
31 typedef void (*ConvertFileUriToMntPath)(const std::vector<std::string> &fileUris,
32 std::vector<std::string> &physicalPaths);
33 namespace {
34 const string PACKAGE_NAME_FLAG = "<PackageName>";
35 const string CURRENT_USER_ID_FLAG = "<currentUserId>";
36 const string PHYSICAL_PATH_KEY = "src-path";
37 const string SANDBOX_PATH_KEY = "sandbox-path";
38 const string MOUNT_PATH_MAP_KEY = "mount-path-map";
39 const string SANDBOX_JSON_FILE_PATH = "/etc/app_file_service/file_share_sandbox.json";
40 const string BACKUP_SANDBOX_JSON_FILE_PATH = "/etc/app_file_service/backup_sandbox.json";
41 const std::string SHARE_PATH_HEAD = "/mnt/hmdfs/";
42 const std::string SHARE_PATH_MID = "/account/cloud_merge_view/files/";
43 const string FILE_MANAGER_URI_HEAD = "/storage/";
44 const string FILE_MANAGER_AUTHORITY = "docs";
45 const string DLP_MANAGER_BUNDLE_NAME = "com.ohos.dlpmanager";
46 const string FUSE_URI_HEAD = "/mnt/data/fuse";
47 const char BACKSLASH = '/';
48 const string MEDIA = "media";
49 const string NETWORK_ID_FLAG = "<networkId>";
50 const string LOCAL = "local";
51 const int ASSET_IN_BUCKET_NUM_MAX = 1000;
52 const int ASSET_DIR_START_NUM = 16;
53 const int DECODE_FORMAT_NUM = 16;
54 const std::string PATH_INVALID_FLAG1 = "../";
55 const std::string PATH_INVALID_FLAG2 = "/..";
56 const uint32_t PATH_INVALID_FLAG_LEN = 3;
57 const std::string NETWORK_PARA = "?networkid=";
58 const std::string AMPERSAND = "&";
59 }
60
61 namespace {
62 std::unordered_map<std::string, std::string> g_sandboxPathMap;
63 std::unordered_map<std::string, std::string> g_backupSandboxPathMap;
64 }
65
66 struct MediaUriInfo {
67 string mediaType;
68 string fileId;
69 string realName;
70 string displayName;
71 };
72
73 std::mutex SandboxHelper::mapMutex_;
74 void* SandboxHelper::libMediaHandle_;
75
Encode(const string & uri)76 string SandboxHelper::Encode(const string &uri)
77 {
78 const unordered_set<char> uriCompentsSet = {
79 '/', '-', '_', '.', '!',
80 '~', '*', '(', ')', '\''
81 };
82 const int32_t encodeLen = 2;
83 ostringstream outPutStream;
84 outPutStream.fill('0');
85 outPutStream << std::hex;
86
87 for (unsigned char tmpChar : uri) {
88 if (std::isalnum(tmpChar) || uriCompentsSet.find(tmpChar) != uriCompentsSet.end()) {
89 outPutStream << tmpChar;
90 } else {
91 outPutStream << std::uppercase;
92 outPutStream << '%' << std::setw(encodeLen) << static_cast<unsigned int>(tmpChar);
93 outPutStream << std::nouppercase;
94 }
95 }
96
97 return outPutStream.str();
98 }
99
Decode(const string & uri)100 string SandboxHelper::Decode(const string &uri)
101 {
102 std::string outPutStr;
103 const int32_t encodeLen = 2;
104 size_t index = 0;
105 while (index < uri.length()) {
106 if (uri[index] == '%') {
107 std::string inputStr(uri.substr(index + 1, encodeLen));
108 outPutStr += static_cast<char>(strtol(inputStr.c_str(), nullptr, DECODE_FORMAT_NUM));
109 index += encodeLen + 1;
110 } else {
111 outPutStr += uri[index];
112 index++;
113 }
114 }
115
116 return outPutStr;
117 }
118
GetLowerPath(string & lowerPathHead,const string & lowerPathTail,const string & userId,const string & bundleName,const string & networkId)119 static string GetLowerPath(string &lowerPathHead, const string &lowerPathTail,
120 const string &userId, const string &bundleName,
121 const string &networkId)
122 {
123 if (lowerPathHead.find(CURRENT_USER_ID_FLAG) != string::npos) {
124 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(CURRENT_USER_ID_FLAG),
125 CURRENT_USER_ID_FLAG.length(), userId);
126 }
127
128 if (lowerPathHead.find(PACKAGE_NAME_FLAG) != string::npos) {
129 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(PACKAGE_NAME_FLAG),
130 PACKAGE_NAME_FLAG.length(), bundleName);
131 }
132
133 if (lowerPathHead.find(NETWORK_ID_FLAG) != string::npos) {
134 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(NETWORK_ID_FLAG),
135 NETWORK_ID_FLAG.length(), networkId);
136 }
137
138 return lowerPathHead + lowerPathTail;
139 }
140
GetLowerDir(string & lowerPathHead,const string & userId,const string & bundleName,const string & networkId)141 string SandboxHelper::GetLowerDir(string &lowerPathHead, const string &userId, const string &bundleName,
142 const string &networkId)
143 {
144 if (lowerPathHead.find(CURRENT_USER_ID_FLAG) != string::npos) {
145 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(CURRENT_USER_ID_FLAG),
146 CURRENT_USER_ID_FLAG.length(), userId);
147 }
148
149 if (lowerPathHead.find(PACKAGE_NAME_FLAG) != string::npos) {
150 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(PACKAGE_NAME_FLAG),
151 PACKAGE_NAME_FLAG.length(), bundleName);
152 }
153
154 if (lowerPathHead.find(NETWORK_ID_FLAG) != string::npos) {
155 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(NETWORK_ID_FLAG),
156 NETWORK_ID_FLAG.length(), networkId);
157 }
158 return lowerPathHead;
159 }
160
GetSandboxPathMap()161 bool SandboxHelper::GetSandboxPathMap()
162 {
163 lock_guard<mutex> lock(mapMutex_);
164 if (g_sandboxPathMap.size() > 0) {
165 return true;
166 }
167
168 nlohmann::json jsonObj;
169 int ret = JsonUtils::GetJsonObjFromPath(jsonObj, SANDBOX_JSON_FILE_PATH);
170 if (ret != 0) {
171 LOGE("Get json object failed with %{public}d", ret);
172 return false;
173 }
174
175 if (jsonObj.find(MOUNT_PATH_MAP_KEY) == jsonObj.end()) {
176 LOGE("Json object find mount path map failed");
177 return false;
178 }
179
180 nlohmann::json mountPathMap = jsonObj[MOUNT_PATH_MAP_KEY];
181 for (size_t i = 0; i < mountPathMap.size(); i++) {
182 string srcPath = mountPathMap[i][PHYSICAL_PATH_KEY];
183 string sandboxPath = mountPathMap[i][SANDBOX_PATH_KEY];
184 g_sandboxPathMap[sandboxPath] = srcPath;
185 }
186
187 if (g_sandboxPathMap.size() == 0) {
188 return false;
189 }
190
191 return true;
192 }
193
GetBackupSandboxPathMap()194 bool SandboxHelper::GetBackupSandboxPathMap()
195 {
196 lock_guard<mutex> lock(mapMutex_);
197 if (g_backupSandboxPathMap.size() > 0) {
198 return true;
199 }
200
201 nlohmann::json jsonObj;
202 int ret = JsonUtils::GetJsonObjFromPath(jsonObj, BACKUP_SANDBOX_JSON_FILE_PATH);
203 if (ret != 0) {
204 LOGE("Get json object failed with %{public}d", ret);
205 return false;
206 }
207
208 if (jsonObj.find(MOUNT_PATH_MAP_KEY) == jsonObj.end()) {
209 LOGE("Json object find mount path map failed");
210 return false;
211 }
212
213 nlohmann::json mountPathMap = jsonObj[MOUNT_PATH_MAP_KEY];
214 for (size_t i = 0; i < mountPathMap.size(); i++) {
215 string srcPath = mountPathMap[i][PHYSICAL_PATH_KEY];
216 string sandboxPath = mountPathMap[i][SANDBOX_PATH_KEY];
217 g_backupSandboxPathMap[sandboxPath] = srcPath;
218 }
219
220 if (g_backupSandboxPathMap.size() == 0) {
221 return false;
222 }
223
224 return true;
225 }
226
GetPathSuffix(const std::string & path,string & pathSuffix)227 static int32_t GetPathSuffix(const std::string &path, string &pathSuffix)
228 {
229 size_t pos = path.rfind('.');
230 if (pos != string::npos) {
231 pathSuffix = path.substr(pos);
232 return 0;
233 }
234 return -EINVAL;
235 }
236
CalAssetBucket(const int32_t & fileId)237 static int32_t CalAssetBucket(const int32_t &fileId)
238 {
239 int32_t bucketNum = 0;
240 if (fileId < 0) {
241 LOGE("input fileId %{private}d is invalid", fileId);
242 return -EINVAL;
243 }
244
245 int32_t start = ASSET_DIR_START_NUM;
246 int32_t divider = ASSET_DIR_START_NUM;
247 while (fileId > start * ASSET_IN_BUCKET_NUM_MAX) {
248 divider = start;
249 start <<= 1;
250 }
251
252 int32_t fileIdRemainder = fileId % divider;
253 if (fileIdRemainder == 0) {
254 bucketNum = start + fileIdRemainder;
255 } else {
256 bucketNum = (start - divider) + fileIdRemainder;
257 }
258 return bucketNum;
259 }
260
GetFileIdFromFileName(const std::string & fileName)261 static int32_t GetFileIdFromFileName(const std::string &fileName)
262 {
263 if (fileName.empty()) {
264 return -EINVAL;
265 }
266
267 string tmpName = fileName;
268 std::replace(tmpName.begin(), tmpName.end(), '_', ' ');
269 stringstream ss;
270 ss << tmpName;
271
272 string mediaType;
273 string dateTime;
274 string idStr;
275 string other;
276 ss >> mediaType >> dateTime >> idStr >> other;
277 if (idStr.empty()) {
278 return -EINVAL;
279 }
280
281 if (!std::all_of(idStr.begin(), idStr.end(), ::isdigit)) {
282 return -EINVAL;
283 }
284
285 return std::atoi(idStr.c_str());
286 }
287
GetBucketNum(const std::string & fileName)288 static int32_t GetBucketNum(const std::string &fileName)
289 {
290 int32_t fileId = GetFileIdFromFileName(fileName);
291 if (fileId < 0) {
292 LOGE("GetFileIdFromFileName failed with %{private}s", fileName.c_str());
293 return fileId;
294 }
295 return CalAssetBucket(fileId);
296 }
297
ParseMediaSandboxPath(const string & sandboxPath,MediaUriInfo & mediaUriInfo)298 static bool ParseMediaSandboxPath(const string &sandboxPath, MediaUriInfo &mediaUriInfo)
299 {
300 string path = sandboxPath;
301 std::replace(path.begin(), path.end(), '/', ' ');
302 stringstream ss;
303 ss << path;
304 ss >> mediaUriInfo.mediaType >> mediaUriInfo.fileId >> mediaUriInfo.realName >> mediaUriInfo.displayName;
305
306 string buf;
307 ss >> buf;
308 if (!buf.empty()) {
309 LOGE("media sandboxPath is invalid");
310 return false;
311 }
312
313 return true;
314 }
315
GetMediaPhysicalPath(const std::string & sandboxPath,const std::string & userId,std::string & physicalPath)316 static int32_t GetMediaPhysicalPath(const std::string &sandboxPath, const std::string &userId,
317 std::string &physicalPath)
318 {
319 MediaUriInfo mediaUriInfo;
320 if (!ParseMediaSandboxPath(sandboxPath, mediaUriInfo)) {
321 return -EINVAL;
322 }
323
324 int32_t bucketNum = GetBucketNum(mediaUriInfo.realName);
325 if (bucketNum < 0) {
326 return -EINVAL;
327 }
328
329 std::string mediaSuffix;
330 if (GetPathSuffix(sandboxPath, mediaSuffix) != 0) {
331 LOGE("GetPathSuffix failed");
332 return -EINVAL;
333 }
334
335 physicalPath = SHARE_PATH_HEAD + userId + SHARE_PATH_MID + mediaUriInfo.mediaType +
336 BACKSLASH + to_string(bucketNum) + BACKSLASH + mediaUriInfo.realName + mediaSuffix;
337 return 0;
338 }
339
GetMediaSharePath(const std::vector<std::string> & fileUris,std::vector<std::string> & physicalPaths)340 int32_t SandboxHelper::GetMediaSharePath(const std::vector<std::string> &fileUris,
341 std::vector<std::string> &physicalPaths)
342 {
343 if (libMediaHandle_ == nullptr) {
344 libMediaHandle_ = dlopen("libmedia_library_handler.z.so", RTLD_LAZY | RTLD_GLOBAL);
345 }
346 if (libMediaHandle_ == nullptr) {
347 LOGE("dlopen libmedia_library_handler.z.so failed, errno = %{public}s", dlerror());
348 return -EINVAL;
349 }
350 ConvertFileUriToMntPath convertFileUriToMntPath =
351 (ConvertFileUriToMntPath)dlsym(libMediaHandle_, "ConvertFileUriToMntPath");
352 if (convertFileUriToMntPath == nullptr) {
353 LOGE("GetMediaSharePath dlsym failed, errno %{public}s", dlerror());
354 return -EINVAL;
355 }
356 convertFileUriToMntPath(fileUris, physicalPaths);
357 if (fileUris.size() != physicalPaths.size()) {
358 LOGE("GetMediaSharePath returns fewer results than the output parameters");
359 return -EINVAL;
360 }
361 return 0;
362 }
363
GetNetworkIdFromUri(const std::string & fileUri,std::string & networkId)364 void SandboxHelper::GetNetworkIdFromUri(const std::string &fileUri, std::string &networkId)
365 {
366 std::string uri = fileUri;
367 size_t pos = uri.find(NETWORK_PARA);
368 if (pos != string::npos && pos > 0 && pos < uri.size() - NETWORK_PARA.size()) {
369 if (uri.substr(pos + NETWORK_PARA.size()).find(BACKSLASH) == string::npos) {
370 size_t endPos = uri.substr(pos + NETWORK_PARA.size()).find(AMPERSAND);
371 if (endPos != string::npos) {
372 networkId = uri.substr(pos + NETWORK_PARA.size(), endPos);
373 } else {
374 networkId = uri.substr(pos + NETWORK_PARA.size());
375 }
376 }
377 }
378 }
379
DoGetPhysicalPath(string & lowerPathTail,string & lowerPathHead,const string & sandboxPath,std::unordered_map<std::string,std::string> & sandboxPathMap)380 static void DoGetPhysicalPath(string &lowerPathTail, string &lowerPathHead, const string &sandboxPath,
381 std::unordered_map<std::string, std::string> &sandboxPathMap)
382 {
383 string::size_type curPrefixMatchLen = 0;
384 for (auto it = sandboxPathMap.begin(); it != sandboxPathMap.end(); it++) {
385 string sandboxPathPrefix = it->first;
386 string::size_type prefixMatchLen = sandboxPathPrefix.length();
387 if (sandboxPath.length() >= prefixMatchLen) {
388 string sandboxPathTemp = sandboxPath.substr(0, prefixMatchLen);
389 if (sandboxPathTemp == sandboxPathPrefix && curPrefixMatchLen <= prefixMatchLen) {
390 curPrefixMatchLen = prefixMatchLen;
391 lowerPathHead = it->second;
392 lowerPathTail = sandboxPath.substr(prefixMatchLen);
393 }
394 }
395 }
396 }
397
GetPhysicalDir(const std::string & fileUri,const std::string & userId,std::string & physicalDir)398 int32_t SandboxHelper::GetPhysicalDir(const std::string &fileUri, const std::string &userId,
399 std::string &physicalDir)
400 {
401 if (!IsValidPath(fileUri)) {
402 LOGE("fileUri is ValidUri, The fileUri contains '/./' or '../'characters");
403 return -EINVAL;
404 }
405 Uri uri(fileUri);
406 string bundleName = uri.GetAuthority();
407 if (bundleName == MEDIA) {
408 LOGE("Media not support.");
409 return -EINVAL;
410 }
411
412 string sandboxPath = SandboxHelper::Decode(uri.GetPath());
413 if ((sandboxPath.find(FILE_MANAGER_URI_HEAD) == 0 && bundleName != FILE_MANAGER_AUTHORITY) ||
414 (sandboxPath.find(FUSE_URI_HEAD) == 0 && bundleName != DLP_MANAGER_BUNDLE_NAME)) {
415 return -EINVAL;
416 }
417
418 if (!GetSandboxPathMap()) {
419 LOGE("GetSandboxPathMap failed");
420 return -EINVAL;
421 }
422
423 string lowerPathTail = "";
424 string lowerPathHead = "";
425 DoGetPhysicalPath(lowerPathTail, lowerPathHead, sandboxPath, g_sandboxPathMap);
426
427 if (lowerPathHead == "") {
428 LOGE("lowerPathHead is invalid");
429 return -EINVAL;
430 }
431
432 string networkId = LOCAL;
433 GetNetworkIdFromUri(fileUri, networkId);
434
435 physicalDir = GetLowerDir(lowerPathHead, userId, bundleName, networkId);
436 return 0;
437 }
438
GetPhysicalPath(const std::string & fileUri,const std::string & userId,std::string & physicalPath)439 int32_t SandboxHelper::GetPhysicalPath(const std::string &fileUri, const std::string &userId,
440 std::string &physicalPath)
441 {
442 if (!IsValidPath(fileUri)) {
443 LOGE("fileUri is ValidUri, The fileUri contains '../' characters");
444 return -EINVAL;
445 }
446 Uri uri(fileUri);
447 string bundleName = uri.GetAuthority();
448 if (bundleName == MEDIA) {
449 return GetMediaPhysicalPath(uri.GetPath(), userId, physicalPath);
450 }
451
452 string sandboxPath = SandboxHelper::Decode(uri.GetPath());
453 if ((sandboxPath.find(FILE_MANAGER_URI_HEAD) == 0 && bundleName != FILE_MANAGER_AUTHORITY) ||
454 (sandboxPath.find(FUSE_URI_HEAD) == 0 && bundleName != DLP_MANAGER_BUNDLE_NAME)) {
455 return -EINVAL;
456 }
457
458 if (!GetSandboxPathMap()) {
459 LOGE("GetSandboxPathMap failed");
460 return -EINVAL;
461 }
462
463 string lowerPathTail = "";
464 string lowerPathHead = "";
465 DoGetPhysicalPath(lowerPathTail, lowerPathHead, sandboxPath, g_sandboxPathMap);
466
467 if (lowerPathHead == "") {
468 LOGE("lowerPathHead is invalid");
469 return -EINVAL;
470 }
471
472 string networkId = LOCAL;
473 GetNetworkIdFromUri(fileUri, networkId);
474
475 physicalPath = GetLowerPath(lowerPathHead, lowerPathTail, userId, bundleName, networkId);
476 return 0;
477 }
478
GetBackupPhysicalPath(const std::string & fileUri,const std::string & userId,std::string & physicalPath)479 int32_t SandboxHelper::GetBackupPhysicalPath(const std::string &fileUri, const std::string &userId,
480 std::string &physicalPath)
481 {
482 if (!IsValidPath(fileUri)) {
483 LOGE("fileUri is ValidUri, The fileUri contains '../' characters");
484 return -EINVAL;
485 }
486 Uri uri(fileUri);
487 string bundleName = uri.GetAuthority();
488 if (bundleName == MEDIA) {
489 return GetMediaPhysicalPath(uri.GetPath(), userId, physicalPath);
490 }
491
492 string sandboxPath = SandboxHelper::Decode(uri.GetPath());
493 if ((sandboxPath.find(FILE_MANAGER_URI_HEAD) == 0 && bundleName != FILE_MANAGER_AUTHORITY) ||
494 (sandboxPath.find(FUSE_URI_HEAD) == 0 && bundleName != DLP_MANAGER_BUNDLE_NAME)) {
495 return -EINVAL;
496 }
497
498 if (!GetBackupSandboxPathMap()) {
499 LOGE("GetBackupSandboxPathMap failed");
500 return -EINVAL;
501 }
502
503 string lowerPathTail = "";
504 string lowerPathHead = "";
505 DoGetPhysicalPath(lowerPathTail, lowerPathHead, sandboxPath, g_backupSandboxPathMap);
506
507 if (lowerPathHead == "") {
508 LOGE("lowerPathHead is invalid");
509 return -EINVAL;
510 }
511
512 string networkId = LOCAL;
513 GetNetworkIdFromUri(fileUri, networkId);
514
515 physicalPath = GetLowerPath(lowerPathHead, lowerPathTail, userId, bundleName, networkId);
516 return 0;
517 }
518
IsValidPath(const std::string & filePath)519 bool SandboxHelper::IsValidPath(const std::string &filePath)
520 {
521 size_t pos = filePath.find(PATH_INVALID_FLAG1);
522 while (pos != string::npos) {
523 if (pos == 0 || filePath[pos - 1] == BACKSLASH) {
524 LOGE("Relative path is not allowed, path contain ../");
525 return false;
526 }
527 pos = filePath.find(PATH_INVALID_FLAG1, pos + PATH_INVALID_FLAG_LEN);
528 }
529 pos = filePath.rfind(PATH_INVALID_FLAG2);
530 if ((pos != string::npos) && (filePath.size() - pos == PATH_INVALID_FLAG_LEN)) {
531 LOGE("Relative path is not allowed, path tail is /..");
532 return false;
533 }
534 return true;
535 }
536
CheckValidPath(const std::string & filePath)537 bool SandboxHelper::CheckValidPath(const std::string &filePath)
538 {
539 if (filePath.empty() || filePath.size() >= PATH_MAX) {
540 return false;
541 }
542
543 char realPath[PATH_MAX]{'\0'};
544 if (realpath(filePath.c_str(), realPath) == nullptr) {
545 LOGE("realpath failed with errno = %{public}d, filePath = %{private}s", errno, filePath.c_str());
546 return false;
547 }
548
549 if (strncmp(realPath, filePath.c_str(), filePath.size()) != 0) {
550 LOGE("filePath is not equal to realPath, realPath.size = %{public}zu, filePath.size() = %{public}zu",
551 string_view(realPath).size(), filePath.size());
552 return false;
553 }
554
555 return true;
556 }
557 } // namespace AppFileService
558 } // namespace OHOS
559
560