acl_common.c revision 1eb4e906ec75b9bde421954ace46ef137b0fc9eb
/*
* 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 2014 Nexenta Systems, Inc. All rights reserved.
*/
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
#include <sys/sysmacros.h>
#include <acl/acl_common.h>
#else
#include <errno.h>
#include <stdlib.h>
#include <stddef.h>
#include <strings.h>
#include <unistd.h>
#include <assert.h>
#include <grp.h>
#include <pwd.h>
#include <acl_common.h>
#endif
#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;
/*
* Generic shellsort, from K&R (1st ed, p 58.), somewhat modified.
* n = # objs in the array
* s = size of each obj (must be multiples of a word size)
* f = ptr to function to compare two objs
* returns (-1 = less than, 0 = equal, 1 = greater than
*/
void
{
int g, i, j, ii;
unsigned int tmp;
/* No work to do */
if (v == NULL || n <= 1)
return;
/* Sanity check on arguments */
ASSERT(s > 0);
for (g = n / 2; g > 0; g /= 2) {
for (i = g; i < n; i++) {
for (j = i - g; j >= 0 &&
(*f)(v + j * s, v + (j + g) * s) == 1;
j -= g) {
p1 = (void *)(v + j * s);
p2 = (void *)(v + (j + g) * s);
}
}
}
}
}
/*
* Compare two acls, all fields. Returns:
* -1 (less than)
* 0 (equal)
* +1 (greater than)
*/
int
cmp2acls(void *a, void *b)
{
/* Compare types */
return (-1);
return (1);
/* Equal types; compare id's */
return (-1);
return (1);
/* Equal ids; compare perms */
return (-1);
return (1);
/* Totally equal */
return (0);
}
/*ARGSUSED*/
static void *
{
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
void *tmp;
return (tmp);
#else
#endif
}
static int
{
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
return (0);
#else
return (errno);
return (0);
#endif
}
/*ARGSUSED*/
static void
{
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
#else
#endif
}
acl_t *
{
return (NULL);
switch (type) {
case ACE_T:
break;
case ACLENT_T:
break;
default:
}
return (aclp);
}
/*
* Free acl_t structure
*/
void
{
int acl_size;
return;
}
}
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 */
access |= ACE_READ_DATA;
}
/* write */
access |= ACE_WRITE_DATA |
if (isdir)
}
/* exec */
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 (EINVAL);
}
if (error)
return (error);
if (dfaclcnt) {
if (error) {
if (acep) {
}
return (error);
}
}
if (dfacecnt != 0) {
return (ENOMEM);
if (dfaclcnt) {
}
}
if (dfaclcnt)
return (0);
}
static int
{
int error = 0;
/* read */
if (mask & ACE_READ_DATA)
/* write */
if (isdir)
if (bits != 0) {
goto out;
}
}
/* exec */
if (mask & ACE_EXECUTE) {
}
out:
return (error);
}
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 (NULL);
(*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
{
/* 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;
if (cacl_malloc((void **)&result,
resultcount * sizeof (aclent_t)) != 0) {
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 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;
}
if (error != 0)
goto out;
ace_list_init(normacl, 0);
if (error != 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 = 0;
if (error)
return (error);
if (dfaclcnt != 0) {
/*
* Slap aclentp and dfaclentp into a single array.
*/
} else {
}
}
if (aclentp) {
*retaclentp = aclentp;
}
if (dfaclentp)
return (error);
}
int
{
int aclcnt;
void *acldata;
int error;
/*
* See if we need to translate
*/
(target_flavor == _ACL_ACLENT_ENABLED &&
return (0);
if (target_flavor == -1) {
goto out;
}
if (target_flavor == _ACL_ACE_ENABLED &&
if (error)
goto out;
} else if (target_flavor == _ACL_ACLENT_ENABLED &&
if (error)
goto out;
} else {
goto out;
}
/*
* replace old acl with newly translated acl
*/
if (target_flavor == _ACL_ACE_ENABLED) {
} else {
}
return (0);
out:
#if defined(_KERNEL) || defined(_FAKE_KERNEL)
return (error);
#else
return (-1);
#endif
}
}
void
{
if (isdir)
}
int
{
int index = 0;
int error;
*count = 3;
(*count)++;
(*count)++;
(*count)++;
return (error);
}
}
}
return (0);
}
/*
* ace_trivial:
* determine whether an ace_t acl is trivial
*
* Trivialness implies that the acl is composed of only
* owner, group, everyone entries. ACL can't
* have read_acl denied, and write_owner/write_acl/write_attributes
* can only be owner@ entry.
*/
int
{
switch (flags & ACE_TYPE_FLAGS) {
case ACE_OWNER:
case ACE_EVERYONE:
break;
default:
return (1);
}
if (flags & (ACE_FILE_INHERIT_ACE|
return (1);
/*
* Special check for some special bits
*
* Don't allow anybody to deny reading basic
* attributes or a files ACL.
*/
return (1);
/*
* Delete permission is never set by default
*/
if (mask & ACE_DELETE)
return (1);
/*
* Child delete permission should be accompanied by write
*/
return (1);
/*
* only allow owner@ to have
*/
if (type == ACE_ACCESS_ALLOWED_ACE_TYPE &&
return (1);
}
return (0);
}
{
return (0);
return (cookie);
}
int
{
}