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