1 /*
2 * Copyright (c) 2022 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 <cerrno>
16 #include <string>
17
18 #include <grp.h>
19 #include <pwd.h>
20 #include <sys/stat.h>
21 #include <sys/xattr.h>
22
23 #include "file_sharing/acl.h"
24 #include "securec.h"
25 #include "storage_service_log.h"
26 #include "utils/file_utils.h"
27
28 namespace OHOS {
29 namespace StorageDaemon {
30 namespace {
AclEntryParseTag(const std::string & tagTxt,AclXattrEntry & entry)31 int AclEntryParseTag(const std::string &tagTxt, AclXattrEntry &entry)
32 {
33 switch (tagTxt[0]) {
34 case 'u':
35 entry.tag = ACL_TAG::USER;
36 break;
37 case 'g':
38 entry.tag = ACL_TAG::GROUP;
39 break;
40 case 'm':
41 entry.tag = ACL_TAG::MASK;
42 break;
43 case 'o':
44 entry.tag = ACL_TAG::OTHER;
45 break;
46 default:
47 errno = EINVAL;
48 return -1;
49 }
50 return 0;
51 }
52
ParseNumericId(const std::string & idTxt,unsigned int & outId)53 bool ParseNumericId(const std::string &idTxt, unsigned int &outId)
54 {
55 char *p;
56 long converted = strtol(idTxt.c_str(), &p, 10);
57 if (*p == '\0' && converted >= 0 && converted <= UINT_MAX) {
58 outId = static_cast<unsigned int>(converted);
59 return true;
60 }
61 return false;
62 }
63
AclEntryParseId(const std::string & idTxt,AclXattrEntry & entry)64 int AclEntryParseId(const std::string &idTxt, AclXattrEntry &entry)
65 {
66 struct passwd *pwd = nullptr;
67 struct group *grp = nullptr;
68
69 switch (entry.tag) {
70 case ACL_TAG::USER:
71 if (idTxt.empty()) {
72 entry.tag = ACL_TAG::USER_OBJ;
73 return 0;
74 }
75 if (ParseNumericId(idTxt, entry.id)) {
76 break;
77 }
78 if ((pwd = getpwnam(idTxt.c_str())) == nullptr) {
79 return -1;
80 }
81 entry.id = pwd->pw_uid;
82 (void)memset_s(pwd, sizeof(struct passwd), 0, sizeof(struct passwd));
83 break;
84 case ACL_TAG::GROUP:
85 if (idTxt.empty()) {
86 entry.tag = ACL_TAG::GROUP_OBJ;
87 return 0;
88 }
89 if (ParseNumericId(idTxt, entry.id)) {
90 break;
91 }
92 if ((grp = getgrnam(idTxt.c_str())) == nullptr) {
93 return -1;
94 }
95 entry.id = grp->gr_gid;
96 (void)memset_s(grp, sizeof(struct group), 0, sizeof(struct group));
97 break;
98 default:
99 if (!idTxt.empty()) {
100 errno = EINVAL;
101 return -1;
102 }
103 break;
104 }
105 return 0;
106 }
107
AclEntryParsePerm(const std::string & permTxt,AclXattrEntry & entry)108 int AclEntryParsePerm(const std::string &permTxt, AclXattrEntry &entry)
109 {
110 if (permTxt.empty()) {
111 errno = EINVAL;
112 return -1;
113 }
114 for (const char &c : permTxt) {
115 switch (c) {
116 case 'r':
117 entry.perm.SetR();
118 break;
119 case 'w':
120 entry.perm.SetW();
121 break;
122 case 'x':
123 entry.perm.SetE();
124 break;
125 case '-':
126 break;
127 default:
128 errno = EINVAL;
129 return -1;
130 }
131 }
132 return 0;
133 }
134
AclEntryParseText(const std::string & entryTxt)135 AclXattrEntry AclEntryParseText(const std::string &entryTxt)
136 {
137 AclXattrEntry entry = {};
138 std::string::size_type last = 0;
139 std::string::size_type pos;
140
141 if ((pos = entryTxt.find(":", last)) == std::string::npos) {
142 LOGE("Invalid ACL entry format");
143 return {};
144 }
145 const std::string tagTxt = entryTxt.substr(last, pos-last);
146 if (AclEntryParseTag(tagTxt, entry) == -1) {
147 LOGE("Unknown tag: %{public}s", tagTxt.c_str());
148 return {};
149 }
150 last = pos + 1;
151
152 if ((pos = entryTxt.find(":", last)) == std::string::npos) {
153 LOGE("Invalid ACL entry format");
154 return {};
155 }
156 const std::string idTxt = entryTxt.substr(last, pos-last);
157 if (AclEntryParseId(idTxt, entry) == -1) {
158 switch (entry.tag) {
159 case ACL_TAG::USER:
160 case ACL_TAG::GROUP:
161 LOGE("Error in processing qualifier: \"%{public}s\": %{public}s",
162 idTxt.c_str(),
163 errno == 0 ? "user/group not found" : std::strerror(errno));
164 break;
165 default:
166 LOGE("Qualifier only allowed for USER & GROUP");
167 break;
168 }
169 return {};
170 }
171 last = pos + 1;
172
173 const std::string permTxt = entryTxt.substr(last); // take substr till the end
174 if (AclEntryParsePerm(permTxt, entry) == -1) {
175 LOGE("Wrong permission: %{public}s", permTxt.c_str());
176 return {};
177 }
178
179 return entry;
180 }
181
AclFromMode(const std::string & file)182 Acl AclFromMode(const std::string &file)
183 {
184 Acl acl;
185 struct stat st;
186
187 if (stat(file.c_str(), &st) == -1) {
188 return acl;
189 }
190
191 acl.InsertEntry(
192 { .tag = ACL_TAG::USER_OBJ,
193 .perm = (st.st_mode & S_IRWXU) >> 6,
194 .id = ACL_UNDEFINED_ID, }
195 );
196 acl.InsertEntry(
197 { .tag = ACL_TAG::GROUP_OBJ,
198 .perm = (st.st_mode & S_IRWXG) >> 3,
199 .id = ACL_UNDEFINED_ID, }
200 );
201 acl.InsertEntry(
202 { .tag = ACL_TAG::OTHER,
203 .perm = (st.st_mode & S_IRWXO),
204 .id = ACL_UNDEFINED_ID, }
205 );
206
207 return acl;
208 }
209 } // anonymous namespace
210
AclSetDefault(const std::string & targetFile,const std::string & entryTxt)211 int AclSetDefault(const std::string &targetFile, const std::string &entryTxt)
212 {
213 if (!IsDir(targetFile)) {
214 LOGE("Failed to confirm %{private}s is a directory: %{public}s",
215 targetFile.c_str(),
216 errno == 0 ? "file exists but isn't a directory" : std::strerror(errno));
217 return -1;
218 }
219
220 /* parse text */
221 AclXattrEntry entry = AclEntryParseText(entryTxt);
222 if (!entry.IsValid()) {
223 LOGE("Failed to parse entry text: %{public}s", std::strerror(errno));
224 return -1;
225 }
226
227 /* init acl from file's mode */
228 Acl acl = AclFromMode(targetFile);
229 if (acl.IsEmpty()) {
230 LOGE("Failed to generate ACL from file's mode: %{public}s", std::strerror(errno));
231 return -1;
232 }
233
234 /* add new entry into set */
235 if (acl.InsertEntry(entry) == -1) {
236 LOGE("Failed to insert new entry into ACL: %{public}s", std::strerror(errno));
237 return -1;
238 }
239
240 /* transform to binary and write to file */
241 size_t bufSize;
242 char *buf = acl.Serialize(bufSize);
243 if (buf == nullptr) {
244 LOGE("Failed to serialize ACL into binary: %{public}s", std::strerror(errno));
245 return -1;
246 }
247 if (setxattr(targetFile.c_str(), ACL_XATTR_DEFAULT, buf, bufSize, 0) == -1) {
248 LOGE("Failed to write into file's xattr: %{public}s", std::strerror(errno));
249 return -1;
250 }
251
252 return 0;
253 }
254 } // namespace StorageDaemon
255 } // namespace OHOS
256