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 <new>
19 #include <type_traits>
20 #include <sys/stat.h>
21 #include <sys/xattr.h>
22
23 #include "medialibrary_errno.h"
24 #include "media_log.h"
25 #include "securec.h"
26
27 namespace OHOS {
28 namespace Media {
ReCalcMaskPerm()29 ACL_PERM Acl::ReCalcMaskPerm()
30 {
31 ACL_PERM perm;
32 for (const auto &e : entries) {
33 if (e.tag == ACL_TAG::USER || e.tag == ACL_TAG::GROUP_OBJ || e.tag == ACL_TAG::GROUP) {
34 perm.Merge(e.perm);
35 }
36 }
37 return perm;
38 }
39
IsEmpty()40 bool Acl::IsEmpty()
41 {
42 return entries.empty();
43 }
44
IsValid()45 bool Acl::IsValid()
46 {
47 if (!entries.count(ACL_TAG::USER_OBJ) || !entries.count(ACL_TAG::GROUP_OBJ) ||
48 !entries.count(ACL_TAG::OTHER)) {
49 return false;
50 }
51 if (maskDemand && !entries.count(ACL_TAG::MASK)) {
52 return false;
53 }
54 return true;
55 }
56
CompareInsertEntry(const AclXattrEntry & entry)57 void Acl::CompareInsertEntry(const AclXattrEntry &entry)
58 {
59 if (entries.count(entry)) {
60 auto it = entries.find(entry);
61 entries.erase(it);
62 }
63 if (entry.perm.IsReadable() || entry.perm.IsWritable() || entry.perm.IsExecutable()) {
64 entries.insert(entry);
65 }
66 }
67
InsertEntry(const AclXattrEntry & entry)68 int Acl::InsertEntry(const AclXattrEntry &entry)
69 {
70 if (entries.size() >= ENTRIES_MAX_NUM) {
71 errno = EAGAIN;
72 return E_ERR;
73 }
74 CompareInsertEntry(entry); // must before ReCalcMaskPerm()
75
76 maskDemand++;
77 /*
78 * In either case there's no or already one ACL_MASK entry in the set,
79 * we need to re-calculate MASK's permission and *insert* it (to replace
80 * the old one in latter case since we can't change std::set's element
81 * in-place). So do the following unconditionally.
82 *
83 * Be warned: do _NOT_ combine the following into one line, otherwise
84 * you can't pass the !!genius!! CI coding style check.
85 */
86 CompareInsertEntry(
87 { ACL_TAG::MASK, ReCalcMaskPerm(), ACL_UNDEFINED_ID }
88 );
89 return E_OK;
90 }
91
Serialize(size_t & bufSize)92 char *Acl::Serialize(size_t &bufSize)
93 {
94 if (!IsValid()) {
95 errno = EINVAL;
96 return nullptr;
97 }
98
99 /* clear possible previous allocation */
100 if (buf != nullptr) {
101 delete[] buf;
102 buf = nullptr;
103 }
104
105 bufSize = sizeof(AclXattrHeader) + sizeof(AclXattrEntry) * entries.size();
106 if (bufSize > BUF_MAX_SIZE) {
107 bufSize = 0;
108 errno = EINVAL;
109 return nullptr;
110 }
111 buf = new (std::nothrow) char[bufSize];
112 if (buf == nullptr) {
113 errno = ENOMEM;
114 return nullptr;
115 }
116 auto err = memcpy_s(buf, bufSize, &header, sizeof(AclXattrHeader));
117 if (err != EOK) {
118 errno = err;
119 delete[] buf;
120 buf = nullptr;
121 return nullptr;
122 }
123
124 size_t restSize = bufSize - sizeof(AclXattrHeader);
125 AclXattrEntry *ptr = reinterpret_cast<AclXattrEntry *>(buf + sizeof(AclXattrHeader));
126 for (const auto &e : entries) {
127 auto err = memcpy_s(ptr++, restSize, &e, sizeof(AclXattrEntry));
128 if (err != EOK) {
129 errno = err;
130 delete[] buf;
131 buf = nullptr;
132 return nullptr;
133 }
134 restSize -= sizeof(AclXattrEntry);
135 }
136 return buf;
137 }
138
AclFromMode(const std::string & file)139 Acl AclFromMode(const std::string &file)
140 {
141 Acl acl;
142 struct stat st;
143
144 if (stat(file.c_str(), &st) == -1) {
145 return acl;
146 }
147
148 acl.InsertEntry(
149 {
150 .tag = ACL_TAG::USER_OBJ,
151 .perm = (st.st_mode & S_IRWXU) >> 6,
152 .id = ACL_UNDEFINED_ID,
153 }
154 );
155 acl.InsertEntry(
156 {
157 .tag = ACL_TAG::GROUP_OBJ,
158 .perm = (st.st_mode & S_IRWXG) >> 3,
159 .id = ACL_UNDEFINED_ID,
160 }
161 );
162 acl.InsertEntry(
163 {
164 .tag = ACL_TAG::OTHER,
165 .perm = (st.st_mode & S_IRWXO),
166 .id = ACL_UNDEFINED_ID,
167 }
168 );
169
170 return acl;
171 }
172
InitSandboxEntry(AclXattrEntry & entry)173 void InitSandboxEntry(AclXattrEntry &entry)
174 {
175 entry.tag = ACL_TAG::GROUP;
176 entry.id = THUMB_ACL_GROUP;
177 entry.perm.SetR();
178 entry.perm.SetE();
179 }
180
AclSetDefault()181 int32_t Acl::AclSetDefault()
182 {
183 AclXattrEntry entry = {};
184 InitSandboxEntry(entry);
185
186 /* init acl from file's mode */
187 Acl acl = AclFromMode(THUMB_DIR);
188 if (acl.IsEmpty()) {
189 MEDIA_ERR_LOG("Failed to generate ACL from file's mode: %{public}s", std::strerror(errno));
190 return E_ERR;
191 }
192
193 /* add new entry into set */
194 if (acl.InsertEntry(entry) == E_ERR) {
195 MEDIA_ERR_LOG("Failed to insert new entry into ACL: %{public}s", std::strerror(errno));
196 return E_ERR;
197 }
198
199 /* transform to binary and write to file */
200 size_t bufSize;
201 char *buf = acl.Serialize(bufSize);
202 if (buf == nullptr) {
203 MEDIA_ERR_LOG("Failed to serialize ACL into binary: %{public}s", std::strerror(errno));
204 return E_ERR;
205 }
206 if (setxattr(THUMB_DIR.c_str(), ACL_XATTR_DEFAULT, buf, bufSize, 0) == -1) {
207 MEDIA_ERR_LOG("Failed to write into file's xattr: %{public}s", std::strerror(errno));
208 return E_ERR;
209 }
210 return E_OK;
211 }
212
~Acl()213 Acl::~Acl()
214 {
215 if (buf != nullptr) {
216 delete[] buf;
217 buf = nullptr;
218 }
219 }
220 } // MEDIA
221 } // OHOS
222