aclutils.c revision 3eb3c57322eccc9d4c2880c26f57ceb5a85c2491
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include <grp.h>
#include <pwd.h>
#include <strings.h>
#include <errno.h>
#include <locale.h>
#include <aclutils.h>
#include <acl_common.h>
#define ACL_PATH 0
#define ACL_FD 1
#define ACE_POSIX_SUPPORTED_BITS (ACE_READ_DATA | \
#define ACL_SYNCHRONIZE_SET_DENY 0x0000001
#define ACL_SYNCHRONIZE_SET_ALLOW 0x0000002
#define ACL_SYNCHRONIZE_ERR_DENY 0x0000004
#define ACL_SYNCHRONIZE_ERR_ALLOW 0x0000008
#define ACL_WRITE_OWNER_SET_DENY 0x0000010
#define ACL_WRITE_OWNER_SET_ALLOW 0x0000020
#define ACL_WRITE_OWNER_ERR_DENY 0x0000040
#define ACL_WRITE_OWNER_ERR_ALLOW 0x0000080
#define ACL_DELETE_SET_DENY 0x0000100
#define ACL_DELETE_SET_ALLOW 0x0000200
#define ACL_DELETE_ERR_DENY 0x0000400
#define ACL_DELETE_ERR_ALLOW 0x0000800
#define ACL_WRITE_ATTRS_OWNER_SET_DENY 0x0001000
#define ACL_WRITE_ATTRS_OWNER_SET_ALLOW 0x0002000
#define ACL_WRITE_ATTRS_OWNER_ERR_DENY 0x0004000
#define ACL_WRITE_ATTRS_OWNER_ERR_ALLOW 0x0008000
#define ACL_WRITE_ATTRS_WRITER_SET_DENY 0x0010000
#define ACL_WRITE_ATTRS_WRITER_SET_ALLOW 0x0020000
#define ACL_WRITE_ATTRS_WRITER_ERR_DENY 0x0040000
#define ACL_WRITE_ATTRS_WRITER_ERR_ALLOW 0x0080000
#define ACL_WRITE_NAMED_WRITER_SET_DENY 0x0100000
#define ACL_WRITE_NAMED_WRITER_SET_ALLOW 0x0200000
#define ACL_WRITE_NAMED_WRITER_ERR_DENY 0x0400000
#define ACL_WRITE_NAMED_WRITER_ERR_ALLOW 0x0800000
#define ACL_READ_NAMED_READER_SET_DENY 0x1000000
#define ACL_READ_NAMED_READER_SET_ALLOW 0x2000000
#define ACL_READ_NAMED_READER_ERR_DENY 0x4000000
#define ACL_READ_NAMED_READER_ERR_ALLOW 0x8000000
#define ACE_VALID_MASK_BITS (\
ACE_READ_DATA | \
ACE_WRITE_DATA | \
ACE_ADD_FILE | \
ACE_APPEND_DATA | \
ACE_EXECUTE | \
ACE_DELETE_CHILD | \
ACE_DELETE | \
ACE_READ_ACL | \
ACE_WRITE_ACL | \
ACE_WRITE_OWNER | \
#define ACE_MASK_UNDEFINED 0x80000000
#define ACE_VALID_FLAG_BITS (ACE_FILE_INHERIT_ACE | \
/*
* ACL conversion helpers
*/
typedef enum {
ace_group, /* includes GROUP and GROUP_OBJ */
typedef struct acevals {
int aent_type;
} acevals_t;
typedef struct ace_list {
int numusers;
int numgroups;
int hasmask;
int dfacl_flag;
int seen; /* bitmask of all aclent_t a_type values seen */
} ace_list_t;
typedef union {
const char *file;
int fd;
} acl_inp;
acl_t *
{
return (NULL);
switch (type) {
case ACE_T:
break;
case ACLENT_T:
break;
default:
}
return (aclp);
}
/*
* Free acl_t structure
*/
void
{
return;
}
/*
* Determine whether a file has a trivial ACL
* returns: 0 = trivial
* 1 = nontrivial
* <0 some other system failure, such as ENOENT or EPERM
*/
int
acl_trivial(const char *filename)
{
int acl_flavor;
int aclcnt;
int cntcmd;
int val = 0;
if (acl_flavor == -1)
return (-1);
if (acl_flavor == _ACL_ACE_ENABLED)
else
if (aclcnt > 0) {
if (acl_flavor == _ACL_ACE_ENABLED) {
return (-1);
return (-1);
}
} else if (aclcnt > MIN_ACL_ENTRIES)
val = 1;
}
return (val);
}
static uint32_t
{
uint32_t access_mask = 0;
int acl_produce;
int synchronize_set = 0, write_owner_set = 0;
int delete_set = 0, write_attrs_set = 0;
int read_named_set = 0, write_named_set = 0;
if (isallow) {
if (hasreadperm)
if (haswriteperm)
if (isowner)
else if (haswriteperm)
} else {
if (hasreadperm)
if (haswriteperm)
if (isowner)
else if (haswriteperm)
else
/*
* If the entity is not the owner and does not
* have write permissions ACE_WRITE_ATTRIBUTES will
* always go in the DENY ACE.
*/
}
if (acl_produce & synchronize_set)
if (acl_produce & write_owner_set)
if (acl_produce & delete_set)
if (acl_produce & write_attrs_set)
if (acl_produce & read_named_set)
if (acl_produce & write_named_set)
return (access_mask);
}
/*
* Given an mode_t, convert it into an access_mask as used
* by nfsace, assuming aclent_t -> nfsace semantics.
*/
static uint32_t
{
int haswriteperm = 0;
int hasreadperm = 0;
if (isallow) {
} else {
}
/*
* The following call takes care of correctly setting the following
* mask bits in the access_mask:
* ACE_SYNCHRONIZE, ACE_WRITE_OWNER, ACE_DELETE,
* ACE_WRITE_ATTRIBUTES, ACE_WRITE_NAMED_ATTRS, ACE_READ_NAMED_ATTRS
*/
if (isallow) {
if (isowner)
access |= ACE_WRITE_ACL;
} else {
if (! isowner)
access |= ACE_WRITE_ACL;
}
/* read */
if (mode & 04) {
access |= ACE_READ_DATA;
}
/* write */
if (mode & 02) {
access |= ACE_WRITE_DATA |
if (isdir)
}
/* exec */
if (mode & 01) {
access |= ACE_EXECUTE;
}
return (access);
}
/*
* Given an nfsace (presumably an ALLOW entry), make a
* corresponding DENY entry at the address given.
*/
static void
{
if (isdir)
B_FALSE);
}
/*
* Make an initial pass over an array of aclent_t's. Gather
* information such as an ACL_MASK (if any), number of users,
* number of groups, and whether the array needs to be sorted.
*/
static int
{
int error = 0;
int i;
int curtype = 0;
*hasmask = 0;
*mask = 07;
*needsort = 0;
*numuser = 0;
*numgroup = 0;
for (i = 0; i < n; i++) {
*needsort = 1;
(*numuser)++;
(*numgroup)++;
if (*hasmask) {
goto out;
} else {
*hasmask = 1;
}
}
}
goto out;
}
out:
return (error);
}
/*
* Convert an array of aclent_t into an array of nfsace entries,
* following POSIX draft -> nfsv4 conversion semantics as outlined in
* the IETF draft.
*/
static int
{
int error = 0;
int resultsize = 0;
int hasmask;
if (error != 0)
goto out;
/* allow + deny for each aclent */
resultsize = n * 2;
if (hasmask) {
/*
* stick extra deny on the group_obj and on each
* user|group for the mask (the group_obj was added
* into the count for numgroup)
*/
/* ... and don't count the mask itself */
resultsize -= 2;
}
/* sort the source if necessary */
if (needsort)
goto out;
for (i = 0; i < n; i++) {
/*
* don't process CLASS_OBJ (mask); mask was grabbed in
* ln_aent_preprocess()
*/
continue;
/* If we need an ACL_MASK emulator, prepend it now */
if ((hasmask) &&
} else {
}
}
/*
* Set the access mask for the prepended deny
* ace. To do this, we invert the mask (found
* in ln_aent_preprocess()) then convert it to an
* DENY ace access_mask.
*/
isdir, 0, 0);
acep += 1;
}
/* handle a_perm -> access_mask */
/* emulate a default aclent */
}
/*
* handle a_perm and a_id
*
* this must be done last, since it involves the
* corresponding deny aces, which are handled
* differently for each different a_type.
*/
acep += 2;
acep += 2;
} else {
}
/*
* Set the corresponding deny for the group ace.
*
* The deny aces go after all of the groups, unlike
* everything else, where they immediately follow
* the allow ace.
*
* We calculate "skip", the number of slots to
* skip ahead for the deny ace, here.
*
* The pattern is:
* MD1 A1 MD2 A2 MD3 A3 D1 D2 D3
* thus, skip is
* (2 * numgroup) - 1 - groupi
* (2 * numgroup) to account for MD + A
* - 1 to account for the fact that we're on the
* access (A), not the mask (MD)
* - groupi to account for the fact that we have
* passed up groupi number of MD's.
*/
/*
* If we just did the last group, skip acep past
* all of the denies; else, just move ahead one.
*/
else
acep += 1;
acep += 2;
} else {
goto out;
}
}
*rescount = resultsize;
out:
if (error != 0) {
}
}
return (error);
}
static int
{
int acecnt = 0;
int dfacecnt = 0;
int dfaclstart = 0;
int dfaclcnt = 0;
int i;
int error;
break;
}
if (i < aclcnt) {
dfaclstart = i;
}
return (-1);
}
if (error)
return (-1);
if (dfaclcnt) {
if (error) {
if (acep) {
}
return (-1);
}
}
if (dfacecnt != 0) {
return (-1);
if (dfaclcnt) {
}
}
if (dfaclcnt)
return (0);
}
static void
{
}
static void
{
}
/*
* Find or create an acevals holder for a given id and avl tree.
*
* Note that only one thread will ever touch these avl trees, so
* there is no need for locking.
*/
static acevals_t *
{
return (rc);
/* this memory is freed by ln_ace_to_aent()->ace_list_free() */
return (rc);
(*num)++;
return (rc);
}
static int
{
int acl_consume;
int haswriteperm, hasreadperm;
} else {
}
if (mask_bit == ACE_SYNCHRONIZE) {
} else if (mask_bit == ACE_WRITE_OWNER) {
} else if (mask_bit == ACE_DELETE) {
} else if (mask_bit == ACE_WRITE_ATTRIBUTES) {
if (isowner) {
} else if (haswriteperm) {
} else {
return (ENOTSUP);
}
return (0);
}
} else if (mask_bit == ACE_READ_NAMED_ATTRS) {
if (!hasreadperm)
return (0);
} else if (mask_bit == ACE_WRITE_NAMED_ATTRS) {
if (!haswriteperm)
return (0);
} else {
return (EINVAL);
}
if (acl_consume & set_deny) {
return (ENOTSUP);
}
} else if (acl_consume & err_deny) {
return (ENOTSUP);
}
}
} else {
/* ACE_ACCESS_ALLOWED_ACE_TYPE */
if (acl_consume & set_allow) {
return (ENOTSUP);
}
} else if (acl_consume & err_allow) {
return (ENOTSUP);
}
}
}
return (0);
}
static int
{
int error = 0;
int isowner;
/* only ALLOW or DENY */
goto out;
}
/* check for invalid flags */
goto out;
}
/* some flags are illegal */
goto out;
}
/* check for invalid masks */
goto out;
}
isowner = 1;
} else {
isowner = 0;
}
if (error)
goto out;
if (error)
goto out;
if (error)
goto out;
if (error)
goto out;
if (error)
goto out;
if (error)
goto out;
/* more detailed checking of masks */
goto out;
}
goto out;
}
goto out;
}
}
/* ACL enforcement */
goto out;
}
(isowner)) {
goto out;
}
(! isowner)) {
goto out;
}
}
out:
return (error);
}
static int
{
int error = 0;
/* read */
if (mask & ACE_READ_DATA)
mode |= 04;
/* write */
if (isdir)
if (bits != 0) {
goto out;
}
mode |= 02;
}
/* exec */
if (mask & ACE_EXECUTE) {
mode |= 01;
}
out:
return (error);
}
static int
{
/* ACE_READ_ACL and ACE_READ_ATTRIBUTES must both be set */
(ACE_READ_ACL | ACE_READ_ATTRIBUTES)) {
return (ENOTSUP);
}
}
static int
{
int error;
if (isdir)
goto out;
}
goto out;
}
if (error != 0)
goto out;
} else {
goto out;
}
out:
return (error);
}
static int
{
int error = 0;
int resultcount;
goto out;
}
goto out;
}
/*
* This must be the same condition as below, when we add the CLASS_OBJ
* (aka ACL mask)
*/
resultcount += 1;
goto out;
}
/* USER_OBJ */
goto out;
}
isdir);
if (error != 0)
goto out;
++aent;
/* USER */
goto out;
}
isdir);
if (error != 0)
goto out;
++aent;
}
/* GROUP_OBJ */
goto out;
}
isdir);
if (error != 0)
goto out;
++aent;
/* GROUP */
goto out;
}
isdir);
if (error != 0)
goto out;
++aent;
}
/*
* CLASS_OBJ (aka ACL_MASK)
*
* An ACL_MASK is not fabricated if the ACL is a default ACL.
* This is to follow UFS's behavior.
*/
if (isdir)
if (error != 0)
goto out;
} else {
/* fabricate the ACL_MASK from the group permissions */
if (error != 0)
goto out;
}
++aent;
}
/* OTHER_OBJ */
goto out;
}
isdir);
if (error != 0)
goto out;
++aent;
*aclcnt = resultcount;
out:
if (error != 0) {
}
return (error);
}
/*
* free all data associated with an ace_list
*/
static void
{
void *cookie;
return;
/* free the container itself */
}
static int
{
return (0);
return (1);
else
return (-1);
}
/*
* Convert a list of ace_t entries to equivalent regular and default
* aclent_t lists. Return error (ENOTSUP) when conversion is not possible.
*/
static int
int isdir)
{
int error = 0;
int i;
*aclcnt = 0;
*dfaclcnt = 0;
/* we need at least user_obj, group_obj, and other_obj */
if (n < 6) {
goto out;
}
goto out;
}
goto out;
}
ace_list_init(normacl, 0);
goto out;
}
/* process every ace_t... */
for (i = 0; i < n; i++) {
/* rule out certain cases quickly */
if (error != 0)
goto out;
/*
* Turn off these bits in order to not have to worry about
* them when doing the checks for compliments.
*/
/* see if this should be a regular or default acl */
if (bits != 0) {
/* all or nothing on these inherit bits */
if (bits != (ACE_INHERIT_ONLY_ACE |
goto out;
}
} else {
}
goto out;
}
goto out;
}
} else {
goto out;
}
}
} else {
goto out;
}
goto out;
}
}
goto out;
}
/* no more than one allowed per aclent_t */
goto out;
}
} else {
/*
* it's a DENY; if there was a previous DENY, it
* must have been an ACL_MASK.
*/
/* ACL_MASK is for USER and GROUP only */
goto out;
}
/* check for mismatched ACL_MASK emulations */
goto out;
}
}
}
}
/* done collating; produce the aclent_t lists */
if (error != 0) {
goto out;
}
}
if (error != 0) {
goto out;
}
}
out:
return (error);
}
static int
{
int error;
if (error)
return (error);
if (dfaclcnt != 0) {
/*
* Slap aclentp and dfaclentp into a single array.
*/
} else {
error = -1;
}
}
if (aclentp) {
*retaclentp = aclentp;
}
if (dfaclentp)
return (error);
}
{
const char *fname;
int fd;
int ace_acl = 0;
int error;
int save_errno;
int stat_error;
} else {
}
if (ace_acl == -1)
return (-1);
/*
* if acl's aren't supported then
* send it through the old GETACL interface
*/
if (ace_acl == 0) {
}
if (ace_acl & _ACL_ACE_ENABLED) {
getcmd = ACE_GETACL;
} else {
}
return (-1);
} else {
}
save_errno = errno;
errno = save_errno;
return (-1);
}
errno = save_errno;
return (0);
}
save_errno = errno;
errno = save_errno;
return (-1);
}
} else {
}
save_errno = errno;
if (error == -1) {
errno = save_errno;
return (-1);
}
if (stat_error == 0) {
} else
case ACLENT_T:
break;
case ACE_T:
break;
default:
return (-1);
}
(get_flag & ACL_NO_TRIVIAL)) {
errno = 0;
return (0);
}
return (0);
}
/*
* return -1 on failure, otherwise the number of acl
* entries is returned
*/
int
{
}
int
{
}
static int
{
int aclcnt;
void *acldata;
int error;
/*
* See if we need to translate
*/
(target_flavor == _ACL_ACLENT_ENABLED &&
return (0);
if (target_flavor == -1)
return (-1);
if (target_flavor == _ACL_ACE_ENABLED &&
if (error) {
return (-1);
}
} else if (target_flavor == _ACL_ACLENT_ENABLED &&
if (error) {
return (-1);
}
} else {
return (-1);
}
/*
* replace old acl with newly translated acl
*/
return (0);
}
/*
* Set an ACL, translates acl to ace_t when appropriate.
*/
static int
{
int error = 0;
int acl_flavor_target;
int stat_error;
int isdir;
if (stat_error)
return (-1);
} else {
if (stat_error)
return (-1);
}
return (error);
}
} else {
}
return (error);
}
int
{
}
int
{
}
int
{
}
int
{
}
acl_t *
{
return (NULL);
return (NULL);
}
return (newaclp);
}
int
{
}
void *
{
}
/*
* Remove an ACL from a file and create a trivial ACL based
* are updated to match owner,group arguments
*/
int
{
int error = 0;
int acl_flavor;
int aclcnt;
if (acl_flavor == -1)
return (-1);
/*
* force it through aclent flavor when file system doesn't
* understand question
*/
if (acl_flavor == 0)
if (acl_flavor & _ACL_ACLENT_ENABLED) {
aclcnt = 4;
} else if (acl_flavor & _ACL_ACE_ENABLED) {
/*
* Make aces match request mode
*/
} else {
error = 1;
}
if (error == 0)
return (error);
}
static int
{
/*
* Need to fixup who field for abstrations for
* accurate comparison, since field is undefined.
*/
}
static int
{
}
/*
* Find acl entries in acl that correspond to removeacl. Search
* is started from slot. The flag argument indicates whether to
* remove all matches or just the first match.
*/
int
{
int i, j;
int match;
void *acl_entry, *remove_entry;
void *start;
int found = 0;
return (EACL_NO_ACL_ENTRY);
return (EACL_DIFF_TYPE);
else
j = 0;
for (;;) {
if (match == 0) {
found++;
if (flag == ACL_REMOVE_FIRST)
break;
/*
* List has changed, restart search from
* beginning.
*/
j = 0;
continue;
}
break;
}
}
}
return ((found == 0) ? EACL_NO_ACL_ENTRY : 0);
}
/*
* Replace entires entries in acl1 with the corresponding entries
* in newentries. The where argument specifies where to begin
* the replacement. If the where argument is 1 greater than the
* number of acl entries in acl1 then they are appended. If the
* where argument is 2+ greater than the number of acl entries then
* EACL_INVALID_SLOT is returned.
*/
int
{
int slot;
int slots_needed;
int slots_left;
int newsize;
return (EACL_NO_ACL_ENTRY);
return (EACL_INVALID_SLOT);
return (EACL_DIFF_TYPE);
return (-1);
}
/*
* Did ACL grow?
*/
}
return (0);
}
/*
* Add acl2 entries into acl1. The where argument specifies where
* to add the entries.
*/
int
{
int newsize;
int len;
void *start;
void *to;
return (EACL_NO_ACL_ENTRY);
return (EACL_DIFF_TYPE);
/*
* allow where to specify 1 past last slot for an append operation
* but anything greater is an error.
*/
return (EACL_INVALID_SLOT);
return (-1);
/*
* first push down entries where new ones will be inserted
*/
}
/*
* now stick in new entries.
*/
return (0);
}
/*
* return text for an ACL error.
*/
char *
acl_strerror(int errnum)
{
switch (errnum) {
case EACL_GRP_ERROR:
return (dgettext(TEXT_DOMAIN,
"There is more than one group or default group entry"));
case EACL_USER_ERROR:
return (dgettext(TEXT_DOMAIN,
"There is more than one user or default user entry"));
case EACL_OTHER_ERROR:
return (dgettext(TEXT_DOMAIN,
"There is more than one other entry"));
case EACL_CLASS_ERROR:
return (dgettext(TEXT_DOMAIN,
"There is more than one mask entry"));
case EACL_DUPLICATE_ERROR:
return (dgettext(TEXT_DOMAIN,
"Duplicate user or group entries"));
case EACL_MISS_ERROR:
return (dgettext(TEXT_DOMAIN,
case EACL_MEM_ERROR:
return (dgettext(TEXT_DOMAIN,
"Memory error"));
case EACL_ENTRY_ERROR:
return (dgettext(TEXT_DOMAIN,
"Unrecognized entry type"));
case EACL_INHERIT_ERROR:
return (dgettext(TEXT_DOMAIN,
"Invalid inheritance flags"));
case EACL_FLAGS_ERROR:
return (dgettext(TEXT_DOMAIN,
"Unrecognized entry flags"));
case EACL_PERM_MASK_ERROR:
return (dgettext(TEXT_DOMAIN,
"Invalid ACL permissions"));
case EACL_COUNT_ERROR:
return (dgettext(TEXT_DOMAIN,
"Invalid ACL count"));
case EACL_INVALID_SLOT:
return (dgettext(TEXT_DOMAIN,
"Invalid ACL entry number specified"));
case EACL_NO_ACL_ENTRY:
return (dgettext(TEXT_DOMAIN,
"ACL entry doesn't exist"));
case EACL_DIFF_TYPE:
return (dgettext(TEXT_DOMAIN,
"ACL type's are different"));
case EACL_INVALID_USER_GROUP:
case EACL_INVALID_STR:
case EACL_FIELD_NOT_BLANK:
case EACL_INVALID_ACCESS_TYPE:
case EACL_UNKNOWN_DATA:
case EACL_MISSING_FIELDS:
return (dgettext(TEXT_DOMAIN,
"ACL specification missing required fields"));
case EACL_INHERIT_NOTDIR:
return (dgettext(TEXT_DOMAIN,
"Inheritance flags are only allowed on directories"));
case -1:
default:
}
}
extern int yyinteractive;
/* PRINTFLIKE1 */
void
{
if (yyinteractive == 0)
return;
}