1 /*
2 * Copyright (C) 2024 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
16 #define LOG_TAG "DataBaseUtilsAcl"
17 #include "acl.h"
18
19 #include "securec.h"
20 #include <cerrno>
21 #include <cstring>
22 #include <dlfcn.h>
23 #include <functional>
24 #include <iosfwd>
25 #include <memory>
26 #include <new>
27 #include <sys/stat.h>
28 #include <sys/xattr.h>
29 #include <type_traits>
30 #include "log_print.h"
31
32
33 namespace OHOS {
34 namespace DATABASE_UTILS {
35 using namespace DistributedKv;
Acl(const std::string & path)36 Acl::Acl(const std::string &path) : path_(path), hasError_(false)
37 {
38 /* init acl from file's defaule or mode*/
39 AclFromDefault();
40 }
41
Acl()42 Acl::Acl()
43 {
44 }
45
ReCalcMaskPerm()46 ACL_PERM Acl::ReCalcMaskPerm()
47 {
48 ACL_PERM perm;
49 for (const auto &e : entries_) {
50 if (e.tag_ == ACL_TAG::USER || e.tag_ == ACL_TAG::GROUP_OBJ || e.tag_ == ACL_TAG::GROUP) {
51 perm.Merge(e.perm_);
52 }
53 }
54 return perm;
55 }
56
IsEmpty()57 bool Acl::IsEmpty()
58 {
59 return entries_.empty();
60 }
61
CompareInsertEntry(const AclXattrEntry & entry)62 void Acl::CompareInsertEntry(const AclXattrEntry &entry)
63 {
64 if (entries_.count(entry)) {
65 auto it = entries_.find(entry);
66 entries_.erase(it);
67 }
68 bool isNecessary = (entry.tag_ == ACL_TAG::USER_OBJ ||
69 entry.tag_ == ACL_TAG::GROUP_OBJ ||
70 entry.tag_ == ACL_TAG::OTHER);
71 if (isNecessary || entry.perm_.IsReadable() || entry.perm_.IsWritable() || entry.perm_.IsExecutable()) {
72 entries_.insert(entry);
73 }
74 }
75
InsertEntry(const AclXattrEntry & entry)76 int Acl::InsertEntry(const AclXattrEntry &entry)
77 {
78 if (entries_.size() >= ENTRIES_MAX_NUM) {
79 return E_ERROR;
80 }
81 CompareInsertEntry(entry); // must before ReCalcMaskPerm()
82
83 maskDemand_++;
84 /*
85 * In either case there's no or already one ACL_MASK entry in the set,
86 * we need to re-calculate MASK's permission and *insert* it (to replace
87 * the old one in latter case since we can't change std::set's element
88 * in-place). So do the following unconditionally.
89 *
90 * Be warned: do _NOT_ combine the following into one line, otherwise
91 * you can't pass the !!genius!! CI coding style check.
92 */
93 CompareInsertEntry(AclXattrEntry(ACL_TAG::MASK, AclXattrEntry::ACL_UNDEFINED_ID, ReCalcMaskPerm()));
94 return E_OK;
95 }
96
Serialize(uint32_t & bufSize)97 std::unique_ptr<char[]> Acl::Serialize(uint32_t &bufSize)
98 {
99 bufSize = sizeof(AclXattrHeader) + sizeof(AclXattrEntry) * entries_.size();
100 if (bufSize > BUF_MAX_SIZE) {
101 bufSize = 0;
102 return nullptr;
103 }
104 auto buf = std::make_unique<char[]>(bufSize);
105 auto err = memcpy_s(buf.get(), bufSize, &header_, sizeof(AclXattrHeader));
106 if (err != EOK) {
107 bufSize = 0;
108 return nullptr;
109 }
110
111 int32_t restSize = static_cast<int32_t>(bufSize - sizeof(AclXattrHeader));
112 AclXattrEntry *ptr = reinterpret_cast<AclXattrEntry *>(buf.get() + sizeof(AclXattrHeader));
113 for (const auto &e : entries_) {
114 auto err = memcpy_s(ptr++, restSize, &e, sizeof(AclXattrEntry));
115 if (err != EOK) {
116 bufSize = 0;
117 return nullptr;
118 }
119 restSize -= sizeof(AclXattrEntry);
120 }
121 return buf;
122 }
123
DeSerialize(const char * p,int32_t bufSize)124 int Acl::DeSerialize(const char *p, int32_t bufSize)
125 {
126 header_ = *reinterpret_cast<const AclXattrHeader *>(p);
127 bufSize -= sizeof(AclXattrHeader);
128 p += sizeof(AclXattrHeader);
129
130 /*
131 * `e->tag != ACL_TAG::UNDEFINED` is unreliable outside the buffer, so check
132 * it after checking the size of remaining buffer.
133 */
134 for (const AclXattrEntry *e = reinterpret_cast<const AclXattrEntry *>(p);
135 bufSize >= static_cast<int32_t>(sizeof(AclXattrEntry)) && e->tag_ != ACL_TAG::UNDEFINED;
136 e++) {
137 InsertEntry(*e);
138 bufSize -= sizeof(AclXattrEntry);
139 }
140 if (bufSize < 0) {
141 entries_.clear();
142 header_ = { 0 };
143 return -1;
144 }
145
146 return 0;
147 }
148
AclFromDefault()149 void Acl::AclFromDefault()
150 {
151 char buf[BUF_SIZE] = { 0 };
152 ssize_t len = getxattr(path_.c_str(), ACL_XATTR_DEFAULT, buf, BUF_SIZE);
153 if (len != -1) {
154 DeSerialize(buf, BUF_SIZE);
155 } else if (errno == ENODATA) {
156 AclFromMode();
157 } else {
158 hasError_ = true;
159 ZLOGW("getxattr failed. error %{public}s path %{public}s", std::strerror(errno), path_.c_str());
160 }
161 }
162
AclFromMode()163 void Acl::AclFromMode()
164 {
165 struct stat st;
166 if (stat(path_.c_str(), &st) == -1) {
167 return;
168 }
169
170 InsertEntry(AclXattrEntry(ACL_TAG::USER_OBJ, AclXattrEntry::ACL_UNDEFINED_ID,
171 (st.st_mode & S_IRWXU) >> USER_OFFSET));
172 InsertEntry(AclXattrEntry(ACL_TAG::GROUP_OBJ, AclXattrEntry::ACL_UNDEFINED_ID,
173 (st.st_mode & S_IRWXG) >> GROUP_OFFSET));
174 InsertEntry(AclXattrEntry(ACL_TAG::OTHER, AclXattrEntry::ACL_UNDEFINED_ID,
175 (st.st_mode & S_IRWXO)));
176 }
177
SetDefault()178 int32_t Acl::SetDefault()
179 {
180 if (IsEmpty()) {
181 ZLOGE("Failed to generate ACL from file's mode: %{public}s", std::strerror(errno));
182 return E_ERROR;
183 }
184
185 /* transform to binary and write to file */
186 uint32_t bufSize;
187 auto buf = Serialize(bufSize);
188 if (buf == nullptr) {
189 ZLOGE("Failed to serialize ACL into binary: %{public}s", std::strerror(errno));
190 return E_ERROR;
191 }
192 if (setxattr(path_.c_str(), ACL_XATTR_DEFAULT, buf.get(), bufSize, 0) == -1) {
193 ZLOGE("Failed to write into file's xattr: %{public}s", std::strerror(errno));
194 return E_ERROR;
195 }
196 return E_OK;
197 }
198
SetDefaultGroup(const uint32_t gid,const uint16_t mode)199 int32_t Acl::SetDefaultGroup(const uint32_t gid, const uint16_t mode)
200 {
201 return InsertEntry(AclXattrEntry(ACL_TAG::GROUP, gid, mode));
202 }
203
SetDefaultUser(const uint32_t uid,const uint16_t mode)204 int32_t Acl::SetDefaultUser(const uint32_t uid, const uint16_t mode)
205 {
206 return InsertEntry(AclXattrEntry(ACL_TAG::USER, uid, mode));
207 }
208
~Acl()209 Acl::~Acl()
210 {
211 if (!hasError_) {
212 SetDefault();
213 }
214 }
215
HasEntry(const AclXattrEntry & Acl)216 bool Acl::HasEntry(const AclXattrEntry &Acl)
217 {
218 return entries_.find(Acl) != entries_.end();
219 }
220 }
221 }