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