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