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 static_assert(std::is_trivially_copyable_v<AclXattrHeader> == true);
163 auto err = memcpy_s(buf, bufSize, &header, sizeof(AclXattrHeader));
164 if (err != EOK) {
165 errno = err;
166 delete[] buf;
167 buf = nullptr;
168 return nullptr;
169 }
170
171 size_t restSize = bufSize - sizeof(AclXattrHeader);
172 AclXattrEntry *ptr = reinterpret_cast<AclXattrEntry*>(buf + sizeof(AclXattrHeader));
173 for (const auto &e : entries) {
174 static_assert(std::is_trivially_copyable_v<AclXattrEntry> == true);
175 auto err = memcpy_s(ptr++, restSize, &e, sizeof(AclXattrEntry));
176 if (err != EOK) {
177 errno = err;
178 delete[] buf;
179 buf = nullptr;
180 return nullptr;
181 }
182 restSize -= sizeof(AclXattrEntry);
183 }
184 return buf;
185 }
186
DeSerialize(const char * aclHead,size_t size)187 int Acl::DeSerialize(const char* aclHead, size_t size)
188 {
189 if (size > BUF_MAX_SIZE || size < sizeof(AclXattrHeader)) {
190 errno = EINVAL;
191 return errno;
192 }
193 header = *reinterpret_cast<const AclXattrHeader*>(aclHead);
194 size -= sizeof(AclXattrHeader);
195 aclHead += sizeof(AclXattrHeader);
196
197 /*
198 * `entry->tag != ACL_TAG::UNDEFINED` is unreliable outside the buffer, so check
199 * it after checking the size of remaining buffer.
200 */
201 for (const AclXattrEntry *entry = reinterpret_cast<const AclXattrEntry*>(aclHead);
202 size >= sizeof(AclXattrEntry) && entry->tag != ACL_TAG::UNDEFINED; entry++) {
203 InsertEntry(*entry);
204 size -= sizeof(AclXattrEntry);
205 }
206 if (size < 0) {
207 entries.clear();
208 header = { 0 };
209 errno = EINVAL;
210 return errno;
211 }
212
213 return E_OK;
214 }
215
AclFromMode(const std::string & file)216 Acl AclFromMode(const std::string &file)
217 {
218 Acl acl;
219 struct stat st;
220
221 if (stat(file.c_str(), &st) == -1) {
222 return acl;
223 }
224
225 acl.InsertEntry(
226 {
227 .tag = ACL_TAG::USER_OBJ,
228 .perm = (st.st_mode & S_IRWXU) >> 6,
229 .id = ACL_UNDEFINED_ID,
230 }
231 );
232 acl.InsertEntry(
233 {
234 .tag = ACL_TAG::GROUP_OBJ,
235 .perm = (st.st_mode & S_IRWXG) >> 3,
236 .id = ACL_UNDEFINED_ID,
237 }
238 );
239 acl.InsertEntry(
240 {
241 .tag = ACL_TAG::OTHER,
242 .perm = (st.st_mode & S_IRWXO),
243 .id = ACL_UNDEFINED_ID,
244 }
245 );
246
247 return acl;
248 }
249
InitSandboxEntry(AclXattrEntry & entry)250 void InitSandboxEntry(AclXattrEntry &entry)
251 {
252 entry.tag = ACL_TAG::GROUP;
253 entry.id = THUMB_ACL_GROUP;
254 entry.perm.SetRead();
255 entry.perm.SetExecute();
256 }
257
InitSandboxGroupEntry(AclXattrEntry & entry,uint32_t id,uint16_t access)258 void InitSandboxGroupEntry(AclXattrEntry& entry, uint32_t id, uint16_t access)
259 {
260 entry.tag = ACL_TAG::GROUP;
261 entry.id = id;
262 if (access & ACL_PERM::Value::READ) {
263 entry.perm.SetRead();
264 }
265 if (access & ACL_PERM::Value::WRITE) {
266 entry.perm.SetWrite();
267 }
268 if (access & ACL_PERM::Value::EXECUTE) {
269 entry.perm.SetExecute();
270 }
271 }
272
AclSetDefault()273 int32_t Acl::AclSetDefault()
274 {
275 if (EnableAcl(THUMB_DIR, ACL_XATTR_DEFAULT, ACL_PERM::Value::READ | ACL_PERM::Value::EXECUTE,
276 MEDIA_DB_ACL_GROUP) != E_OK) {
277 MEDIA_ERR_LOG("Failed to set the acl permission for the Photo dir");
278 return E_ERR;
279 }
280 return E_OK;
281 }
282
RecursiveEnableAcl(const std::string & path,const char * aclAttrName,const uint16_t & permission,uint32_t groupId)283 int32_t Acl::RecursiveEnableAcl(const std::string& path, const char* aclAttrName, const uint16_t& permission,
284 uint32_t groupId)
285 {
286 DIR* fileDir;
287 struct dirent* dirEntry;
288 struct stat st;
289 std::list<std::string> dirPathList{path};
290 int32_t result = E_OK;
291 while (!dirPathList.empty()) {
292 std::string dir = dirPathList.back();
293 dirPathList.pop_back();
294 if ((fileDir = opendir(dir.c_str())) == nullptr) {
295 MEDIA_ERR_LOG("dir not exist: %{private}s, error: %{public}s", dir.c_str(), strerror(errno));
296 result = E_ERR;
297 continue;
298 }
299 while ((dirEntry = readdir(fileDir)) != nullptr) {
300 if ((strcmp(dirEntry->d_name, ".") == 0) || (strcmp(dirEntry->d_name, "..") == 0)) {
301 continue;
302 }
303 std::string fileName = dir + "/" + dirEntry->d_name;
304 if (stat(fileName.c_str(), &st) != 0) {
305 MEDIA_ERR_LOG("getting file: %{private}s stat fail, error: %{public}s",
306 fileName.c_str(), strerror(errno));
307 result = E_ERR;
308 continue;
309 }
310 if (st.st_mode & S_IFDIR) {
311 dirPathList.push_front(fileName);
312 }
313 if (EnableAcl(fileName, aclAttrName, permission, groupId) != E_OK) {
314 MEDIA_ERR_LOG("Failed to set the acl permission for the %{private}s", fileName.c_str());
315 result = E_ERR;
316 } else {
317 MEDIA_INFO_LOG("acl set succeed %{private}s", fileName.c_str());
318 }
319 }
320 closedir(fileDir);
321 }
322 return result;
323 }
324
EnableAcl(const std::string & path,const char * aclAttrName,const uint16_t & permission,uint32_t groupId)325 int32_t Acl::EnableAcl(const std::string& path, const char* aclAttrName, const uint16_t& permission, uint32_t groupId)
326 {
327 AclXattrEntry entry = {};
328 InitSandboxGroupEntry(entry, groupId, permission);
329 int32_t err = EntryInsert(entry, path, aclAttrName);
330 if (err != E_OK) {
331 MEDIA_ERR_LOG("Failed to set the acl permission for path %{private}s", path.c_str());
332 return E_ERR;
333 }
334 return E_OK;
335 }
336
AclSetDatabase()337 int32_t Acl::AclSetDatabase()
338 {
339 if (EnableAcl(MEDIA_DB_DIR, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
340 ACL_PERM::Value::EXECUTE, MEDIA_DB_ACL_GROUP) != E_OK) {
341 MEDIA_ERR_LOG("Failed to set the acl permission for the DB dir");
342 return E_ERR;
343 }
344 if (RecursiveEnableAcl(MEDIA_DB_DIR, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
345 ACL_PERM::Value::EXECUTE, MEDIA_DB_ACL_GROUP) != E_OK) {
346 MEDIA_ERR_LOG("Failed to set the acl permission for the DB sub dir");
347 return E_ERR;
348 }
349 return E_OK;
350 }
351
IsDirExist(const std::string & path)352 bool IsDirExist(const std::string &path)
353 {
354 DIR *dir = opendir(path.c_str());
355 if (dir == nullptr) {
356 MEDIA_ERR_LOG("Failed to open dir:%{private}s, errno: %{public}d, Just return dir NOT empty.",
357 path.c_str(), errno);
358 return false;
359 }
360 if (closedir(dir) < 0) {
361 MEDIA_ERR_LOG("Failed to closedir: %{private}s, errno: %{public}d.", path.c_str(), errno);
362 }
363 return true;
364 }
365
AclSetSlaveDatabase()366 int32_t Acl::AclSetSlaveDatabase()
367 {
368 if (!IsDirExist(MEDIA_DB_DIR)) {
369 MEDIA_ERR_LOG("Media database directory is not exist");
370 return E_ERR;
371 }
372
373 if (EnableAcl(MEDIA_DB_FILE_SLAVE, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
374 ACL_PERM::Value::EXECUTE, DDMS_ACL_GROUP) != E_OK) {
375 MEDIA_ERR_LOG("Failed to set the acl permission for the DB file");
376 return E_ERR;
377 }
378 if (EnableAcl(MEDIA_DB_FILE_SLAVE_SHM, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
379 ACL_PERM::Value::EXECUTE, DDMS_ACL_GROUP) != E_OK) {
380 MEDIA_ERR_LOG("Failed to set the acl permission for the DB file");
381 return E_ERR;
382 }
383 if (EnableAcl(MEDIA_DB_FILE_SLAVE_WAL, ACL_XATTR_ACCESS, ACL_PERM::Value::READ | ACL_PERM::Value::WRITE |
384 ACL_PERM::Value::EXECUTE, DDMS_ACL_GROUP) != E_OK) {
385 MEDIA_ERR_LOG("Failed to set the acl permission for the DB file");
386 return E_ERR;
387 }
388
389 return E_OK;
390 }
391
AclFromFile(const std::string & file)392 Acl AclFromFile(const std::string& file)
393 {
394 Acl acl;
395 char buf[BUF_SIZE] = { 0 };
396 ssize_t len = getxattr(file.c_str(), ACL_XATTR_ACCESS, buf, BUF_SIZE);
397 if (len != -1) {
398 acl.DeSerialize(buf, BUF_SIZE);
399 return acl;
400 }
401 MEDIA_INFO_LOG("Failed to get ACL_XATTR_ACCESS from file: %{public}s", file.c_str());
402 return AclFromMode(file);
403 }
404
EntryInsert(AclXattrEntry & entry,const std::string & path,const char * aclAttrName)405 int32_t Acl::EntryInsert(AclXattrEntry& entry, const std::string& path, const char* aclAttrName)
406 {
407 /* init acl from file's mode */
408 Acl acl;
409 if (strcmp(aclAttrName, ACL_XATTR_ACCESS) == 0) {
410 acl = AclFromFile(path);
411 } else {
412 acl = AclFromMode(path);
413 }
414 if (acl.IsEmpty()) {
415 MEDIA_ERR_LOG("Failed to generate ACL from file's mode: %{public}s", std::strerror(errno));
416 return E_ERR;
417 }
418
419 /* add new entry into set */
420 if (acl.InsertEntry(entry) == E_ERR) {
421 MEDIA_ERR_LOG("Failed to insert new entry into ACL: %{public}s", std::strerror(errno));
422 return E_ERR;
423 }
424
425 /* in case that this acl has no OTHER TAG and can't be serialized */
426 acl.InsertEntry(
427 {
428 .tag = ACL_TAG::OTHER,
429 .perm = S_IXOTH,
430 .id = ACL_UNDEFINED_ID,
431 }
432 );
433
434 acl.InsertEntry(
435 {
436 .tag = ACL_TAG::GROUP_OBJ,
437 .perm = S_IRWXG >> 3,
438 .id = ACL_UNDEFINED_ID,
439 }
440 );
441
442 /* transform to binary and write to file */
443 size_t bufSize;
444 char *buf = acl.Serialize(bufSize);
445 if (buf == nullptr) {
446 MEDIA_ERR_LOG("Failed to serialize ACL into binary: %{public}s", std::strerror(errno));
447 return E_ERR;
448 }
449 if (setxattr(path.c_str(), aclAttrName, buf, bufSize, 0) == -1) {
450 MEDIA_ERR_LOG("Failed to write into file's xattr: %{public}s", std::strerror(errno));
451 return E_ERR;
452 }
453 return E_OK;
454 }
455
Print(const std::string & path)456 void Acl::Print(const std::string& path)
457 {
458 MEDIA_DEBUG_LOG("Version: %#x, path: %{private}s\n", header.version, path.c_str());
459 for (const auto &e: entries) {
460 MEDIA_DEBUG_LOG("---------------ACL ATTR---------------\n"
461 "tag: %s\n"
462 "perm: %hx\n"
463 "id: %#x (%u)\n",
464 ACL_TAG_STR.at(e.tag), (uint16_t)e.perm, e.id, e.id);
465 }
466 }
467
~Acl()468 Acl::~Acl()
469 {
470 if (buf != nullptr) {
471 delete[] buf;
472 buf = nullptr;
473 }
474 }
475 } // MEDIA
476 } // OHOS
477