• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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