1 /*
2 Copyright 2017 Skytechnology sp. z o.o.
3
4 This file is part of LizardFS.
5
6 LizardFS is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, version 3.
9
10 LizardFS is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with LizardFS. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "common/platform.h"
20
21 #ifdef __APPLE__
22
23 #include "mount/osx_acl_converter.h"
24
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <stdio.h>
30 #include <membership.h>
31 #include <array>
32
33 using namespace osxAclConverter;
34
35 static std::array<std::pair<uint32_t, acl_perm_t>, 17> kPermLookup = {{
36 {RichACL::Ace::kReadData, ACL_READ_DATA},
37 {RichACL::Ace::kWriteData, ACL_WRITE_DATA},
38 {RichACL::Ace::kAddFile, ACL_ADD_FILE},
39 {RichACL::Ace::kAppendData, ACL_WRITE_DATA},
40 {RichACL::Ace::kReadNamedAttrs, ACL_READ_ATTRIBUTES},
41 {RichACL::Ace::kWriteNamedAttrs, ACL_WRITE_ATTRIBUTES},
42 {RichACL::Ace::kExecute, ACL_EXECUTE},
43 {RichACL::Ace::kDeleteChild, ACL_DELETE_CHILD},
44 {RichACL::Ace::kReadAttributes, ACL_READ_ATTRIBUTES},
45 {RichACL::Ace::kWriteAttributes, ACL_WRITE_ATTRIBUTES},
46 {RichACL::Ace::kWriteRetention, ACL_WRITE_ATTRIBUTES},
47 {RichACL::Ace::kWriteRetentionHold, ACL_WRITE_ATTRIBUTES},
48 {RichACL::Ace::kDelete, ACL_DELETE},
49 {RichACL::Ace::kReadAcl, ACL_READ_SECURITY},
50 {RichACL::Ace::kWriteAcl, ACL_WRITE_SECURITY},
51 {RichACL::Ace::kWriteOwner, ACL_CHANGE_OWNER},
52 {RichACL::Ace::kSynchronize, ACL_SYNCHRONIZE}
53 }};
54
osxPermsetToRichACLMask(acl_permset_t permset)55 static uint32_t osxPermsetToRichACLMask(acl_permset_t permset) {
56 uint32_t mask = 0;
57 for (const auto &perm_entry : kPermLookup) {
58 if (acl_get_perm_np(permset, perm_entry.second)) {
59 mask |= perm_entry.first;
60 }
61 }
62
63 return mask;
64 }
65
convertOsxEntryToRichACL(acl_entry_t entry)66 static RichACL::Ace convertOsxEntryToRichACL(acl_entry_t entry) {
67 RichACL::Ace ace;
68 ace.id = RichACL::Ace::kInvalidId;
69
70 acl_tag_t tag;
71 acl_permset_t permset;
72 int ret;
73
74 uuid_t *uu;
75 ret = acl_get_tag_type(entry, &tag);
76 if (ret < 0) {
77 lzfs_pretty_syslog(LOG_WARNING, "Failed to get tag type from ACL entry");
78 return ace;
79 }
80 uu = (uuid_t *)acl_get_qualifier(entry);
81
82 if (tag == ACL_EXTENDED_ALLOW) {
83 ace.type = RichACL::Ace::kAccessAllowedAceType;
84 } else if (tag == ACL_EXTENDED_DENY) {
85 ace.type = RichACL::Ace::kAccessDeniedAceType;
86 } else {
87 lzfs_pretty_syslog(LOG_WARNING, "Only allow and deny entries are supported");
88 goto free_acl_entry;
89 }
90
91 id_t uid_or_gid;
92 int id_type;
93 ret = mbr_uuid_to_id(*uu, &uid_or_gid, &id_type);
94 if (ret < 0) {
95 lzfs_pretty_syslog(LOG_WARNING, "Failed to translate uuid to id");
96 goto free_acl_entry;
97 }
98
99 ret = acl_get_permset(entry, &permset);
100 if (ret < 0) {
101 lzfs_pretty_syslog(LOG_WARNING, "Failed to get permset from ACL entry");
102 goto free_acl_entry;
103 }
104 ace.mask = osxPermsetToRichACLMask(permset);
105
106 if (id_type == ID_TYPE_GID) {
107 ace.flags |= RichACL::Ace::kIdentifierGroup;
108 } else if (id_type != ID_TYPE_UID) {
109 lzfs_pretty_syslog(LOG_WARNING, "Unsupported id type for RichACL: %u", id_type);
110 goto free_acl_entry;
111 }
112
113 ace.id = uid_or_gid;
114
115 free_acl_entry:
116 acl_free(uu);
117 return ace;
118 }
119
convertOsxToRichACL(acl_t osx_acl)120 static RichACL convertOsxToRichACL(acl_t osx_acl) {
121 RichACL out;
122
123 int ret = 0;
124 int entry_id = ACL_FIRST_ENTRY;
125
126 while (ret == 0) {
127 acl_entry_t entry;
128 ret = acl_get_entry(osx_acl, entry_id, &entry);
129 if (ret != 0) {
130 break;
131 }
132 RichACL::Ace ace = convertOsxEntryToRichACL(entry);
133 if (ace.id != RichACL::Ace::kInvalidId) {
134 out.insert(ace);
135 }
136 entry_id = ACL_NEXT_ENTRY;
137 }
138
139 out.setFlags(RichACL::kAutoSetMode);
140 return out;
141 }
142
extractAclObject(const void * data,size_t)143 RichACL osxAclConverter::extractAclObject(const void *data, size_t /*size*/) {
144 RichACL out;
145 acl_t acl = acl_copy_int(data);
146 ssize_t osx_acl_size = acl_size(acl);
147 if (acl) {
148 out = convertOsxToRichACL(acl);
149 }
150 acl_free(acl);
151 if (osx_acl_size > 0 && out.size() == 0) {
152 throw AclConversionException("Extracting RichACL from OSX xattr failed");
153 }
154 return out;
155 }
156
createOsxEntry(acl_t osx_acl,const RichACL::Ace & ace)157 static int createOsxEntry(acl_t osx_acl, const RichACL::Ace &ace) {
158 static const char kNfsOwner[] = "OWNER@";
159 static const char kNfsGroup[] = "GROUP@";
160 static const char kNfsEveryone[] = "EVERYONE@";
161
162 acl_entry_t osx_entry;
163 acl_permset_t permset;
164 uuid_t uu;
165 int ret = 0;
166
167 ret = acl_create_entry(&osx_acl, &osx_entry);
168 if (ret < 0) {
169 lzfs_pretty_syslog(LOG_WARNING, "Failed to create ACL entry");
170 return ret;
171 }
172 ret = acl_get_permset(osx_entry, &permset);
173 if (ret < 0) {
174 lzfs_pretty_syslog(LOG_WARNING, "Failed to get ACL permset");
175 acl_delete_entry(osx_acl, osx_entry);
176 return ret;
177 }
178 for (const auto &perm_entry : kPermLookup) {
179 if (ace.mask & perm_entry.first) {
180 ret = acl_add_perm(permset, perm_entry.second);
181 if (ret < 0) {
182 lzfs_pretty_syslog(LOG_WARNING, "Failed to add permission to permset");
183 acl_delete_entry(osx_acl, osx_entry);
184 return ret;
185 }
186 }
187 }
188 acl_tag_t tag = ace.isAllow() ? ACL_EXTENDED_ALLOW : ACL_EXTENDED_DENY;
189 ret = acl_set_tag_type(osx_entry, tag);
190 if (ret < 0) {
191 lzfs_pretty_syslog(LOG_WARNING, "Failed to set tag type for ACL");
192 acl_delete_entry(osx_acl, osx_entry);
193 return ret;
194 }
195 if (ace.isOwner()) {
196 ret = mbr_identifier_to_uuid(ID_TYPE_USER_NFS, kNfsOwner, sizeof(kNfsOwner), uu);
197 } else if (ace.isGroup()) {
198 ret = mbr_identifier_to_uuid(ID_TYPE_GROUP_NFS, kNfsGroup, sizeof(kNfsGroup), uu);
199 } else if (ace.isEveryone()) {
200 ret = mbr_identifier_to_uuid(ID_TYPE_GROUP_NFS, kNfsEveryone, sizeof(kNfsEveryone), uu);
201 } else if (ace.isUnixGroup()) {
202 ret = mbr_gid_to_uuid(ace.id, uu);
203 } else if (ace.isUnixUser()) {
204 ret = mbr_uid_to_uuid(ace.id, uu);
205 } else {
206 ret = -1;
207 }
208
209 if (ret < 0) {
210 lzfs_pretty_syslog(LOG_WARNING, "Failed to translate uid/gid to uuid");
211 acl_delete_entry(osx_acl, osx_entry);
212 return ret;
213 }
214 ret = acl_set_qualifier(osx_entry, (void *)uu);
215 if (ret < 0) {
216 lzfs_pretty_syslog(LOG_WARNING, "Failed to set ACL qualifier");
217 acl_delete_entry(osx_acl, osx_entry);
218 return ret;
219 }
220 acl_flagset_t flagset;
221 ret = acl_get_flagset_np(osx_entry, &flagset);
222 if (ret < 0) {
223 lzfs_pretty_syslog(LOG_WARNING, "Failed to get ACL flagset");
224 acl_delete_entry(osx_acl, osx_entry);
225 return ret;
226 }
227 if (ace.flags & RichACL::Ace::kInheritOnlyAce) {
228 ret = acl_add_flag_np(flagset, ACL_ENTRY_ONLY_INHERIT);
229 }
230 if (ret == 0 && ace.flags & RichACL::Ace::kFileInheritAce) {
231 ret = acl_add_flag_np(flagset, ACL_ENTRY_FILE_INHERIT);
232 }
233 if (ret == 0 && ace.flags & RichACL::Ace::kDirectoryInheritAce) {
234 ret = acl_add_flag_np(flagset, ACL_ENTRY_DIRECTORY_INHERIT);
235 }
236 if (ret == 0 && ace.flags & RichACL::Ace::kInheritedAce) {
237 ret = acl_add_flag_np(flagset, ACL_ENTRY_INHERITED);
238 }
239 if (ret == 0 && ace.flags & RichACL::Ace::kNoPropagateInheritAce) {
240 ret = acl_add_flag_np(flagset, ACL_FLAG_DEFER_INHERIT);
241 }
242 if (ret < 0) {
243 lzfs_pretty_syslog(LOG_WARNING, "Failed to add flags to ACL flagset");
244 acl_delete_entry(osx_acl, osx_entry);
245 return ret;
246 }
247 return ret;
248 }
249
convertRichACLToOsx(const RichACL & acl)250 static acl_t convertRichACLToOsx(const RichACL &acl) {
251 acl_t osx_acl = acl_init(acl.size());
252
253 int ret = 0;
254 for (const RichACL::Ace &ace : acl) {
255 ret = createOsxEntry(osx_acl, ace);
256 if (ret < 0) {
257 lzfs_pretty_syslog(LOG_ERR, "Failed to create OSX entry");
258 acl_free(osx_acl);
259 return nullptr;
260 }
261 }
262 return osx_acl;
263 }
264
objectToOsxXattr(const RichACL & acl)265 std::vector<uint8_t> osxAclConverter::objectToOsxXattr(const RichACL &acl) {
266 int ret;
267 std::vector<uint8_t> out;
268 acl_t osx_acl = convertRichACLToOsx(acl);
269 if (osx_acl == nullptr) {
270 return std::vector<uint8_t>();
271 }
272 ssize_t size = acl_size(osx_acl);
273 if (size <= 0) {
274 acl_free(osx_acl);
275 return std::vector<uint8_t>();
276 }
277 out.resize(size);
278 ret = acl_copy_ext(out.data(), osx_acl, out.size());
279 if (ret < 0) {
280 lzfs_pretty_syslog(LOG_WARNING, "Failed to serialize ACL to OSX xattr");
281 acl_free(osx_acl);
282 throw AclConversionException("Serializing RichACL to OSX xattr failed");
283 }
284 acl_free(osx_acl);
285 return out;
286 }
287
288 #endif // __APPLE__
289