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 <iomanip>
19 #include <sstream>
20 #include <unordered_set>
21 #include <vector>
22
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 namespace {
32 const string PACKAGE_NAME_FLAG = "<PackageName>";
33 const string CURRENT_USER_ID_FLAG = "<currentUserId>";
34 const string PHYSICAL_PATH_KEY = "src-path";
35 const string SANDBOX_PATH_KEY = "sandbox-path";
36 const string MOUNT_PATH_MAP_KEY = "mount-path-map";
37 const string SANDBOX_JSON_FILE_PATH = "/etc/app_file_service/file_share_sandbox.json";
38 const string BACKUP_SANDBOX_JSON_FILE_PATH = "/etc/app_file_service/backup_sandbox.json";
39 const std::string SHAER_PATH_HEAD = "/mnt/hmdfs/";
40 const std::string SHAER_PATH_MID = "/account/cloud_merge_view/files/";
41 const string FILE_MANAGER_URI_HEAD = "/storage/";
42 const string FILE_MANAGER_AUTHORITY = "docs";
43 const string DLP_MANAGER_BUNDLE_NAME = "com.ohos.dlpmanager";
44 const string FUSE_URI_HEAD = "/mnt/data/fuse";
45 const string BACKFLASH = "/";
46 const string MEDIA = "media";
47 const string NETWORK_ID_FLAG = "<networkId>";
48 const string LOCAL = "local";
49 const int ASSET_IN_BUCKET_NUM_MAX = 1000;
50 const int ASSET_DIR_START_NUM = 16;
51 const int DECODE_FORMAT_NUM = 16;
52 }
53
54 struct MediaUriInfo {
55 string mediaType;
56 string fileId;
57 string realName;
58 string displayName;
59 };
60
61 std::unordered_map<std::string, std::string> SandboxHelper::sandboxPathMap_;
62 std::unordered_map<std::string, std::string> SandboxHelper::backupSandboxPathMap_;
63 std::mutex SandboxHelper::mapMutex_;
64
Encode(const string & uri)65 string SandboxHelper::Encode(const string &uri)
66 {
67 const unordered_set<char> uriCompentsSet = {
68 '/', '-', '_', '.', '!',
69 '~', '*', '(', ')', '\''
70 };
71 const int32_t encodeLen = 2;
72 ostringstream outPutStream;
73 outPutStream.fill('0');
74 outPutStream << std::hex;
75
76 for (unsigned char tmpChar : uri) {
77 if (std::isalnum(tmpChar) || uriCompentsSet.find(tmpChar) != uriCompentsSet.end()) {
78 outPutStream << tmpChar;
79 } else {
80 outPutStream << std::uppercase;
81 outPutStream << '%' << std::setw(encodeLen) << static_cast<unsigned int>(tmpChar);
82 outPutStream << std::nouppercase;
83 }
84 }
85
86 return outPutStream.str();
87 }
88
Decode(const string & uri)89 string SandboxHelper::Decode(const string &uri)
90 {
91 std::string outPutStr;
92 const int32_t encodeLen = 2;
93 size_t index = 0;
94 while (index < uri.length()) {
95 if (uri[index] == '%') {
96 std::string inputStr(uri.substr(index + 1, encodeLen));
97 outPutStr += static_cast<char>(strtol(inputStr.c_str(), nullptr, DECODE_FORMAT_NUM));
98 index += encodeLen + 1;
99 } else {
100 outPutStr += uri[index];
101 index++;
102 }
103 }
104
105 return outPutStr;
106 }
107
GetLowerPath(string & lowerPathHead,const string & lowerPathTail,const string & userId,const string & bundleName,const string & networkId)108 static string GetLowerPath(string &lowerPathHead, const string &lowerPathTail,
109 const string &userId, const string &bundleName,
110 const string &networkId)
111 {
112 if (lowerPathHead.find(CURRENT_USER_ID_FLAG) != string::npos) {
113 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(CURRENT_USER_ID_FLAG),
114 CURRENT_USER_ID_FLAG.length(), userId);
115 }
116
117 if (lowerPathHead.find(PACKAGE_NAME_FLAG) != string::npos) {
118 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(PACKAGE_NAME_FLAG),
119 PACKAGE_NAME_FLAG.length(), bundleName);
120 }
121
122 if (lowerPathHead.find(NETWORK_ID_FLAG) != string::npos) {
123 lowerPathHead = lowerPathHead.replace(lowerPathHead.find(NETWORK_ID_FLAG),
124 NETWORK_ID_FLAG.length(), networkId);
125 }
126
127 return lowerPathHead + lowerPathTail;
128 }
129
GetSandboxPathMap()130 bool SandboxHelper::GetSandboxPathMap()
131 {
132 lock_guard<mutex> lock(mapMutex_);
133 if (sandboxPathMap_.size() > 0) {
134 return true;
135 }
136
137 nlohmann::json jsonObj;
138 int ret = JsonUtils::GetJsonObjFromPath(jsonObj, SANDBOX_JSON_FILE_PATH);
139 if (ret != 0) {
140 LOGE("Get json object failed with %{public}d", ret);
141 return false;
142 }
143
144 if (jsonObj.find(MOUNT_PATH_MAP_KEY) == jsonObj.end()) {
145 LOGE("Json object find mount path map failed");
146 return false;
147 }
148
149 nlohmann::json mountPathMap = jsonObj[MOUNT_PATH_MAP_KEY];
150 for (size_t i = 0; i < mountPathMap.size(); i++) {
151 string srcPath = mountPathMap[i][PHYSICAL_PATH_KEY];
152 string sandboxPath = mountPathMap[i][SANDBOX_PATH_KEY];
153 sandboxPathMap_[sandboxPath] = srcPath;
154 }
155
156 if (sandboxPathMap_.size() == 0) {
157 return false;
158 }
159
160 return true;
161 }
162
GetBackupSandboxPathMap()163 bool SandboxHelper::GetBackupSandboxPathMap()
164 {
165 lock_guard<mutex> lock(mapMutex_);
166 if (backupSandboxPathMap_.size() > 0) {
167 return true;
168 }
169
170 nlohmann::json jsonObj;
171 int ret = JsonUtils::GetJsonObjFromPath(jsonObj, BACKUP_SANDBOX_JSON_FILE_PATH);
172 if (ret != 0) {
173 LOGE("Get json object failed with %{public}d", ret);
174 return false;
175 }
176
177 if (jsonObj.find(MOUNT_PATH_MAP_KEY) == jsonObj.end()) {
178 LOGE("Json object find mount path map failed");
179 return false;
180 }
181
182 nlohmann::json mountPathMap = jsonObj[MOUNT_PATH_MAP_KEY];
183 for (size_t i = 0; i < mountPathMap.size(); i++) {
184 string srcPath = mountPathMap[i][PHYSICAL_PATH_KEY];
185 string sandboxPath = mountPathMap[i][SANDBOX_PATH_KEY];
186 backupSandboxPathMap_[sandboxPath] = srcPath;
187 }
188
189 if (backupSandboxPathMap_.size() == 0) {
190 return false;
191 }
192
193 return true;
194 }
195
GetPathSuffix(const std::string & path,string & pathSuffix)196 static int32_t GetPathSuffix(const std::string &path, string &pathSuffix)
197 {
198 size_t pos = path.rfind('.');
199 if (pos != string::npos) {
200 pathSuffix = path.substr(pos);
201 return 0;
202 }
203 return -EINVAL;
204 }
205
CalAssetBucket(const int32_t & fileId)206 static int32_t CalAssetBucket(const int32_t &fileId)
207 {
208 int32_t bucketNum = 0;
209 if (fileId < 0) {
210 LOGE("input fileId %{private}d is invalid", fileId);
211 return -EINVAL;
212 }
213
214 int32_t start = ASSET_DIR_START_NUM;
215 int32_t divider = ASSET_DIR_START_NUM;
216 while (fileId > start * ASSET_IN_BUCKET_NUM_MAX) {
217 divider = start;
218 start <<= 1;
219 }
220
221 int32_t fileIdRemainder = fileId % divider;
222 if (fileIdRemainder == 0) {
223 bucketNum = start + fileIdRemainder;
224 } else {
225 bucketNum = (start - divider) + fileIdRemainder;
226 }
227 return bucketNum;
228 }
229
GetFileIdFromFileName(const std::string & fileName)230 static int32_t GetFileIdFromFileName(const std::string &fileName)
231 {
232 if (fileName.empty()) {
233 return -EINVAL;
234 }
235
236 string tmpName = fileName;
237 std::replace(tmpName.begin(), tmpName.end(), '_', ' ');
238 stringstream ss;
239 ss << tmpName;
240
241 string mediaType;
242 string dateTime;
243 string idStr;
244 string other;
245 ss >> mediaType >> dateTime >> idStr >> other;
246 if (idStr.empty()) {
247 return -EINVAL;
248 }
249
250 if (!std::all_of(idStr.begin(), idStr.end(), ::isdigit)) {
251 return -EINVAL;
252 }
253
254 return std::stoi(idStr);
255 }
256
GetBucketNum(const std::string & fileName)257 static int32_t GetBucketNum(const std::string &fileName)
258 {
259 int32_t fileId = GetFileIdFromFileName(fileName);
260 if (fileId < 0) {
261 LOGE("GetFileIdFromFileName failed with %{public}s", fileName.c_str());
262 return fileId;
263 }
264 return CalAssetBucket(fileId);
265 }
266
ParseMediaSandboxPath(const string & sandboxPath,MediaUriInfo & mediaUriInfo)267 static bool ParseMediaSandboxPath(const string &sandboxPath, MediaUriInfo &mediaUriInfo)
268 {
269 string path = sandboxPath;
270 std::replace(path.begin(), path.end(), '/', ' ');
271 stringstream ss;
272 ss << path;
273 ss >> mediaUriInfo.mediaType >> mediaUriInfo.fileId >> mediaUriInfo.realName >> mediaUriInfo.displayName;
274
275 string buf;
276 ss >> buf;
277 if (!buf.empty()) {
278 LOGE("media sandboxPath is invalid");
279 return false;
280 }
281
282 return true;
283 }
284
GetMediaPhysicalPath(const std::string & sandboxPath,const std::string & userId,std::string & physicalPath)285 static int32_t GetMediaPhysicalPath(const std::string &sandboxPath, const std::string &userId,
286 std::string &physicalPath)
287 {
288 MediaUriInfo mediaUriInfo;
289 if (!ParseMediaSandboxPath(sandboxPath, mediaUriInfo)) {
290 return -EINVAL;
291 }
292
293 int32_t bucketNum = GetBucketNum(mediaUriInfo.realName);
294 if (bucketNum < 0) {
295 return -EINVAL;
296 }
297
298 std::string mediaSuffix;
299 if (GetPathSuffix(sandboxPath, mediaSuffix) != 0) {
300 LOGE("GetPathSuffix failed");
301 return -EINVAL;
302 }
303
304 physicalPath = SHAER_PATH_HEAD + userId + SHAER_PATH_MID + mediaUriInfo.mediaType +
305 BACKFLASH + to_string(bucketNum) + BACKFLASH + mediaUriInfo.realName + mediaSuffix;
306 return 0;
307 }
308
GetNetworkIdFromUri(const std::string & fileUri,string & networkId)309 static void GetNetworkIdFromUri(const std::string &fileUri, string &networkId)
310 {
311 Uri uri(fileUri);
312 std::string networkIdInfo = uri.GetQuery();
313 if (networkIdInfo.empty()) {
314 return;
315 }
316
317 size_t posIndex = networkIdInfo.find('=');
318 if (posIndex == string::npos || posIndex == (networkIdInfo.size() - 1)) {
319 return;
320 }
321 networkId = networkIdInfo.substr(posIndex + 1);
322 }
323
DoGetPhysicalPath(string & lowerPathTail,string & lowerPathHead,const string & sandboxPath,std::unordered_map<std::string,std::string> & sandboxPathMap)324 static void DoGetPhysicalPath(string &lowerPathTail, string &lowerPathHead, const string &sandboxPath,
325 std::unordered_map<std::string, std::string> &sandboxPathMap)
326 {
327 string::size_type curPrefixMatchLen = 0;
328 for (auto it = sandboxPathMap.begin(); it != sandboxPathMap.end(); it++) {
329 string sandboxPathPrefix = it->first;
330 string::size_type prefixMatchLen = sandboxPathPrefix.length();
331 if (sandboxPath.length() >= prefixMatchLen) {
332 string sandboxPathTemp = sandboxPath.substr(0, prefixMatchLen);
333 if (sandboxPathTemp == sandboxPathPrefix && curPrefixMatchLen <= prefixMatchLen) {
334 curPrefixMatchLen = prefixMatchLen;
335 lowerPathHead = it->second;
336 lowerPathTail = sandboxPath.substr(prefixMatchLen);
337 }
338 }
339 }
340 }
341
GetPhysicalPath(const std::string & fileUri,const std::string & userId,std::string & physicalPath)342 int32_t SandboxHelper::GetPhysicalPath(const std::string &fileUri, const std::string &userId,
343 std::string &physicalPath)
344 {
345 Uri uri(fileUri);
346 string bundleName = uri.GetAuthority();
347 if (bundleName == MEDIA) {
348 return GetMediaPhysicalPath(uri.GetPath(), userId, physicalPath);
349 }
350
351 string sandboxPath = SandboxHelper::Decode(uri.GetPath());
352 if ((sandboxPath.find(FILE_MANAGER_URI_HEAD) == 0 && bundleName != FILE_MANAGER_AUTHORITY) ||
353 (sandboxPath.find(FUSE_URI_HEAD) == 0 && bundleName != DLP_MANAGER_BUNDLE_NAME)) {
354 return -EINVAL;
355 }
356
357 if (!GetSandboxPathMap()) {
358 LOGE("GetSandboxPathMap failed");
359 return -EINVAL;
360 }
361
362 string lowerPathTail = "";
363 string lowerPathHead = "";
364 DoGetPhysicalPath(lowerPathTail, lowerPathHead, sandboxPath, sandboxPathMap_);
365
366 if (lowerPathHead == "") {
367 LOGE("lowerPathHead is invalid");
368 return -EINVAL;
369 }
370
371 string networkId = LOCAL;
372 GetNetworkIdFromUri(fileUri, networkId);
373
374 physicalPath = GetLowerPath(lowerPathHead, lowerPathTail, userId, bundleName, networkId);
375 return 0;
376 }
377
GetBackupPhysicalPath(const std::string & fileUri,const std::string & userId,std::string & physicalPath)378 int32_t SandboxHelper::GetBackupPhysicalPath(const std::string &fileUri, const std::string &userId,
379 std::string &physicalPath)
380 {
381 Uri uri(fileUri);
382 string bundleName = uri.GetAuthority();
383 if (bundleName == MEDIA) {
384 return GetMediaPhysicalPath(uri.GetPath(), userId, physicalPath);
385 }
386
387 string sandboxPath = SandboxHelper::Decode(uri.GetPath());
388 if ((sandboxPath.find(FILE_MANAGER_URI_HEAD) == 0 && bundleName != FILE_MANAGER_AUTHORITY) ||
389 (sandboxPath.find(FUSE_URI_HEAD) == 0 && bundleName != DLP_MANAGER_BUNDLE_NAME)) {
390 return -EINVAL;
391 }
392
393 if (!GetBackupSandboxPathMap()) {
394 LOGE("GetBackupSandboxPathMap failed");
395 return -EINVAL;
396 }
397
398 string lowerPathTail = "";
399 string lowerPathHead = "";
400 DoGetPhysicalPath(lowerPathTail, lowerPathHead, sandboxPath, backupSandboxPathMap_);
401
402 if (lowerPathHead == "") {
403 LOGE("lowerPathHead is invalid");
404 return -EINVAL;
405 }
406
407 string networkId = LOCAL;
408 GetNetworkIdFromUri(fileUri, networkId);
409
410 physicalPath = GetLowerPath(lowerPathHead, lowerPathTail, userId, bundleName, networkId);
411 return 0;
412 }
413
IsValidPath(const std::string & path)414 bool SandboxHelper::IsValidPath(const std::string &path)
415 {
416 if (path.find("/./") != std::string::npos ||
417 path.find("/../") != std::string::npos) {
418 return false;
419 }
420 return true;
421 }
422
CheckValidPath(const std::string & filePath)423 bool SandboxHelper::CheckValidPath(const std::string &filePath)
424 {
425 if (filePath.empty() || filePath.size() >= PATH_MAX) {
426 return false;
427 }
428
429 char realPath[PATH_MAX]{'\0'};
430 if (realpath(filePath.c_str(), realPath) == nullptr) {
431 LOGE("realpath failed with %{public}d", errno);
432 return false;
433 }
434
435 if (strncmp(realPath, filePath.c_str(), filePath.size()) != 0) {
436 LOGE("filePath is not equal to realPath, realPath.size = %{public}zu, filePath.size() = %{public}zu",
437 string_view(realPath).size(), filePath.size());
438 return false;
439 }
440
441 return true;
442 }
443 } // namespace AppFileService
444 } // namespace OHOS
445
446