1 /*
2 * Copyright (C) 2023 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 #include "acl.h"
16
17 #include <cerrno>
18 #include <dirent.h>
19 #include <list>
20 #include <new>
21 #include <type_traits>
22 #include <sys/stat.h>
23 #include <sys/xattr.h>
24
25 #include "medialibrary_errno.h"
26 #include "media_log.h"
27 #include "securec.h"
28
29 namespace OHOS {
30 namespace Media {
31
32 const std::map<ACL_TAG, const char *> ACL_TAG_STR = {
33 { ACL_TAG::UNDEFINED, "ACL_UNDEFINED_TAG" },
34 { ACL_TAG::USER_OBJ, "ACL_USER_OBJ" },
35 { ACL_TAG::USER, "ACL_USER" },
36 { ACL_TAG::GROUP_OBJ, "ACL_GROUP_OBJ" },
37 { ACL_TAG::GROUP, "ACL_GROUP" },
38 { ACL_TAG::MASK, "ACL_MASK" },
39 { ACL_TAG::OTHER, "ACL_OTHER" },
40 };
41
42 constexpr int BUF_SIZE = 400;
43
PrintACLDetail(const std::string & path,const char * aclAttrName)44 void PrintACLDetail(const std::string& path, const char* aclAttrName)
45 {
46 char *buf = nullptr;
47 ssize_t len = getxattr(path.c_str(), aclAttrName, nullptr, 0);
48 if (len > 0) {
49 buf = new (std::nothrow) char[len]{};
50 if (buf == nullptr) {
51 MEDIA_ERR_LOG("Memory allocation fails");
52 return;
53 }
54 len = getxattr(path.c_str(), aclAttrName, buf, len);
55 }
56 if (len == -1) {
57 MEDIA_ERR_LOG("getxattr error");
58 if (buf != nullptr) {
59 delete[] buf;
60 buf = nullptr;
61 }
62 return;
63 }
64
65 Acl acl;
66 acl.DeSerialize(buf, len);
67 acl.Print(path);
68
69 if (buf != nullptr) {
70 delete[] buf;
71 buf = nullptr;
72 }
73 }
74
ReCalcMaskPerm()75 ACL_PERM Acl::ReCalcMaskPerm()
76 {
77 ACL_PERM perm;
78 for (const auto &e : entries) {
79 if (e.tag == ACL_TAG::USER || e.tag == ACL_TAG::GROUP_OBJ || e.tag == ACL_TAG::GROUP) {
80 perm.Merge(e.perm);
81 }
82 }
83 return perm;
84 }
85
IsEmpty()86 bool Acl::IsEmpty()
87 {
88 return entries.empty();
89 }
90
IsValid()91 bool Acl::IsValid()
92 {
93 if (!entries.count(ACL_TAG::USER_OBJ) || !entries.count(ACL_TAG::GROUP_OBJ) ||
94 !entries.count(ACL_TAG::OTHER)) {
95 return false;
96 }
97 if (maskDemand && !entries.count(ACL_TAG::MASK)) {
98 return false;
99 }
100 return true;
101 }
102
CompareInsertEntry(const AclXattrEntry & entry)103 void Acl::CompareInsertEntry(const AclXattrEntry &entry)
104 {
105 if (entries.count(entry)) {
106 auto it = entries.find(entry);
107 entries.erase(it);
108 }
109 if (entry.perm.IsReadable() || entry.perm.IsWritable() || entry.perm.IsExecutable()) {
110 entries.insert(entry);
111 }
112 }
113
InsertEntry(const AclXattrEntry & entry)114 int Acl::InsertEntry(const AclXattrEntry &entry)
115 {
116 if (entries.size() >= ENTRIES_MAX_NUM) {
117 errno = EAGAIN;
118 return E_ERR;
119 }
120 CompareInsertEntry(entry); // must before ReCalcMaskPerm()
121
122 maskDemand++;
123 /*
124 * In either case there's no or already one ACL_MASK entry in the set,
125 * we need to re-calculate MASK's permission and *insert* it (to replace
126 * the old one in latter case since we can't change std::set's element
127 * in-place). So do the following unconditionally.
128 *
129 * Be warned: do _NOT_ combine the following into one line, otherwise
130 * you can't pass the !!genius!! CI coding style check.
131 */
132 CompareInsertEntry(
133 { ACL_TAG::MASK, ReCalcMaskPerm(), ACL_UNDEFINED_ID }
134 );
135 return E_OK;
136 }
137
Serialize(size_t & bufSize)138 char *Acl::Serialize(size_t &bufSize)
139 {
140 if (!IsValid()) {
141 errno = EINVAL;
142 return nullptr;
143 }
144
145 /* clear possible previous allocation */
146 if (buf != nullptr) {
147 delete[] buf;
148 buf = nullptr;
149 }
150
151 bufSize = sizeof(AclXattrHeader) + sizeof(AclXattrEntry) * entries.size();
152 if (bufSize > BUF_MAX_SIZE) {
153 bufSize = 0;
154 errno = EINVAL;
155 return nullptr;
156 }
157 buf = new (std::nothrow) char[bufSize];
158 if (buf == nullptr) {
159 errno = ENOMEM;
160 return nullptr;
161 }
162 auto err = memcpy_s(buf, bufSize, &header, sizeof(AclXattrHeader));
163 if (err != EOK) {
164 errno = err;
165 delete[] buf;
166 buf = nullptr;
167 return nullptr;
168 }
169
170 size_t restSize = bufSize - sizeof(AclXattrHeader);
171 AclXattrEntry *ptr = reinterpret_cast<AclXattrEntry*>(buf + sizeof(AclXattrHeader));
172 for (const auto &e : entries) {
173 auto err = memcpy_s(ptr++, restSize, &e, sizeof(AclXattrEntry));
174 if (err != EOK) {
175 errno = err;
176 delete[] buf;
177 buf = nullptr;
178 return nullptr;
179 }
180 restSize -= sizeof(AclXattrEntry);
181 }
182 return buf;
183 }
184
DeSerialize(const char * aclHead,size_t size)185 int Acl::DeSerialize(const char* aclHead, size_t size)
186 {
187 if (size > BUF_MAX_SIZE || size < sizeof(AclXattrHeader)) {
188 errno = EINVAL;
189 return errno;
190 }
191 header = *reinterpret_cast<const AclXattrHeader*>(aclHead);
192 size -= sizeof(AclXattrHeader);
193 aclHead += sizeof(AclXattrHeader);
194
195 /*
196 * `entry->tag != ACL_TAG::UNDEFINED` is unreliable outside the buffer, so check
197 * it after checking the size of remaining buffer.
198 */
199 for (const AclXattrEntry *entry = reinterpret_cast<const AclXattrEntry*>(aclHead);
200 size >= sizeof(AclXattrEntry) && entry->tag != ACL_TAG::UNDEFINED; entry++) {
201 InsertEntry(*entry);
202 size -= sizeof(AclXattrEntry);
203 }
204 if (size < 0) {
205 entries.clear();
206 header = { 0 };
207 errno = EINVAL;
208 return errno;
209 }
210
211 return E_OK;
212 }
213
AclFromMode(const std::string & file)214 Acl AclFromMode(const std::string &file)
215 {
216 Acl acl;
217 struct stat st;
218
219 if (stat(file.c_str(), &st) == -1) {
220 return acl;
221 }
222
223 acl.InsertEntry(
224 {
225 .tag = ACL_TAG::USER_OBJ,
226 .perm = (st.st_mode & S_IRWXU) >> 6,
227 .id = ACL_UNDEFINED_ID,
228 }
229 );
230 acl.InsertEntry(
231 {
232 .tag = ACL_TAG::GROUP_OBJ,
233 .perm = (st.st_mode & S_IRWXG) >> 3,
234 .id = ACL_UNDEFINED_ID,
235 }
236 );
237 acl.InsertEntry(
238 {
239 .tag = ACL_TAG::OTHER,
240 .perm = (st.st_mode & S_IRWXO),
241 .id = ACL_UNDEFINED_ID,
242 }
243 );
244
245 return acl;
246 }
247
InitSandboxEntry(AclXattrEntry & entry)248 void InitSandboxEntry(AclXattrEntry &entry)
249 {
250 entry.tag = ACL_TAG::GROUP;
251 entry.id = THUMB_ACL_GROUP;
252 entry.perm.SetRead();
253 entry.perm.SetExecute();
254 }
255
InitSandboxGroupEntry(AclXattrEntry & entry,uint32_t id,uint16_t access)256 void InitSandboxGroupEntry(AclXattrEntry& entry, uint32_t id, uint16_t access)
257 {
258 entry.tag = ACL_TAG::GROUP;
259 entry.id = id;
260 if (access & ACL_PERM::Value::READ) {
261 entry.perm.SetRead();
262 }
263 if (access & ACL_PERM::Value::WRITE) {
264 entry.perm.SetWrite();
265 }
266 if (access & ACL_PERM::Value::EXECUTE) {
267 entry.perm.SetExecute();
268 }
269 }
270
AclSetDefault()271 int32_t Acl::AclSetDefault()
272 {
273 if (EnableAcl(THUMB_DIR, ACL_XATTR_DEFAULT, ACL_PERM::Value::READ | ACL_PERM::Value::EXECUTE,
274 MEDIA_DB_ACL_GROUP) != E_OK) {
275 MEDIA_ERR_LOG("Failed to set the acl permission for the Photo dir");
276 return E_ERR;
277 }
278 return E_OK;
279 }
280
RecursiveEnableAcl(const std::string & path,const char * aclAttrName,const uint16_t & permission,uint32_t groupId)281 int32_t Acl::RecursiveEnableAcl(const std::string& path, const char* aclAttrName, const uint16_t& permission,
282 uint32_t groupId)
283 {
284 DIR* fileDir;
285 struct dirent* dirEntry;
286 struct stat st;
287 std::list<std::string> dirPathList{path};
288 int32_t result = E_OK;
289 while (!dirPathList.empty()) {
290 std::string dir = dirPathList.back();
291 dirPathList.pop_back();
292 if ((fileDir = opendir(dir.c_str())) == nullptr) {
293 MEDIA_ERR_LOG("dir not exist: %{private}s, error: %s", dir.c_str(), strerror(errno));
294 result = E_ERR;
295 continue;
296 }
297 while ((dirEntry = readdir(fileDir)) != nullptr) {
298 if ((strcmp(dirEntry->d_name, ".") == 0) || (strcmp(dirEntry->d_name, "..") == 0)) {
299 continue;
300 }
301 std::string fileName = dir + "/" + dirEntry->d_name;
302 if (stat(fileName.c_str(), &st) != 0) {
303 MEDIA_ERR_LOG("getting file: %{private}s stat fail, error: %s", fileName.c_str(), strerror(errno));
304 result = E_ERR;
305 continue;
306 }
307 if (st.st_mode & S_IFDIR) {
308 dirPathList.push_front(fileName);
309 }
310 if (EnableAcl(fileName, aclAttrName, permission, groupId) != E_OK) {
311 MEDIA_ERR_LOG("Failed to set the acl permission for the %{private}s", fileName.c_str());
312 result = E_ERR;
313 } else {
314 MEDIA_INFO_LOG("acl set succeed %{public}s", fileName.c_str());
315 }
316 }
317 closedir(fileDir);
318 }
319 return result;
320 }
321
EnableAcl(const std::string & path,const char * aclAttrName,const uint16_t & permission,uint32_t groupId)322 int32_t Acl::EnableAcl(const std::string& path, const char* aclAttrName, const uint16_t& permission, uint32_t groupId)
323 {
324 AclXattrEntry entry = {};
325 InitSandboxGroupEntry(entry, groupId, permission);
326 int32_t err = EntryInsert(entry, path, aclAttrName);
327 if (err != E_OK) {
328 MEDIA_ERR_LOG("Failed to set the acl permission for path %{private}s", path.c_str());
329 return E_ERR;
330 }
331 return E_OK;
332 }
333
AclSetDatabase()334 int32_t Acl::AclSetDatabase()
335 {
336 if (EnableAcl(MEDIA_DB_DIR, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
337 ACL_PERM::Value::EXECUTE, MEDIA_DB_ACL_GROUP) != E_OK) {
338 MEDIA_ERR_LOG("Failed to set the acl permission for the DB dir");
339 return E_ERR;
340 }
341 if (RecursiveEnableAcl(MEDIA_DB_DIR, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
342 ACL_PERM::Value::EXECUTE, MEDIA_DB_ACL_GROUP) != E_OK) {
343 MEDIA_ERR_LOG("Failed to set the acl permission for the DB sub dir");
344 return E_ERR;
345 }
346 return E_OK;
347 }
348
IsDirExist(const std::string & path)349 bool IsDirExist(const std::string &path)
350 {
351 DIR *dir = opendir(path.c_str());
352 if (dir == nullptr) {
353 MEDIA_ERR_LOG("Failed to open dir:%{private}s, errno: %{public}d, Just return dir NOT empty.",
354 path.c_str(), errno);
355 return false;
356 }
357 if (closedir(dir) < 0) {
358 MEDIA_ERR_LOG("Failed to closedir: %{private}s, errno: %{public}d.", path.c_str(), errno);
359 return false;
360 }
361 return true;
362 }
363
AclSetSlaveDatabase()364 int32_t Acl::AclSetSlaveDatabase()
365 {
366 if (!IsDirExist(MEDIA_DB_DIR)) {
367 MEDIA_ERR_LOG("Media database directory is not exist");
368 return E_ERR;
369 }
370
371 if (EnableAcl(MEDIA_DB_FILE_SLAVE, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
372 ACL_PERM::Value::EXECUTE, DDMS_ACL_GROUP) != E_OK) {
373 MEDIA_ERR_LOG("Failed to set the acl permission for the DB file");
374 return E_ERR;
375 }
376 if (EnableAcl(MEDIA_DB_FILE_SLAVE_SHM, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
377 ACL_PERM::Value::EXECUTE, DDMS_ACL_GROUP) != E_OK) {
378 MEDIA_ERR_LOG("Failed to set the acl permission for the DB file");
379 return E_ERR;
380 }
381 if (EnableAcl(MEDIA_DB_FILE_SLAVE_WAL, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
382 ACL_PERM::Value::EXECUTE, DDMS_ACL_GROUP) != E_OK) {
383 MEDIA_ERR_LOG("Failed to set the acl permission for the DB file");
384 return E_ERR;
385 }
386
387 return E_OK;
388 }
389
AclFromFile(const std::string & file)390 Acl AclFromFile(const std::string& file)
391 {
392 Acl acl;
393 char buf[BUF_SIZE] = { 0 };
394 ssize_t len = getxattr(file.c_str(), ACL_XATTR_ACCESS, buf, BUF_SIZE);
395 if (len != -1) {
396 acl.DeSerialize(buf, BUF_SIZE);
397 return acl;
398 }
399 return AclFromMode(file);
400 }
401
EntryInsert(AclXattrEntry & entry,const std::string & path,const char * aclAttrName)402 int32_t Acl::EntryInsert(AclXattrEntry& entry, const std::string& path, const char* aclAttrName)
403 {
404 /* init acl from file's mode */
405 Acl acl;
406 if (strcmp(aclAttrName, ACL_XATTR_ACCESS) == 0) {
407 acl = AclFromFile(path);
408 } else {
409 acl = AclFromMode(path);
410 }
411 if (acl.IsEmpty()) {
412 MEDIA_ERR_LOG("Failed to generate ACL from file's mode: %{public}s", std::strerror(errno));
413 return E_ERR;
414 }
415
416 /* add new entry into set */
417 if (acl.InsertEntry(entry) == E_ERR) {
418 MEDIA_ERR_LOG("Failed to insert new entry into ACL: %{public}s", std::strerror(errno));
419 return E_ERR;
420 }
421
422 /* in case that this acl has no OTHER TAG and can't be serialized */
423 acl.InsertEntry(
424 {
425 .tag = ACL_TAG::OTHER,
426 .perm = S_IXOTH,
427 .id = ACL_UNDEFINED_ID,
428 }
429 );
430
431 acl.InsertEntry(
432 {
433 .tag = ACL_TAG::GROUP_OBJ,
434 .perm = S_IRWXG >> 3,
435 .id = ACL_UNDEFINED_ID,
436 }
437 );
438
439 /* transform to binary and write to file */
440 size_t bufSize;
441 char *buf = acl.Serialize(bufSize);
442 if (buf == nullptr) {
443 MEDIA_ERR_LOG("Failed to serialize ACL into binary: %{public}s", std::strerror(errno));
444 return E_ERR;
445 }
446 if (setxattr(path.c_str(), aclAttrName, buf, bufSize, 0) == -1) {
447 MEDIA_ERR_LOG("Failed to write into file's xattr: %{public}s", std::strerror(errno));
448 return E_ERR;
449 }
450 return E_OK;
451 }
452
Print(const std::string & path)453 void Acl::Print(const std::string& path)
454 {
455 MEDIA_DEBUG_LOG("Version: %#x, path: %{private}s\n", header.version, path.c_str());
456 for (const auto &e: entries) {
457 MEDIA_DEBUG_LOG("---------------ACL ATTR---------------\n"
458 "tag: %s\n"
459 "perm: %hx\n"
460 "id: %#x (%u)\n",
461 ACL_TAG_STR.at(e.tag), (uint16_t)e.perm, e.id, e.id);
462 }
463 }
464
~Acl()465 Acl::~Acl()
466 {
467 if (buf != nullptr) {
468 delete[] buf;
469 buf = nullptr;
470 }
471 }
472 } // MEDIA
473 } // OHOS
474