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 }