Path: blob/master/Utilities/cmlibarchive/libarchive/archive_disk_acl_darwin.c
5053 views
/*-1* Copyright (c) 2017 Martin Matuska2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR14* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES15* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.16* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,17* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT18* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,19* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY20* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT21* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF22* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.23*/2425#include "archive_platform.h"2627#if ARCHIVE_ACL_DARWIN2829#ifdef HAVE_FCNTL_H30#include <fcntl.h>31#endif32#if HAVE_ERRNO_H33#include <errno.h>34#endif35#if HAVE_MEMBERSHIP_H36#include <membership.h>37#endif38#ifdef HAVE_SYS_TYPES_H39#include <sys/types.h>40#endif41#ifdef HAVE_SYS_ACL_H42#define _ACL_PRIVATE /* For debugging */43#include <sys/acl.h>44#endif4546#include "archive_entry.h"47#include "archive_private.h"48#include "archive_read_disk_private.h"49#include "archive_write_disk_private.h"5051typedef struct {52const int a_perm; /* Libarchive permission or flag */53const int p_perm; /* Platform permission or flag */54} acl_perm_map_t;5556static const acl_perm_map_t acl_nfs4_perm_map[] = {57{ARCHIVE_ENTRY_ACL_READ_DATA, ACL_READ_DATA},58{ARCHIVE_ENTRY_ACL_LIST_DIRECTORY, ACL_LIST_DIRECTORY},59{ARCHIVE_ENTRY_ACL_WRITE_DATA, ACL_WRITE_DATA},60{ARCHIVE_ENTRY_ACL_ADD_FILE, ACL_ADD_FILE},61{ARCHIVE_ENTRY_ACL_EXECUTE, ACL_EXECUTE},62{ARCHIVE_ENTRY_ACL_DELETE, ACL_DELETE},63{ARCHIVE_ENTRY_ACL_APPEND_DATA, ACL_APPEND_DATA},64{ARCHIVE_ENTRY_ACL_ADD_SUBDIRECTORY, ACL_ADD_SUBDIRECTORY},65{ARCHIVE_ENTRY_ACL_DELETE_CHILD, ACL_DELETE_CHILD},66{ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES, ACL_READ_ATTRIBUTES},67{ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES, ACL_WRITE_ATTRIBUTES},68{ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS, ACL_READ_EXTATTRIBUTES},69{ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS, ACL_WRITE_EXTATTRIBUTES},70{ARCHIVE_ENTRY_ACL_READ_ACL, ACL_READ_SECURITY},71{ARCHIVE_ENTRY_ACL_WRITE_ACL, ACL_WRITE_SECURITY},72{ARCHIVE_ENTRY_ACL_WRITE_OWNER, ACL_CHANGE_OWNER},73#if HAVE_DECL_ACL_SYNCHRONIZE74{ARCHIVE_ENTRY_ACL_SYNCHRONIZE, ACL_SYNCHRONIZE}75#endif76};7778static const int acl_nfs4_perm_map_size =79(int)(sizeof(acl_nfs4_perm_map)/sizeof(acl_nfs4_perm_map[0]));8081static const acl_perm_map_t acl_nfs4_flag_map[] = {82{ARCHIVE_ENTRY_ACL_ENTRY_INHERITED, ACL_ENTRY_INHERITED},83{ARCHIVE_ENTRY_ACL_ENTRY_FILE_INHERIT, ACL_ENTRY_FILE_INHERIT},84{ARCHIVE_ENTRY_ACL_ENTRY_DIRECTORY_INHERIT, ACL_ENTRY_DIRECTORY_INHERIT},85{ARCHIVE_ENTRY_ACL_ENTRY_NO_PROPAGATE_INHERIT, ACL_ENTRY_LIMIT_INHERIT},86{ARCHIVE_ENTRY_ACL_ENTRY_INHERIT_ONLY, ACL_ENTRY_ONLY_INHERIT}87};8889static const int acl_nfs4_flag_map_size =90(int)(sizeof(acl_nfs4_flag_map)/sizeof(acl_nfs4_flag_map[0]));9192static int translate_guid(struct archive *a, acl_entry_t acl_entry,93int *ae_id, int *ae_tag, const char **ae_name)94{95void *q;96uid_t ugid;97int r, idtype;9899q = acl_get_qualifier(acl_entry);100if (q == NULL)101return (1);102r = mbr_uuid_to_id((const unsigned char *)q, &ugid, &idtype);103if (r != 0) {104acl_free(q);105return (1);106}107if (idtype == ID_TYPE_UID) {108*ae_tag = ARCHIVE_ENTRY_ACL_USER;109*ae_id = ugid;110*ae_name = archive_read_disk_uname(a, *ae_id);111} else if (idtype == ID_TYPE_GID) {112*ae_tag = ARCHIVE_ENTRY_ACL_GROUP;113*ae_id = ugid;114*ae_name = archive_read_disk_gname(a, *ae_id);115} else116r = 1;117118acl_free(q);119return (r);120}121122static void123add_trivial_nfs4_acl(struct archive_entry *entry)124{125mode_t mode;126const int rperm = ARCHIVE_ENTRY_ACL_READ_DATA;127const int wperm = ARCHIVE_ENTRY_ACL_WRITE_DATA |128ARCHIVE_ENTRY_ACL_APPEND_DATA;129const int eperm = ARCHIVE_ENTRY_ACL_EXECUTE;130const int pubset = ARCHIVE_ENTRY_ACL_READ_ATTRIBUTES |131ARCHIVE_ENTRY_ACL_READ_NAMED_ATTRS |132ARCHIVE_ENTRY_ACL_READ_ACL |133ARCHIVE_ENTRY_ACL_SYNCHRONIZE;134const int ownset = pubset | ARCHIVE_ENTRY_ACL_WRITE_ATTRIBUTES |135ARCHIVE_ENTRY_ACL_WRITE_NAMED_ATTRS |136ARCHIVE_ENTRY_ACL_WRITE_ACL |137ARCHIVE_ENTRY_ACL_WRITE_OWNER;138139struct {140const int type;141const int tag;142int permset;143} tacl_entry[] = {144{ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},145{ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_USER_OBJ, 0},146{ARCHIVE_ENTRY_ACL_TYPE_DENY, ARCHIVE_ENTRY_ACL_GROUP_OBJ, 0},147{ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_USER_OBJ, ownset},148{ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_GROUP_OBJ, pubset},149{ARCHIVE_ENTRY_ACL_TYPE_ALLOW, ARCHIVE_ENTRY_ACL_EVERYONE, pubset}150};151152mode = archive_entry_mode(entry);153154/* Permissions for everyone@ */155if (mode & 0004)156tacl_entry[5].permset |= rperm;157if (mode & 0002)158tacl_entry[5].permset |= wperm;159if (mode & 0001)160tacl_entry[5].permset |= eperm;161162/* Permissions for group@ */163if (mode & 0040)164tacl_entry[4].permset |= rperm;165else if (mode & 0004)166tacl_entry[2].permset |= rperm;167if (mode & 0020)168tacl_entry[4].permset |= wperm;169else if (mode & 0002)170tacl_entry[2].permset |= wperm;171if (mode & 0010)172tacl_entry[4].permset |= eperm;173else if (mode & 0001)174tacl_entry[2].permset |= eperm;175176/* Permissions for owner@ */177if (mode & 0400) {178tacl_entry[3].permset |= rperm;179if (!(mode & 0040) && (mode & 0004))180tacl_entry[0].permset |= rperm;181} else if ((mode & 0040) || (mode & 0004))182tacl_entry[1].permset |= rperm;183if (mode & 0200) {184tacl_entry[3].permset |= wperm;185if (!(mode & 0020) && (mode & 0002))186tacl_entry[0].permset |= wperm;187} else if ((mode & 0020) || (mode & 0002))188tacl_entry[1].permset |= wperm;189if (mode & 0100) {190tacl_entry[3].permset |= eperm;191if (!(mode & 0010) && (mode & 0001))192tacl_entry[0].permset |= eperm;193} else if ((mode & 0010) || (mode & 0001))194tacl_entry[1].permset |= eperm;195196for (size_t i = 0; i < sizeof(tacl_entry) / sizeof(tacl_entry[0]); i++) {197if (tacl_entry[i].permset != 0) {198archive_entry_acl_add_entry(entry,199tacl_entry[i].type, tacl_entry[i].permset,200tacl_entry[i].tag, -1, NULL);201}202}203204return;205}206207static int208translate_acl(struct archive_read_disk *a,209struct archive_entry *entry, acl_t acl)210{211acl_tag_t acl_tag;212acl_flagset_t acl_flagset;213acl_entry_t acl_entry;214acl_permset_t acl_permset;215int i, entry_acl_type;216int r, s, ae_id, ae_tag, ae_perm;217const char *ae_name;218219s = acl_get_entry(acl, ACL_FIRST_ENTRY, &acl_entry);220if (s == -1) {221archive_set_error(&a->archive, errno,222"Failed to get first ACL entry");223return (ARCHIVE_WARN);224}225226while (s == 0) {227ae_id = -1;228ae_name = NULL;229ae_perm = 0;230231if (acl_get_tag_type(acl_entry, &acl_tag) != 0) {232archive_set_error(&a->archive, errno,233"Failed to get ACL tag type");234return (ARCHIVE_WARN);235}236switch (acl_tag) {237case ACL_EXTENDED_ALLOW:238entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_ALLOW;239r = translate_guid(&a->archive, acl_entry,240&ae_id, &ae_tag, &ae_name);241break;242case ACL_EXTENDED_DENY:243entry_acl_type = ARCHIVE_ENTRY_ACL_TYPE_DENY;244r = translate_guid(&a->archive, acl_entry,245&ae_id, &ae_tag, &ae_name);246break;247default:248/* Skip types that libarchive can't support. */249s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);250continue;251}252253/* Skip if translate_guid() above failed */254if (r != 0) {255s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);256continue;257}258259/*260* Libarchive stores "flag" (NFSv4 inheritance bits)261* in the ae_perm bitmap.262*263* acl_get_flagset_np() fails with non-NFSv4 ACLs264*/265if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {266archive_set_error(&a->archive, errno,267"Failed to get flagset from a NFSv4 ACL entry");268return (ARCHIVE_WARN);269}270for (i = 0; i < acl_nfs4_flag_map_size; ++i) {271r = acl_get_flag_np(acl_flagset,272acl_nfs4_flag_map[i].p_perm);273if (r == -1) {274archive_set_error(&a->archive, errno,275"Failed to check flag in a NFSv4 "276"ACL flagset");277return (ARCHIVE_WARN);278} else if (r)279ae_perm |= acl_nfs4_flag_map[i].a_perm;280}281282if (acl_get_permset(acl_entry, &acl_permset) != 0) {283archive_set_error(&a->archive, errno,284"Failed to get ACL permission set");285return (ARCHIVE_WARN);286}287288for (i = 0; i < acl_nfs4_perm_map_size; ++i) {289/*290* acl_get_perm() is spelled differently on different291* platforms; see above.292*/293r = acl_get_perm_np(acl_permset,294acl_nfs4_perm_map[i].p_perm);295if (r == -1) {296archive_set_error(&a->archive, errno,297"Failed to check permission in an ACL "298"permission set");299return (ARCHIVE_WARN);300} else if (r)301ae_perm |= acl_nfs4_perm_map[i].a_perm;302}303304#if !HAVE_DECL_ACL_SYNCHRONIZE305/* On Mac OS X without ACL_SYNCHRONIZE assume it is set */306ae_perm |= ARCHIVE_ENTRY_ACL_SYNCHRONIZE;307#endif308309archive_entry_acl_add_entry(entry, entry_acl_type,310ae_perm, ae_tag,311ae_id, ae_name);312313s = acl_get_entry(acl, ACL_NEXT_ENTRY, &acl_entry);314}315return (ARCHIVE_OK);316}317318static int319set_acl(struct archive *a, int fd, const char *name,320struct archive_acl *abstract_acl,321int ae_requested_type, const char *tname)322{323acl_t acl;324acl_entry_t acl_entry;325acl_permset_t acl_permset;326acl_flagset_t acl_flagset;327int ret;328int ae_type, ae_permset, ae_tag, ae_id;329uuid_t ae_uuid;330uid_t ae_uid;331gid_t ae_gid;332const char *ae_name;333int entries;334int i;335336ret = ARCHIVE_OK;337entries = archive_acl_reset(abstract_acl, ae_requested_type);338if (entries == 0)339return (ARCHIVE_OK);340341if (ae_requested_type != ARCHIVE_ENTRY_ACL_TYPE_NFS4) {342errno = ENOENT;343archive_set_error(a, errno, "Unsupported ACL type");344return (ARCHIVE_FAILED);345}346347acl = acl_init(entries);348if (acl == (acl_t)NULL) {349archive_set_error(a, errno,350"Failed to initialize ACL working storage");351return (ARCHIVE_FAILED);352}353354while (archive_acl_next(a, abstract_acl, ae_requested_type, &ae_type,355&ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {356/*357* Mac OS doesn't support NFSv4 ACLs for358* owner@, group@ and everyone@.359* We skip any of these ACLs found.360*/361if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ ||362ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ ||363ae_tag == ARCHIVE_ENTRY_ACL_EVERYONE)364continue;365366if (acl_create_entry(&acl, &acl_entry) != 0) {367archive_set_error(a, errno,368"Failed to create a new ACL entry");369ret = ARCHIVE_FAILED;370goto exit_free;371}372373switch (ae_type) {374case ARCHIVE_ENTRY_ACL_TYPE_ALLOW:375acl_set_tag_type(acl_entry, ACL_EXTENDED_ALLOW);376break;377case ARCHIVE_ENTRY_ACL_TYPE_DENY:378acl_set_tag_type(acl_entry, ACL_EXTENDED_DENY);379break;380default:381/* We don't support any other types on MacOS */382continue;383}384385switch (ae_tag) {386case ARCHIVE_ENTRY_ACL_USER:387ae_uid = archive_write_disk_uid(a, ae_name, ae_id);388if (mbr_uid_to_uuid(ae_uid, ae_uuid) != 0)389continue;390if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)391continue;392break;393case ARCHIVE_ENTRY_ACL_GROUP:394ae_gid = archive_write_disk_gid(a, ae_name, ae_id);395if (mbr_gid_to_uuid(ae_gid, ae_uuid) != 0)396continue;397if (acl_set_qualifier(acl_entry, &ae_uuid) != 0)398continue;399break;400default:401archive_set_error(a, ARCHIVE_ERRNO_MISC,402"Unsupported ACL tag");403ret = ARCHIVE_FAILED;404goto exit_free;405}406407if (acl_get_permset(acl_entry, &acl_permset) != 0) {408archive_set_error(a, errno,409"Failed to get ACL permission set");410ret = ARCHIVE_FAILED;411goto exit_free;412}413if (acl_clear_perms(acl_permset) != 0) {414archive_set_error(a, errno,415"Failed to clear ACL permissions");416ret = ARCHIVE_FAILED;417goto exit_free;418}419420for (i = 0; i < acl_nfs4_perm_map_size; ++i) {421if (ae_permset & acl_nfs4_perm_map[i].a_perm) {422if (acl_add_perm(acl_permset,423acl_nfs4_perm_map[i].p_perm) != 0) {424archive_set_error(a, errno,425"Failed to add ACL permission");426ret = ARCHIVE_FAILED;427goto exit_free;428}429}430}431432/*433* acl_get_flagset_np() fails with non-NFSv4 ACLs434*/435if (acl_get_flagset_np(acl_entry, &acl_flagset) != 0) {436archive_set_error(a, errno,437"Failed to get flagset from an NFSv4 ACL entry");438ret = ARCHIVE_FAILED;439goto exit_free;440}441if (acl_clear_flags_np(acl_flagset) != 0) {442archive_set_error(a, errno,443"Failed to clear flags from an NFSv4 ACL flagset");444ret = ARCHIVE_FAILED;445goto exit_free;446}447448for (i = 0; i < acl_nfs4_flag_map_size; ++i) {449if (ae_permset & acl_nfs4_flag_map[i].a_perm) {450if (acl_add_flag_np(acl_flagset,451acl_nfs4_flag_map[i].p_perm) != 0) {452archive_set_error(a, errno,453"Failed to add flag to "454"NFSv4 ACL flagset");455ret = ARCHIVE_FAILED;456goto exit_free;457}458}459}460}461462if (fd >= 0) {463if (acl_set_fd_np(fd, acl, ACL_TYPE_EXTENDED) == 0)464ret = ARCHIVE_OK;465else {466if (errno == EOPNOTSUPP) {467/* Filesystem doesn't support ACLs */468ret = ARCHIVE_OK;469} else {470archive_set_error(a, errno,471"Failed to set acl on fd: %s", tname);472ret = ARCHIVE_WARN;473}474}475} else if (acl_set_link_np(name, ACL_TYPE_EXTENDED, acl) != 0) {476if (errno == EOPNOTSUPP) {477/* Filesystem doesn't support ACLs */478ret = ARCHIVE_OK;479} else {480archive_set_error(a, errno, "Failed to set acl: %s",481tname);482ret = ARCHIVE_WARN;483}484}485exit_free:486acl_free(acl);487return (ret);488}489490int491archive_read_disk_entry_setup_acls(struct archive_read_disk *a,492struct archive_entry *entry, int *fd)493{494const char *accpath;495acl_t acl;496int r;497498accpath = NULL;499500if (*fd < 0) {501accpath = archive_read_disk_entry_setup_path(a, entry, fd);502if (accpath == NULL)503return (ARCHIVE_WARN);504}505506archive_entry_acl_clear(entry);507508acl = NULL;509510if (*fd >= 0)511acl = acl_get_fd_np(*fd, ACL_TYPE_EXTENDED);512else if (!a->follow_symlinks)513acl = acl_get_link_np(accpath, ACL_TYPE_EXTENDED);514else515acl = acl_get_file(accpath, ACL_TYPE_EXTENDED);516517if (acl != NULL) {518r = translate_acl(a, entry, acl);519acl_free(acl);520acl = NULL;521522if (r != ARCHIVE_OK) {523archive_set_error(&a->archive, errno,524"Couldn't translate NFSv4 ACLs");525}526527/*528* Because Mac OS doesn't support owner@, group@ and everyone@529* ACLs we need to add NFSv4 ACLs mirroring the file mode to530* the archive entry. Otherwise extraction on non-Mac platforms531* would lead to an invalid file mode.532*/533if ((archive_entry_acl_types(entry) &534ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0)535add_trivial_nfs4_acl(entry);536537return (r);538}539return (ARCHIVE_OK);540}541542int543archive_write_disk_set_acls(struct archive *a, int fd, const char *name,544struct archive_acl *abstract_acl, __LA_MODE_T mode)545{546int ret = ARCHIVE_OK;547548(void)mode; /* UNUSED */549550if ((archive_acl_types(abstract_acl) &551ARCHIVE_ENTRY_ACL_TYPE_NFS4) != 0) {552ret = set_acl(a, fd, name, abstract_acl,553ARCHIVE_ENTRY_ACL_TYPE_NFS4, "nfs4");554}555return (ret);556}557#endif /* ARCHIVE_ACL_DARWIN */558559560