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