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