• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-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 #include <grp.h>
17 #include <pwd.h>
18 #include <sys/stat.h>
19 #include <sys/xattr.h>
20 
21 #include "file_sharing/acl.h"
22 #include "securec.h"
23 #include "storage_service_log.h"
24 #include "utils/file_utils.h"
25 
26 constexpr int BUF_SIZE = 400;
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 = nullptr;
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 
AclFromFile(const std::string & file)210 Acl AclFromFile(const std::string &file)
211 {
212     Acl acl;
213     char buf[BUF_SIZE] = { 0 };
214     ssize_t len = getxattr(file.c_str(), ACL_XATTR_ACCESS, buf, BUF_SIZE);
215     if (len != -1) {
216         acl.DeSerialize(buf, BUF_SIZE);
217         return acl;
218     }
219     return AclFromMode(file);
220 }
221 
222 } // anonymous namespace
223 
AclSetAttribution(const std::string & targetFile,const std::string & entryTxt,const char * aclAttrName)224 int AclSetAttribution(const std::string &targetFile, const std::string &entryTxt, const char *aclAttrName)
225 {
226     if (strcmp(aclAttrName, ACL_XATTR_ACCESS) != 0 && !IsDir(targetFile)) {
227         LOGE("Failed to confirm %{private}s is a directory: %{public}s",
228             targetFile.c_str(),
229             errno == 0 ? "file exists but isn't a directory" : std::strerror(errno));
230         return -1;
231     }
232 
233     /* parse text */
234     AclXattrEntry entry = AclEntryParseText(entryTxt);
235     if (!entry.IsValid()) {
236         LOGE("Failed to parse entry text: %{public}s", std::strerror(errno));
237         return -1;
238     }
239 
240     /* init acl from file's mode */
241     Acl acl;
242     if (strcmp(aclAttrName, ACL_XATTR_ACCESS) == 0) {
243         acl = AclFromFile(targetFile);
244     } else {
245         acl = AclFromMode(targetFile);
246     }
247     if (acl.IsEmpty()) {
248         LOGE("Failed to generate ACL from file's mode: %{public}s", std::strerror(errno));
249         return -1;
250     }
251 
252     /* add new entry into set */
253     if (acl.InsertEntry(entry) == -1) {
254         LOGE("Failed to insert new entry into ACL: %{public}s", std::strerror(errno));
255         return -1;
256     }
257 
258     /* transform to binary and write to file */
259     size_t bufSize;
260     char *buf = acl.Serialize(bufSize);
261     if (buf == nullptr) {
262         LOGE("Failed to serialize ACL into binary: %{public}s, bufSize: %{public}zu",
263             std::strerror(errno), bufSize);
264         return -1;
265     }
266     if (setxattr(targetFile.c_str(), aclAttrName, buf, bufSize, 0) == -1) {
267         LOGE("Failed to write into file's xattr: %{public}s", std::strerror(errno));
268         return -1;
269     }
270     return 0;
271 }
272 
AclSetDefault(const std::string & targetFile,const std::string & entryTxt)273 int AclSetDefault(const std::string &targetFile, const std::string &entryTxt)
274 {
275     return AclSetAttribution(targetFile, entryTxt, ACL_XATTR_DEFAULT);
276 }
277 
AclSetAccess(const std::string & targetFile,const std::string & entryTxt)278 int AclSetAccess(const std::string &targetFile, const std::string &entryTxt)
279 {
280     return AclSetAttribution(targetFile, entryTxt, ACL_XATTR_ACCESS);
281 }
282 } // namespace StorageDaemon
283 } // namespace OHOS
284