1 /*
2  * Copyright (c) 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 #define MLOG_TAG "MediaPrivacyManager"
16 
17 #include "media_privacy_manager.h"
18 
19 #include <algorithm>
20 #include <cerrno>
21 #include <mntent.h>
22 #include <securec.h>
23 #include <unistd.h>
24 #include <unordered_map>
25 #include <fcntl.h>
26 
27 #include "epfs.h"
28 #include "image_source.h"
29 #include "media_container_types.h"
30 #include "media_file_utils.h"
31 #include "media_log.h"
32 #include "medialibrary_bundle_manager.h"
33 #include "medialibrary_errno.h"
34 #include "medialibrary_type_const.h"
35 #include "media_exif.h"
36 #include "media_library_manager.h"
37 #include "medialibrary_bundle_manager.h"
38 #include "medialibrary_urisensitive_operations.h"
39 #include "medialibrary_tracer.h"
40 #include "parameters.h"
41 #include "permission_utils.h"
42 
43 using namespace std;
44 using PrivacyRanges = vector<pair<uint32_t, uint32_t>>;
45 
46 namespace OHOS {
47 namespace Media {
48 constexpr uint32_t E_NO_EXIF = 1;
49 constexpr uint32_t E_NO_PRIVACY_EXIF_TAG = 2;
50 const std::vector<std::string> ALL_SENSITIVE_EXIF = {
51     PHOTO_DATA_IMAGE_GPS_LATITUDE,
52     PHOTO_DATA_IMAGE_GPS_LONGITUDE,
53     PHOTO_DATA_IMAGE_GPS_TIME_STAMP,
54     PHOTO_DATA_IMAGE_GPS_DATE_STAMP,
55     PHOTO_DATA_IMAGE_GPS_ALTITUDE,
56     PHOTO_DATA_IMAGE_GPS_VERSION_ID,
57     PHOTO_DATA_IMAGE_MAKE,
58     PHOTO_DATA_IMAGE_MODEL,
59     PHOTO_DATA_IMAGE_SOFTWARE,
60     PHOTO_DATA_IMAGE_DATE_TIME,
61     PHOTO_DATA_IMAGE_EXPOSURE_TIME,
62     PHOTO_DATA_IMAGE_F_NUMBER,
63     PHOTO_DATA_IMAGE_EXPOSURE_PROGRAM,
64     PHOTO_DATA_IMAGE_STANDARD_OUTPUT_SENSITIVITY,
65     PHOTO_DATA_IMAGE_PHOTOGRAPHIC_SENSITIVITY,
66     PHOTO_DATA_IMAGE_DATE_TIME_ORIGINAL,
67     PHOTO_DATA_IMAGE_DATE_TIME_ORIGINAL_FOR_MEDIA,
68     PHOTO_DATA_IMAGE_DATE_TIME_DIGITIZED,
69     PHOTO_DATA_IMAGE_EXPOSURE_BIAS_VALUE,
70     PHOTO_DATA_IMAGE_METERING_MODE,
71     PHOTO_DATA_IMAGE_LIGHT_SOURCE,
72     PHOTO_DATA_IMAGE_FLASH,
73     PHOTO_DATA_IMAGE_FOCAL_LENGTH,
74     PHOTO_DATA_IMAGE_EXPOSURE_MODE,
75     PHOTO_DATA_IMAGE_WHITE_BALANCE,
76     PHOTO_DATA_IMAGE_DIGITAL_ZOOM_RATIO,
77     PHOTO_DATA_IMAGE_FOCAL_LENGTH_IN_35_MM_FILM
78 };
79 const std::vector<std::string> GEOGRAPHIC_LOCATION_EXIF = {
80     PHOTO_DATA_IMAGE_GPS_LATITUDE,
81     PHOTO_DATA_IMAGE_GPS_LONGITUDE,
82     PHOTO_DATA_IMAGE_GPS_TIME_STAMP,
83     PHOTO_DATA_IMAGE_GPS_DATE_STAMP,
84     PHOTO_DATA_IMAGE_GPS_ALTITUDE,
85     PHOTO_DATA_IMAGE_GPS_VERSION_ID
86 };
87 const std::vector<std::string> SHOOTING_PARAM_EXIF = {
88     PHOTO_DATA_IMAGE_MAKE,
89     PHOTO_DATA_IMAGE_MODEL,
90     PHOTO_DATA_IMAGE_SOFTWARE,
91     PHOTO_DATA_IMAGE_DATE_TIME,
92     PHOTO_DATA_IMAGE_EXPOSURE_TIME,
93     PHOTO_DATA_IMAGE_F_NUMBER,
94     PHOTO_DATA_IMAGE_EXPOSURE_PROGRAM,
95     PHOTO_DATA_IMAGE_PHOTOGRAPHIC_SENSITIVITY,
96     PHOTO_DATA_IMAGE_DATE_TIME_ORIGINAL,
97     PHOTO_DATA_IMAGE_DATE_TIME_DIGITIZED,
98     PHOTO_DATA_IMAGE_EXPOSURE_BIAS_VALUE,
99     PHOTO_DATA_IMAGE_METERING_MODE,
100     PHOTO_DATA_IMAGE_LIGHT_SOURCE,
101     PHOTO_DATA_IMAGE_FLASH,
102     PHOTO_DATA_IMAGE_FOCAL_LENGTH,
103     PHOTO_DATA_IMAGE_EXPOSURE_MODE,
104     PHOTO_DATA_IMAGE_WHITE_BALANCE,
105     PHOTO_DATA_IMAGE_DIGITAL_ZOOM_RATIO,
106     PHOTO_DATA_IMAGE_FOCAL_LENGTH_IN_35_MM_FILM
107 };
108 
MediaPrivacyManager(const string & path,const string & mode,const string & fileId)109 MediaPrivacyManager::MediaPrivacyManager(const string &path, const string &mode, const string &fileId)
110     : path_(path), mode_(mode), fileId_(fileId)
111 {}
112 
~MediaPrivacyManager()113 MediaPrivacyManager::~MediaPrivacyManager()
114 {}
115 
116 const unordered_map<PrivacyType, string> PRIVACY_PERMISSION_MAP = {
117     { PrivacyType::PRIVACY_LOCATION, PERMISSION_NAME_MEDIA_LOCATION },
118 };
119 
120 const vector<string> EXIF_SUPPORTED_EXTENSION = {
121     IMAGE_CONTAINER_TYPE_JPG,
122     IMAGE_CONTAINER_TYPE_JPEG,
123     IMAGE_CONTAINER_TYPE_JPE,
124     IMAGE_CONTAINER_TYPE_PNG,
125     IMAGE_CONTAINER_TYPE_WEBP,
126     IMAGE_CONTAINER_TYPE_DNG,
127     IMAGE_CONTAINER_TYPE_HEIC,
128 };
129 
IsTargetExtension(const string & path)130 static bool IsTargetExtension(const string &path)
131 {
132     const string ext = MediaFileUtils::GetExtensionFromPath(path);
133     return find(EXIF_SUPPORTED_EXTENSION.begin(), EXIF_SUPPORTED_EXTENSION.end(), ext) !=
134         EXIF_SUPPORTED_EXTENSION.end();
135 }
136 
IsWriteMode(const string & mode)137 static bool IsWriteMode(const string &mode)
138 {
139     return mode.find(MEDIA_FILEMODE_WRITEONLY) != string::npos;
140 }
141 
CheckFsMounted(const string & fsType,const string & mountPoint)142 static bool CheckFsMounted(const string &fsType, const string &mountPoint)
143 {
144     struct mntent mountEntry;
145     constexpr uint32_t mntEntrySize = 1024;
146     char entryStr[mntEntrySize] = {0};
147     FILE *mountTable = setmntent("/proc/mounts", "r");
148     if (mountTable == nullptr) {
149         MEDIA_ERR_LOG("Failed to get mount table, errno:%{public}d", errno);
150         return false;
151     }
152 
153     do {
154         struct mntent *mnt = getmntent_r(mountTable, &mountEntry, entryStr, sizeof(entryStr));
155         if (mnt == nullptr) {
156             endmntent(mountTable);
157             break;
158         }
159         if ((mountEntry.mnt_type != nullptr) &&
160             (mountEntry.mnt_dir != nullptr) &&
161             (strcmp(mountEntry.mnt_type, fsType.c_str()) == 0) &&
162             (strcmp(mountEntry.mnt_dir, mountPoint.c_str()) == 0)) {
163             endmntent(mountTable);
164             return true;
165         }
166     } while (true);
167     return false;
168 }
169 
BindFilterProxyFdToOrigin(const int32_t originFd,int32_t & proxyFd)170 static int32_t BindFilterProxyFdToOrigin(const int32_t originFd, int32_t &proxyFd)
171 {
172     int ret = ioctl(proxyFd, IOC_SET_ORIGIN_FD, &originFd);
173     if (ret < 0) {
174         MEDIA_ERR_LOG("Failed to set origin fd: %{public}d to filter proxy fd: %{public}d, error: %{public}d",
175                       originFd, proxyFd, errno);
176         return ret;
177     }
178     return ret;
179 }
180 
SendRangesToIoctl(const int32_t originFd,const int32_t proxyFd,const PrivacyRanges & rans)181 static int32_t SendRangesToIoctl(const int32_t originFd, const int32_t proxyFd, const PrivacyRanges &rans)
182 {
183     FilterProxyRanges *ranges = (FilterProxyRanges *)malloc(sizeof(*ranges) + sizeof(ranges->range[0]) * rans.size());
184     if (ranges == nullptr) {
185         MEDIA_ERR_LOG("Failed to malloc ranges, errno: %{public}d", errno);
186         return -ENOMEM;
187     }
188     ranges->size = static_cast<uint64_t>(rans.size());
189     ranges->reserved = 0;
190     for (size_t i = 0; i < rans.size(); i++) {
191         // first: offset, second: end
192         ranges->range[i].begin = static_cast<uint64_t>(rans[i].first);
193         ranges->range[i].end = static_cast<uint64_t>(rans[i].second);
194     }
195     int err = ioctl(proxyFd, IOC_SET_FILTER_PROXY_RANGE, ranges);
196     if (err < 0) {
197         MEDIA_ERR_LOG("Failed to set ranges to fd: %{public}d, error: %{public}d", proxyFd, errno);
198     }
199     free(ranges);
200     return err;
201 }
202 
203 /* Caller is responsible to close the returned fd */
OpenOriginFd(const string & path,const string & mode)204 static int32_t OpenOriginFd(const string &path, const string &mode)
205 {
206     MediaLibraryTracer tracer;
207     tracer.Start("MediaPrivacyManager::OpenOriginFd");
208     string clientBundle = MediaLibraryBundleManager::GetInstance()->GetClientBundleName();
209     if (clientBundle.empty()) {
210         MEDIA_DEBUG_LOG("clientBundleName is empty");
211     }
212     return MediaFileUtils::OpenFile(path, mode, clientBundle);
213 }
214 
215 /*
216  * Read to the returned @filterProxyFd will redirect to the file specified by @path, but the privacy ranges(@ranges) in
217  * read buffer will be filtered out and filled with 0.
218  *
219  * Caller is responsible to close the returned @filterProxyFd.
220  */
OpenFilterProxyFd(const string & path,const string & mode,const PrivacyRanges & ranges)221 static int32_t OpenFilterProxyFd(const string &path, const string &mode, const PrivacyRanges &ranges)
222 {
223     MediaLibraryTracer tracer;
224     tracer.Start("MediaPrivacyManager::OpenFilterProxyFd");
225     if (!CheckFsMounted(FS_TYPE_EPFS, EPFS_MOUNT_POINT)) {
226         MEDIA_INFO_LOG("Epfs is currently not supported yet");
227         return OpenOriginFd(path, mode);
228     }
229 
230     int32_t originFd = open(path.c_str(), O_RDONLY);
231     if (originFd < 0) {
232         MEDIA_ERR_LOG("Failed to open file, errno: %{public}d, path: %{private}s", errno, path.c_str());
233         return originFd;
234     }
235     constexpr mode_t epfsFileMode = 0400;
236     // filterProxyFd_ will be returned to user, so there is no need to close it here.
237     int32_t filterProxyFd = open(EPFS_MOUNT_POINT.c_str(), O_TMPFILE | O_RDWR, epfsFileMode);
238     if (filterProxyFd < 0) {
239         MEDIA_ERR_LOG("Failed to open epfs, error: %{public}d", errno);
240         close(originFd);
241         return filterProxyFd;
242     }
243     int32_t ret = BindFilterProxyFdToOrigin(originFd, filterProxyFd);
244     if (ret < 0) {
245         close(originFd);
246         close(filterProxyFd);
247         return ret;
248     }
249     ret = SendRangesToIoctl(originFd, filterProxyFd, ranges);
250     if (ret < 0) {
251         close(originFd);
252         close(filterProxyFd);
253         return ret;
254     }
255     close(originFd);
256     MEDIA_INFO_LOG("FilterProxyFd will be returned: %{private}d", filterProxyFd);
257     return filterProxyFd;
258 }
259 
ShowRanges(const PrivacyRanges & ranges)260 static void ShowRanges(const PrivacyRanges &ranges)
261 {
262     for (auto range : ranges) {
263         MEDIA_DEBUG_LOG("Range: [%{public}u, %{public}u)", range.first, range.second);
264     }
265 }
266 
CmpMode(pair<uint32_t,uint32_t> pairA,pair<uint32_t,uint32_t> pairB)267 static bool CmpMode(pair<uint32_t, uint32_t> pairA, pair<uint32_t, uint32_t> pairB)
268 {
269     return pairA.first < pairB.first;
270 }
271 
SortRangesAndCheck(PrivacyRanges & ranges)272 static int32_t SortRangesAndCheck(PrivacyRanges &ranges)
273 {
274     if (ranges.empty()) {
275         return E_SUCCESS;
276     }
277     uint32_t size = ranges.size();
278     if (size > PRIVACY_MAX_RANGES) {
279         MEDIA_ERR_LOG("Privacy ranges size invalid: %{public}d", size);
280         return -EINVAL;
281     }
282     sort(ranges.begin(), ranges.end(), CmpMode);
283     const auto u_idx = unique(ranges.begin(), ranges.end());
284     ranges.erase(u_idx, ranges.end());
285     size = ranges.size();
286 
287     if (ranges[0].first >= ranges[0].second) {
288         MEDIA_ERR_LOG("Incorrect fileter ranges: begin(%{public}u) is not less than end(%{public}u)",
289                       ranges[0].first, ranges[0].second);
290         return -EINVAL;
291     }
292 
293     for (uint32_t i = 1; i < size; i++) {
294         if ((ranges[i].first >= ranges[i].second) || (ranges[i].first < ranges[i - 1].second)) {
295             MEDIA_ERR_LOG("Invalid ranges: [%{public}u, %{public}u), last range is [%{public}u, %{public}u)",
296                           ranges[i].first, ranges[i].second, ranges[i - 1].first, ranges[i - 1].second);
297             return -EINVAL;
298         }
299     }
300     ShowRanges(ranges);
301     return E_SUCCESS;
302 }
303 
CollectRanges(const string & path,const HideSensitiveType & sensitiveType,PrivacyRanges & ranges)304 static int32_t CollectRanges(const string &path, const HideSensitiveType &sensitiveType, PrivacyRanges &ranges)
305 {
306     if (sensitiveType == HideSensitiveType::NO_DESENSITIZE) {
307         return E_SUCCESS;
308     }
309 
310     SourceOptions opts;
311     opts.formatHint = "image/jpeg";
312     uint32_t err = -1;
313     auto imageSource = ImageSource::CreateImageSource(path, opts, err);
314     if (imageSource == nullptr) {
315         MEDIA_ERR_LOG("Failed to create image source, err: %{public}u", err);
316         return -ENOMEM;
317     }
318 
319     PrivacyRanges areas;
320     std::vector<std::string> exifKeys;
321     switch (sensitiveType) {
322         case HideSensitiveType::ALL_DESENSITIZE:
323             err = imageSource->GetFilterArea(ALL_SENSITIVE_EXIF, areas);
324             break;
325         case HideSensitiveType::GEOGRAPHIC_LOCATION_DESENSITIZE:
326             err = imageSource->GetFilterArea(GEOGRAPHIC_LOCATION_EXIF, areas);
327             break;
328         case HideSensitiveType::SHOOTING_PARAM_DESENSITIZE:
329             err = imageSource->GetFilterArea(SHOOTING_PARAM_EXIF, areas);
330             break;
331         default:
332             MEDIA_ERR_LOG("Invaild hide sensitive type %{public}d", sensitiveType);
333             return E_SUCCESS;
334     }
335 
336     if ((err != E_SUCCESS) && (err != E_NO_EXIF) && (err != E_NO_PRIVACY_EXIF_TAG)) {
337         MEDIA_ERR_LOG("Failed to get privacy area with type %{public}d, err: %{public}u", sensitiveType, err);
338         return E_ERR;
339     }
340     for (auto &range : areas) {
341         ranges.insert(ranges.end(), std::make_pair(range.first, range.first + range.second));
342     }
343     return E_SUCCESS;
344 }
345 
346 /*
347  * @path: [Input], the real path of the target file
348  * @mode: [Input], the mode specified by user
349  * @ranges: [Output], the privacy ranges of the target file
350  *
351  * The return value is listed below:
352  * o Not a jpeg file: return success with empty ranges
353  * o Write jpeg with no MEDIA_LOCATION: return permission denied
354  * o Write jpeg with MEDIA_LOCATION: return success with empty ranges
355  * o Read jpeg with no MEDIA_LOCATION: return success with privacy ranges if have any
356  * o Read jpeg with MEDIA_LOCATION: return success with empty ranges
357  * o Other cases: return negative error code.
358  */
GetPrivacyRanges(const string & path,const string & mode,const string & fileId,PrivacyRanges & ranges)359 static int32_t GetPrivacyRanges(const string &path, const string &mode, const string &fileId, PrivacyRanges &ranges)
360 {
361     MediaLibraryTracer tracer;
362     tracer.Start("MediaPrivacyManager::GetPrivacyRanges");
363     if (!IsTargetExtension(path)) {
364         return E_SUCCESS;
365     }
366 
367     if (fileId.empty()) {
368         return E_SUCCESS;
369     }
370 
371     if (mode.find('w') != string::npos) {
372         return E_SUCCESS;
373     }
374 
375     for (auto &item : PRIVACY_PERMISSION_MAP) {
376         const string &perm = item.second;
377         bool result = PermissionUtils::CheckCallerPermission(perm);
378         if ((result == false) && (perm == PERMISSION_NAME_MEDIA_LOCATION) && IsWriteMode(mode)) {
379             MEDIA_ERR_LOG("Write is not allowed if have no location permission");
380             return E_PERMISSION_DENIED;
381         }
382         if (result) {
383             continue;
384         }
385         //collect ranges by hideSensitiveType
386         string bundleName = MediaLibraryBundleManager::GetInstance()->GetClientBundleName();
387         string appId = PermissionUtils::GetAppIdByBundleName(bundleName);
388         string appIdFile = UriSensitiveOperations::QueryAppId(fileId);
389         if (appId == appIdFile) {
390             continue;
391         }
392 
393         HideSensitiveType sensitiveType =
394             static_cast<HideSensitiveType>(UriSensitiveOperations::QuerySensitiveType(appId, fileId));
395         int32_t err = CollectRanges(path, sensitiveType, ranges);
396         if (err < 0) {
397             return err;
398         }
399     }
400     return SortRangesAndCheck(ranges);
401 }
402 
IsDeveloperMediaTool()403 static bool IsDeveloperMediaTool()
404 {
405     if (!PermissionUtils::IsRootShell() && !PermissionUtils::IsHdcShell()) {
406         MEDIA_DEBUG_LOG("Mediatool permission check failed: target is not root");
407         return false;
408     }
409     if (!OHOS::system::GetBoolParameter("const.security.developermode.state", true)) {
410         MEDIA_DEBUG_LOG("Mediatool permission check failed: target is not in developer mode");
411         return false;
412     }
413     return true;
414 }
415 
Open()416 int32_t MediaPrivacyManager::Open()
417 {
418     int err = GetPrivacyRanges(path_, mode_, fileId_, ranges_);
419     if (err < 0) {
420         return err;
421     }
422     if (ranges_.size() > 0 && !IsDeveloperMediaTool()) {
423         return OpenFilterProxyFd(path_, mode_, ranges_);
424     }
425     return OpenOriginFd(path_, mode_);
426 }
427 } // namespace Media
428 } // namespace OHOS
429