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_file_utils.h"
30 #include "media_log.h"
31 #include "medialibrary_errno.h"
32 #include "medialibrary_type_const.h"
33 #include "permission_utils.h"
34
35 using namespace std;
36 using PrivacyRanges = vector<pair<uint32_t, uint32_t>>;
37
38 namespace OHOS {
39 namespace Media {
40 constexpr uint32_t E_NO_EXIF = 1;
41 constexpr uint32_t E_NO_PRIVACY_EXIF_TAG = 2;
42
MediaPrivacyManager(const string & path,const string & mode)43 MediaPrivacyManager::MediaPrivacyManager(const string &path, const string &mode) : path_(path), mode_(mode)
44 {}
45
~MediaPrivacyManager()46 MediaPrivacyManager::~MediaPrivacyManager()
47 {}
48
49 const unordered_map<PrivacyType, string> PRIVACY_PERMISSION_MAP = {
50 { PrivacyType::PRIVACY_LOCATION, PERMISSION_NAME_MEDIA_LOCATION },
51 };
52
53 const vector<string> EXIF_SUPPORTED_EXTENSION = {
54 IMAGE_CONTAINER_TYPE_JPG,
55 IMAGE_CONTAINER_TYPE_JPEG,
56 IMAGE_CONTAINER_TYPE_JPE
57 };
58
IsTargetExtension(const string & path)59 static bool IsTargetExtension(const string &path)
60 {
61 const string ext = MediaFileUtils::GetExtensionFromPath(path);
62 return find(EXIF_SUPPORTED_EXTENSION.begin(), EXIF_SUPPORTED_EXTENSION.end(), ext) !=
63 EXIF_SUPPORTED_EXTENSION.end();
64 }
65
IsWriteMode(const string & mode)66 static bool IsWriteMode(const string &mode)
67 {
68 return mode.find(MEDIA_FILEMODE_WRITEONLY) != string::npos;
69 }
70
CheckFsMounted(const string & fsType,const string & mountPoint)71 static bool CheckFsMounted(const string &fsType, const string &mountPoint)
72 {
73 struct mntent mountEntry;
74 constexpr uint32_t mntEntrySize = 1024;
75 char entryStr[mntEntrySize] = {0};
76 FILE *mountTable = setmntent("/proc/mounts", "r");
77 if (mountTable == nullptr) {
78 MEDIA_ERR_LOG("Failed to get mount table, errno:%{public}d", errno);
79 return false;
80 }
81
82 do {
83 struct mntent *mnt = getmntent_r(mountTable, &mountEntry, entryStr, sizeof(entryStr));
84 if (mnt == nullptr) {
85 endmntent(mountTable);
86 break;
87 }
88 if ((mountEntry.mnt_type != nullptr) &&
89 (mountEntry.mnt_dir != nullptr) &&
90 (strcmp(mountEntry.mnt_type, fsType.c_str()) == 0) &&
91 (strcmp(mountEntry.mnt_dir, mountPoint.c_str()) == 0)) {
92 endmntent(mountTable);
93 return true;
94 }
95 } while (true);
96 return false;
97 }
98
BindFilterProxyFdToOrigin(const int32_t originFd,int32_t & proxyFd)99 static int32_t BindFilterProxyFdToOrigin(const int32_t originFd, int32_t &proxyFd)
100 {
101 int ret = ioctl(proxyFd, IOC_SET_ORIGIN_FD, &originFd);
102 if (ret < 0) {
103 MEDIA_ERR_LOG("Failed to set origin fd: %{public}d to filter proxy fd: %{public}d, error: %{public}d",
104 originFd, proxyFd, errno);
105 return ret;
106 }
107 return ret;
108 }
109
SendRangesToIoctl(const int32_t originFd,const int32_t proxyFd,const PrivacyRanges & rans)110 static int32_t SendRangesToIoctl(const int32_t originFd, const int32_t proxyFd, const PrivacyRanges &rans)
111 {
112 FilterProxyRanges *ranges = (FilterProxyRanges *)malloc(sizeof(*ranges) + sizeof(ranges->range[0]) * rans.size());
113 if (ranges == nullptr) {
114 MEDIA_ERR_LOG("Failed to malloc ranges, errno: %{public}d", errno);
115 return -ENOMEM;
116 }
117 ranges->size = static_cast<uint64_t>(rans.size());
118 ranges->reserved = 0;
119 for (size_t i = 0; i < rans.size(); i++) {
120 // first: offset, second: end
121 ranges->range[i].begin = static_cast<uint64_t>(rans[i].first);
122 ranges->range[i].end = static_cast<uint64_t>(rans[i].second);
123 }
124 int err = ioctl(proxyFd, IOC_SET_FILTER_PROXY_RANGE, ranges);
125 if (err < 0) {
126 MEDIA_ERR_LOG("Failed to set ranges to fd: %{public}d, error: %{public}d", proxyFd, errno);
127 }
128 free(ranges);
129 return err;
130 }
131
132 /* Caller is responsible to close the returned fd */
OpenOriginFd(const string & path,const string & mode)133 static int32_t OpenOriginFd(const string &path, const string &mode)
134 {
135 return MediaFileUtils::OpenFile(path, mode);
136 }
137
138 /*
139 * Read to the returned @filterProxyFd will redirect to the file specified by @path, but the privacy ranges(@ranges) in
140 * read buffer will be filtered out and filled with 0.
141 *
142 * Caller is responsible to close the returned @filterProxyFd.
143 */
OpenFilterProxyFd(const string & path,const string & mode,const PrivacyRanges & ranges)144 static int32_t OpenFilterProxyFd(const string &path, const string &mode, const PrivacyRanges &ranges)
145 {
146 if (!CheckFsMounted(FS_TYPE_EPFS, EPFS_MOUNT_POINT)) {
147 MEDIA_INFO_LOG("Epfs is currently not supported yet");
148 return OpenOriginFd(path, mode);
149 }
150
151 int32_t originFd = open(path.c_str(), O_RDONLY);
152 if (originFd < 0) {
153 MEDIA_ERR_LOG("Failed to open file, errno: %{public}d, path: %{private}s", errno, path.c_str());
154 return originFd;
155 }
156 constexpr mode_t epfsFileMode = 0400;
157 // filterProxyFd_ will be returned to user, so there is no need to close it here.
158 int32_t filterProxyFd = open(EPFS_MOUNT_POINT.c_str(), O_TMPFILE | O_RDWR, epfsFileMode);
159 if (filterProxyFd < 0) {
160 MEDIA_ERR_LOG("Failed to open epfs, error: %{public}d", errno);
161 close(originFd);
162 return filterProxyFd;
163 }
164 int32_t ret = BindFilterProxyFdToOrigin(originFd, filterProxyFd);
165 if (ret < 0) {
166 close(originFd);
167 close(filterProxyFd);
168 return ret;
169 }
170 ret = SendRangesToIoctl(originFd, filterProxyFd, ranges);
171 if (ret < 0) {
172 close(originFd);
173 close(filterProxyFd);
174 return ret;
175 }
176 close(originFd);
177 MEDIA_INFO_LOG("FilterProxyFd will be returned: %{private}d", filterProxyFd);
178 return filterProxyFd;
179 }
180
ShowRanges(const PrivacyRanges & ranges)181 static void ShowRanges(const PrivacyRanges &ranges)
182 {
183 for (auto range : ranges) {
184 MEDIA_DEBUG_LOG("Range: [%{public}u, %{public}u)", range.first, range.second);
185 }
186 }
187
CmpMode(pair<uint32_t,uint32_t> pairA,pair<uint32_t,uint32_t> pairB)188 static bool CmpMode(pair<uint32_t, uint32_t> pairA, pair<uint32_t, uint32_t> pairB)
189 {
190 return pairA.first < pairB.first;
191 }
192
SortRangesAndCheck(PrivacyRanges & ranges)193 static int32_t SortRangesAndCheck(PrivacyRanges &ranges)
194 {
195 if (ranges.empty()) {
196 return E_SUCCESS;
197 }
198 uint32_t size = ranges.size();
199 if (size > PRIVACY_MAX_RANGES) {
200 MEDIA_ERR_LOG("Privacy ranges size invalid: %{public}d", size);
201 return -EINVAL;
202 }
203 sort(ranges.begin(), ranges.end(), CmpMode);
204
205 if (ranges[0].first >= ranges[0].second) {
206 MEDIA_ERR_LOG("Incorrect fileter ranges: begin(%{public}u) is not less than end(%{public}u)",
207 ranges[0].first, ranges[0].second);
208 return -EINVAL;
209 }
210
211 for (uint32_t i = 1; i < size; i++) {
212 if ((ranges[i].first >= ranges[i].second) || (ranges[i].first < ranges[i - 1].second)) {
213 MEDIA_ERR_LOG("Invalid ranges: [%{public}u, %{public}u), last range is [%{public}u, %{public}u)",
214 ranges[i].first, ranges[i].second, ranges[i - 1].first, ranges[i - 1].second);
215 return -EINVAL;
216 }
217 }
218 ShowRanges(ranges);
219 return E_SUCCESS;
220 }
221
CollectRanges(const string & path,const PrivacyType & type,PrivacyRanges & ranges)222 static int32_t CollectRanges(const string &path, const PrivacyType &type, PrivacyRanges &ranges)
223 {
224 SourceOptions opts;
225 opts.formatHint = "image/jpeg";
226 uint32_t err = -1;
227 auto imageSource = ImageSource::CreateImageSource(path, opts, err);
228 if (imageSource == nullptr) {
229 MEDIA_ERR_LOG("Failed to create image source, err: %{public}u", err);
230 return -ENOMEM;
231 }
232
233 PrivacyRanges areas;
234 err = imageSource->GetFilterArea(type, areas);
235 if ((err != E_SUCCESS) && (err != E_NO_EXIF) && (err != E_NO_PRIVACY_EXIF_TAG)) {
236 MEDIA_ERR_LOG("Failed to get privacy area with type %{public}d, err: %{public}u", type, err);
237 return E_ERR;
238 }
239 for (auto &range : areas) {
240 ranges.insert(ranges.end(), std::make_pair(range.first, range.first + range.second));
241 }
242 return E_SUCCESS;
243 }
244
245 /*
246 * @path: [Input], the real path of the target file
247 * @mode: [Input], the mode specified by user
248 * @ranges: [Output], the privacy ranges of the target file
249 *
250 * The return value is listed below:
251 * o Not a jpeg file: return success with empty ranges
252 * o Write jpeg with no MEDIA_LOCATION: return permission denied
253 * o Write jpeg with MEDIA_LOCATION: return success with empty ranges
254 * o Read jpeg with no MEDIA_LOCATION: return success with privacy ranges if have any
255 * o Read jpeg with MEDIA_LOCATION: return success with empty ranges
256 * o Other cases: return negative error code.
257 */
GetPrivacyRanges(const string & path,const string & mode,PrivacyRanges & ranges)258 static int32_t GetPrivacyRanges(const string &path, const string &mode, PrivacyRanges &ranges)
259 {
260 if (!IsTargetExtension(path)) {
261 return E_SUCCESS;
262 }
263
264 if (mode.find('w') != string::npos) {
265 return E_SUCCESS;
266 }
267
268 for (auto &item : PRIVACY_PERMISSION_MAP) {
269 const string &perm = item.second;
270 bool result = PermissionUtils::CheckCallerPermission(perm);
271 if ((result == false) && (perm == PERMISSION_NAME_MEDIA_LOCATION) && IsWriteMode(mode)) {
272 MEDIA_ERR_LOG("Write is not allowed if have no location permission");
273 return E_PERMISSION_DENIED;
274 }
275 if (result) {
276 continue;
277 }
278 int32_t err = CollectRanges(path, item.first, ranges);
279 if (err < 0) {
280 return err;
281 }
282 }
283 return SortRangesAndCheck(ranges);
284 }
285
Open()286 int32_t MediaPrivacyManager::Open()
287 {
288 int err = GetPrivacyRanges(path_, mode_, ranges_);
289 if (err < 0) {
290 return err;
291 }
292 if (ranges_.size() > 0) {
293 return OpenFilterProxyFd(path_, mode_, ranges_);
294 }
295 return OpenOriginFd(path_, mode_);
296 }
297 } // namespace Media
298 } // namespace OHOS
299