1/*-
2 * Copyright (c) 2017 Martin Matuska
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include "test.h"
26
27#if ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL
28static const acl_perm_t acl_perms[] = {
29#if ARCHIVE_ACL_DARWIN
30    ACL_READ_DATA,
31    ACL_LIST_DIRECTORY,
32    ACL_WRITE_DATA,
33    ACL_ADD_FILE,
34    ACL_EXECUTE,
35    ACL_SEARCH,
36    ACL_DELETE,
37    ACL_APPEND_DATA,
38    ACL_ADD_SUBDIRECTORY,
39    ACL_DELETE_CHILD,
40    ACL_READ_ATTRIBUTES,
41    ACL_WRITE_ATTRIBUTES,
42    ACL_READ_EXTATTRIBUTES,
43    ACL_WRITE_EXTATTRIBUTES,
44    ACL_READ_SECURITY,
45    ACL_WRITE_SECURITY,
46    ACL_CHANGE_OWNER,
47    ACL_SYNCHRONIZE
48#else /* !ARCHIVE_ACL_DARWIN */
49    ACL_EXECUTE,
50    ACL_WRITE,
51    ACL_READ,
52#if ARCHIVE_ACL_FREEBSD_NFS4
53    ACL_READ_DATA,
54    ACL_LIST_DIRECTORY,
55    ACL_WRITE_DATA,
56    ACL_ADD_FILE,
57    ACL_APPEND_DATA,
58    ACL_ADD_SUBDIRECTORY,
59    ACL_READ_NAMED_ATTRS,
60    ACL_WRITE_NAMED_ATTRS,
61    ACL_DELETE_CHILD,
62    ACL_READ_ATTRIBUTES,
63    ACL_WRITE_ATTRIBUTES,
64    ACL_DELETE,
65    ACL_READ_ACL,
66    ACL_WRITE_ACL,
67    ACL_WRITE_OWNER,
68    ACL_SYNCHRONIZE
69#endif	/* ARCHIVE_ACL_FREEBSD_NFS4 */
70#endif /* !ARCHIVE_ACL_DARWIN */
71};
72#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4
73static const acl_flag_t acl_flags[] = {
74#if ARCHIVE_ACL_DARWIN
75    ACL_ENTRY_INHERITED,
76    ACL_ENTRY_FILE_INHERIT,
77    ACL_ENTRY_DIRECTORY_INHERIT,
78    ACL_ENTRY_LIMIT_INHERIT,
79    ACL_ENTRY_ONLY_INHERIT
80#else	/* ARCHIVE_ACL_FREEBSD_NFS4 */
81    ACL_ENTRY_FILE_INHERIT,
82    ACL_ENTRY_DIRECTORY_INHERIT,
83    ACL_ENTRY_NO_PROPAGATE_INHERIT,
84    ACL_ENTRY_INHERIT_ONLY,
85    ACL_ENTRY_SUCCESSFUL_ACCESS,
86    ACL_ENTRY_FAILED_ACCESS,
87#ifdef ACL_ENTRY_INHERITED
88    ACL_ENTRY_INHERITED
89#endif
90#endif	/* ARCHIVE_ACL_FREEBSD_NFS4 */
91};
92#endif /* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD_NFS4 */
93
94/*
95 * Compare two ACL entries on FreeBSD or on Mac OS X
96 */
97static int
98compare_acl_entry(acl_entry_t ae_a, acl_entry_t ae_b, int is_nfs4)
99{
100	acl_tag_t tag_a, tag_b;
101	acl_permset_t permset_a, permset_b;
102	int perm_a, perm_b, perm_start, perm_end;
103	void *qual_a, *qual_b;
104#if ARCHIVE_ACL_FREEBSD_NFS4
105	acl_entry_type_t type_a, type_b;
106#endif
107#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN
108	acl_flagset_t flagset_a, flagset_b;
109	int flag_a, flag_b;
110#endif
111	int i, r;
112
113
114	/* Compare ACL tag */
115	r = acl_get_tag_type(ae_a, &tag_a);
116	failure("acl_get_tag_type() error: %s", strerror(errno));
117	if (assertEqualInt(r, 0) == 0)
118		return (-1);
119	r = acl_get_tag_type(ae_b, &tag_b);
120	failure("acl_get_tag_type() error: %s", strerror(errno));
121	if (assertEqualInt(r, 0) == 0)
122		return (-1);
123	if (tag_a != tag_b)
124		return (0);
125
126	/* Compare ACL qualifier */
127#if ARCHIVE_ACL_DARWIN
128	if (tag_a == ACL_EXTENDED_ALLOW || tag_b == ACL_EXTENDED_DENY)
129#else
130	if (tag_a == ACL_USER || tag_a == ACL_GROUP)
131#endif
132	{
133		qual_a = acl_get_qualifier(ae_a);
134		failure("acl_get_qualifier() error: %s", strerror(errno));
135		if (assert(qual_a != NULL) == 0)
136			return (-1);
137		qual_b = acl_get_qualifier(ae_b);
138		failure("acl_get_qualifier() error: %s", strerror(errno));
139		if (assert(qual_b != NULL) == 0) {
140			acl_free(qual_a);
141			return (-1);
142		}
143#if ARCHIVE_ACL_DARWIN
144		if (memcmp(((guid_t *)qual_a)->g_guid,
145		    ((guid_t *)qual_b)->g_guid, KAUTH_GUID_SIZE) != 0)
146#else
147		if ((tag_a == ACL_USER &&
148		    (*(uid_t *)qual_a != *(uid_t *)qual_b)) ||
149		    (tag_a == ACL_GROUP &&
150		    (*(gid_t *)qual_a != *(gid_t *)qual_b)))
151#endif
152		{
153			acl_free(qual_a);
154			acl_free(qual_b);
155			return (0);
156		}
157		acl_free(qual_a);
158		acl_free(qual_b);
159	}
160
161#if ARCHIVE_ACL_FREEBSD_NFS4
162	if (is_nfs4) {
163		/* Compare NFS4 ACL type */
164		r = acl_get_entry_type_np(ae_a, &type_a);
165		failure("acl_get_entry_type_np() error: %s", strerror(errno));
166		if (assertEqualInt(r, 0) == 0)
167			return (-1);
168		r = acl_get_entry_type_np(ae_b, &type_b);
169		failure("acl_get_entry_type_np() error: %s", strerror(errno));
170		if (assertEqualInt(r, 0) == 0)
171			return (-1);
172		if (type_a != type_b)
173			return (0);
174	}
175#endif
176
177	/* Compare ACL perms */
178	r = acl_get_permset(ae_a, &permset_a);
179	failure("acl_get_permset() error: %s", strerror(errno));
180	if (assertEqualInt(r, 0) == 0)
181		return (-1);
182	r = acl_get_permset(ae_b, &permset_b);
183	failure("acl_get_permset() error: %s", strerror(errno));
184	if (assertEqualInt(r, 0) == 0)
185		return (-1);
186
187	perm_start = 0;
188	perm_end = (int)(sizeof(acl_perms) / sizeof(acl_perms[0]));
189#if ARCHIVE_ACL_FREEBSD_NFS4
190	if (is_nfs4)
191		perm_start = 3;
192	else
193		perm_end = 3;
194#endif
195	/* Cycle through all perms and compare their value */
196	for (i = perm_start; i < perm_end; i++) {
197#if ARCHIVE_ACL_LIBACL
198		perm_a = acl_get_perm(permset_a, acl_perms[i]);
199		perm_b = acl_get_perm(permset_b, acl_perms[i]);
200#else
201		perm_a = acl_get_perm_np(permset_a, acl_perms[i]);
202		perm_b = acl_get_perm_np(permset_b, acl_perms[i]);
203#endif
204		if (perm_a == -1 || perm_b == -1)
205			return (-1);
206		if (perm_a != perm_b)
207			return (0);
208	}
209
210#if ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN
211	if (is_nfs4) {
212		r = acl_get_flagset_np(ae_a, &flagset_a);
213		failure("acl_get_flagset_np() error: %s", strerror(errno));
214		if (assertEqualInt(r, 0) == 0)
215			return (-1);
216		r = acl_get_flagset_np(ae_b, &flagset_b);
217		failure("acl_get_flagset_np() error: %s", strerror(errno));
218		if (assertEqualInt(r, 0) == 0)
219			return (-1);
220		/* Cycle through all flags and compare their status */
221		for (i = 0; i < (int)(sizeof(acl_flags) / sizeof(acl_flags[0]));
222		    i++) {
223			flag_a = acl_get_flag_np(flagset_a, acl_flags[i]);
224			flag_b = acl_get_flag_np(flagset_b, acl_flags[i]);
225			if (flag_a == -1 || flag_b == -1)
226				return (-1);
227			if (flag_a != flag_b)
228				return (0);
229		}
230	}
231#else	/* ARCHIVE_ACL_FREEBSD_NFS4 || ARCHIVE_ACL_DARWIN */
232	(void)is_nfs4;	/* UNUSED */
233#endif
234	return (1);
235}
236#endif	/* ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_LIBACL */
237
238#if ARCHIVE_ACL_SUPPORT
239/*
240 * Clear default ACLs or inheritance flags
241 */
242static void
243clear_inheritance_flags(const char *path, int type)
244{
245	switch (type) {
246	case ARCHIVE_TEST_ACL_TYPE_POSIX1E:
247#if ARCHIVE_ACL_POSIX1E
248#if !ARCHIVE_ACL_SUNOS
249		acl_delete_def_file(path);
250#else
251		/* Solaris */
252		setTestAcl(path);
253#endif
254#endif	/* ARCHIVE_ACL_POSIX1E */
255		break;
256	case ARCHIVE_TEST_ACL_TYPE_NFS4:
257#if ARCHIVE_ACL_NFS4
258		setTestAcl(path);
259#endif
260		break;
261	default:
262		(void)path;	/* UNUSED */
263		break;
264	}
265}
266
267static int
268compare_acls(const char *path_a, const char *path_b)
269{
270	int ret = 1;
271	int is_nfs4 = 0;
272#if ARCHIVE_ACL_SUNOS
273	void *acl_a, *acl_b;
274	int aclcnt_a, aclcnt_b;
275        aclent_t *aclent_a, *aclent_b;
276        ace_t *ace_a, *ace_b;
277	int e;
278#elif ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL
279	acl_t acl_a, acl_b;
280	acl_entry_t aclent_a, aclent_b;
281	int a, b, r;
282#endif
283#if ARCHIVE_ACL_LIBRICHACL
284	struct richacl *richacl_a, *richacl_b;
285
286	richacl_a = NULL;
287	richacl_b = NULL;
288#endif
289
290#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL || \
291    ARCHIVE_ACL_SUNOS
292	acl_a = NULL;
293	acl_b = NULL;
294#endif
295#if ARCHIVE_ACL_SUNOS
296	acl_a = sunacl_get(GETACL, &aclcnt_a, 0, path_a);
297	if (acl_a == NULL) {
298#if ARCHIVE_ACL_SUNOS_NFS4
299		is_nfs4 = 1;
300		acl_a = sunacl_get(ACE_GETACL, &aclcnt_a, 0, path_a);
301#endif
302		failure("acl_get() error: %s", strerror(errno));
303		if (assert(acl_a != NULL) == 0)
304			return (-1);
305#if ARCHIVE_ACL_SUNOS_NFS4
306		acl_b = sunacl_get(ACE_GETACL, &aclcnt_b, 0, path_b);
307#endif
308	} else
309		acl_b = sunacl_get(GETACL, &aclcnt_b, 0, path_b);
310	if (acl_b == NULL && (errno == ENOSYS || errno == ENOTSUP)) {
311		free(acl_a);
312		return (0);
313	}
314	failure("acl_get() error: %s", strerror(errno));
315	if (assert(acl_b != NULL) == 0) {
316		free(acl_a);
317		return (-1);
318	}
319
320	if (aclcnt_a != aclcnt_b) {
321		ret = 0;
322		goto exit_free;
323	}
324
325	for (e = 0; e < aclcnt_a; e++) {
326		if (!is_nfs4) {
327			aclent_a = &((aclent_t *)acl_a)[e];
328			aclent_b = &((aclent_t *)acl_b)[e];
329			if (aclent_a->a_type != aclent_b->a_type ||
330			    aclent_a->a_id != aclent_b->a_id ||
331			    aclent_a->a_perm != aclent_b->a_perm) {
332				ret = 0;
333				goto exit_free;
334			}
335		}
336#if ARCHIVE_ACL_SUNOS_NFS4
337		else {
338			ace_a = &((ace_t *)acl_a)[e];
339			ace_b = &((ace_t *)acl_b)[e];
340			if (ace_a->a_who != ace_b->a_who ||
341			    ace_a->a_access_mask != ace_b->a_access_mask ||
342			    ace_a->a_flags != ace_b->a_flags ||
343			    ace_a->a_type != ace_b->a_type) {
344				ret = 0;
345				goto exit_free;
346			}
347		}
348#endif
349	}
350#else	/* !ARCHIVE_ACL_SUNOS */
351#if ARCHIVE_ACL_LIBRICHACL
352	richacl_a = richacl_get_file(path_a);
353#if !ARCHIVE_ACL_LIBACL
354	if (richacl_a == NULL &&
355	    (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS))
356		return (0);
357	failure("richacl_get_file() error: %s (%s)", path_a, strerror(errno));
358	if (assert(richacl_a != NULL) == 0)
359		return (-1);
360#endif
361	if (richacl_a != NULL) {
362		richacl_b = richacl_get_file(path_b);
363		if (richacl_b == NULL &&
364		    (errno == ENODATA || errno == ENOTSUP || errno == ENOSYS)) {
365			richacl_free(richacl_a);
366			return (0);
367		}
368		failure("richacl_get_file() error: %s (%s)", path_b,
369		    strerror(errno));
370		if (assert(richacl_b != NULL) == 0) {
371			richacl_free(richacl_a);
372			return (-1);
373		}
374		if (richacl_compare(richacl_a, richacl_b) == 0)
375			ret = 0;
376		richacl_free(richacl_a);
377		richacl_free(richacl_b);
378		return (ret);
379        }
380#endif /* ARCHIVE_ACL_LIBRICHACL */
381#if ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL
382#if ARCHIVE_ACL_DARWIN
383	is_nfs4 = 1;
384	acl_a = acl_get_file(path_a, ACL_TYPE_EXTENDED);
385#elif ARCHIVE_ACL_FREEBSD_NFS4
386	acl_a = acl_get_file(path_a, ACL_TYPE_NFS4);
387	if (acl_a != NULL)
388		is_nfs4 = 1;
389#endif
390	if (acl_a == NULL)
391		acl_a = acl_get_file(path_a, ACL_TYPE_ACCESS);
392	failure("acl_get_file() error: %s (%s)", path_a, strerror(errno));
393	if (assert(acl_a != NULL) == 0)
394		return (-1);
395#if ARCHIVE_ACL_DARWIN
396	acl_b = acl_get_file(path_b, ACL_TYPE_EXTENDED);
397#elif ARCHIVE_ACL_FREEBSD_NFS4
398	acl_b = acl_get_file(path_b, ACL_TYPE_NFS4);
399#endif
400#if !ARCHIVE_ACL_DARWIN
401	if (acl_b == NULL) {
402#if ARCHIVE_ACL_FREEBSD_NFS4
403		if (is_nfs4) {
404			acl_free(acl_a);
405			return (0);
406		}
407#endif
408		acl_b = acl_get_file(path_b, ACL_TYPE_ACCESS);
409	}
410	failure("acl_get_file() error: %s (%s)", path_b, strerror(errno));
411	if (assert(acl_b != NULL) == 0) {
412		acl_free(acl_a);
413		return (-1);
414	}
415#endif
416	a = acl_get_entry(acl_a, ACL_FIRST_ENTRY, &aclent_a);
417	if (a == -1) {
418		ret = 0;
419		goto exit_free;
420	}
421	b = acl_get_entry(acl_b, ACL_FIRST_ENTRY, &aclent_b);
422	if (b == -1) {
423		ret = 0;
424		goto exit_free;
425	}
426#if ARCHIVE_ACL_DARWIN
427	while (a == 0 && b == 0)
428#else	/* FreeBSD, Linux */
429	while (a == 1 && b == 1)
430#endif
431	{
432		r = compare_acl_entry(aclent_a, aclent_b, is_nfs4);
433		if (r != 1) {
434			ret = r;
435			goto exit_free;
436		}
437		a = acl_get_entry(acl_a, ACL_NEXT_ENTRY, &aclent_a);
438		b = acl_get_entry(acl_b, ACL_NEXT_ENTRY, &aclent_b);
439	}
440	/* Entry count must match */
441	if (a != b)
442		ret = 0;
443#endif	/* ARCHIVE_ACL_DARWIN || ARCHIVE_ACL_FREEBSD || ARCHIVE_ACL_LIBACL */
444#endif	/* !ARCHIVE_ACL_SUNOS */
445exit_free:
446#if ARCHIVE_ACL_SUNOS
447	free(acl_a);
448	free(acl_b);
449#else
450	acl_free(acl_a);
451	acl_free(acl_b);
452#endif
453	return (ret);
454}
455#endif	/* ARCHIVE_ACL_SUPPORT */
456
457DEFINE_TEST(test_option_acls)
458{
459#if !ARCHIVE_ACL_SUPPORT
460        skipping("ACLs are not supported on this platform");
461#else   /* ARCHIVE_ACL_SUPPORT */
462	int acltype, r;
463
464	assertMakeFile("f", 0644, "a");
465	acltype = setTestAcl("f");
466	if (acltype == 0) {
467		skipping("Can't write ACLs on the filesystem");
468		return;
469	}
470
471	/* Archive it with acls */
472        r = systemf("%s -c --no-mac-metadata --acls -f acls.tar f >acls.out 2>acls.err", testprog);
473        assertEqualInt(r, 0);
474
475	/* Archive it without acls */
476	r = systemf("%s -c --no-mac-metadata --no-acls -f noacls.tar f >noacls.out 2>noacls.err", testprog);
477	assertEqualInt(r, 0);
478
479	/* Extract acls with acls */
480	assertMakeDir("acls_acls", 0755);
481	clear_inheritance_flags("acls_acls", acltype);
482	r = systemf("%s -x -C acls_acls --no-same-permissions --acls -f acls.tar >acls_acls.out 2>acls_acls.err", testprog);
483	assertEqualInt(r, 0);
484	r = compare_acls("f", "acls_acls/f");
485	assertEqualInt(r, 1);
486
487	/* Extract acls without acls */
488	assertMakeDir("acls_noacls", 0755);
489	clear_inheritance_flags("acls_noacls", acltype);
490	r = systemf("%s -x -C acls_noacls -p --no-acls -f acls.tar >acls_noacls.out 2>acls_noacls.err", testprog);
491	assertEqualInt(r, 0);
492	r = compare_acls("f", "acls_noacls/f");
493	assertEqualInt(r, 0);
494
495	/* Extract noacls with acls flag */
496	assertMakeDir("noacls_acls", 0755);
497	clear_inheritance_flags("noacls_acls", acltype);
498	r = systemf("%s -x -C noacls_acls --no-same-permissions --acls -f noacls.tar >noacls_acls.out 2>noacls_acls.err", testprog);
499	assertEqualInt(r, 0);
500	r = compare_acls("f", "noacls_acls/f");
501	assertEqualInt(r, 0);
502
503	/* Extract noacls with noacls */
504	assertMakeDir("noacls_noacls", 0755);
505	clear_inheritance_flags("noacls_noacls", acltype);
506	r = systemf("%s -x -C noacls_noacls -p --no-acls -f noacls.tar >noacls_noacls.out 2>noacls_noacls.err", testprog);
507	assertEqualInt(r, 0);
508	r = compare_acls("f", "noacls_noacls/f");
509	assertEqualInt(r, 0);
510#endif	/* ARCHIVE_ACL_SUPPORT */
511}
512