ufs_acl.c revision 2879e8a4b7cea216d18c3e536d6145465fd55453
/*
* 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 2007 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/sysmacros.h>
/* Cache routines */
static int si_signature(si_t *);
static void si_cache_put(si_t *);
void si_cache_del(si_t *, int);
void si_cache_init(void);
static void ufs_si_free_mem(si_t *);
static int acl_count(ufs_ic_acl_t *);
static int acl_validate(aclent_t *, int, int);
long si_cachehit = 0;
long si_cachemiss = 0;
/*
* Store the new acls in aclp. Attempts to make things atomic.
* Search the acl cache for an identical sp and, if found, attach
* the cache'd acl to ip. If the acl is new (not in the cache),
* add it to the cache, then attach it to ip. Last, remove and
* decrement the reference count of any prior acl list attached
* to the ip.
*
* Parameters:
* ip - Ptr to inode to receive the acl list
* sp - Ptr to in-core acl structure to attach to the inode.
* puship - 0 do not push the object inode(ip) 1 push the ip
* cr - Ptr to credentials
*
* Returns: 0 - Success
* N - From errno.h
*/
static int
{
int shadow;
int err;
int refcnt;
int usecnt;
int signature;
int resid;
return (ENOSYS);
/*
* create a shadow inode. If there is already a shadow with
* the file, remove it.
*
*/
return (0);
}
loop:
/*
* Check cache. If in cache, use existing shadow inode.
* Increment the shadow link count, then attach to the
* cached ufs_acl_entry struct, and increment it's reference
* count. Then discard the passed-in ufs_acl_entry and
* return.
*/
(void) ufs_si_free_mem(sp);
return (0);
}
/*
* We can't call ufs_iget while holding the csp locked,
* because we might deadlock. So we drop the
* lock on csp, then go search the si_cache again
* to see if the csp is still there.
*/
(void) ufs_si_free_mem(sp);
return (EIO);
}
goto loop;
}
/* Get the csp again */
goto loop;
}
/* See if we got the right shadow */
goto loop;
}
/* Increment link count */
/*
* Always release s_lock before both releasing i_contents
* and calling VN_RELE.
*/
(void) ufs_si_free_mem(sp);
si_cachehit++;
goto switchshadows;
}
/* Alloc a shadow inode and fill it in */
if (err) {
(void) ufs_si_free_mem(sp);
return (err);
}
goto errout;
offset = 0;
/*
* We don't actually care about the residual count upon failure,
* but giving ufs_rdwri() the pointer means it won't translate
* all failures to EIO. Our caller needs to know when ENOSPC
* gets hit.
*/
resid = 0;
(resid != 0)) {
goto errout;
}
offset += acldatalen;
/* Sync & free the shadow inode */
/* We're committed to using this sp */
/* Now put the new acl stuff in the cache */
/* XXX Might make a duplicate */
si_cachemiss++;
/* Now switch the parent inode to use the new shadow inode */
/*
* Change the mode bits to follow the acl list
*
* NOTE: a directory is not required to have a "regular" acl
* bug id's 1238908, 1257173, 1263171 and 1263188
*
* but if a "regular" acl is present, it must contain
* an "owner", "group", and "other" acl
*
* If an ACL mask exists, the effective group rights are
* set to the mask. Otherwise, the effective group rights
* are set to the object group bits.
*/
}
}
}
/*
* when creating a file there is no need to push the inode, it
* is pushed later
*/
if (puship == 1)
/*
* Decrement link count on the old shadow inode,
* and decrement reference count on the old aclp,
*/
if (oldshadow) {
/* Get the shadow inode */
return (EIO);
}
/* Decrement link count */
if (oldsp)
if (oldsp) {
/*
* Always release s_lock before both releasing
* i_contents and calling VN_RELE.
*/
}
}
return (0);
/* Throw the newly alloc'd inode away */
(void) ufs_si_free_mem(sp);
return (err);
}
/*
* Load the acls for inode ip either from disk (adding to the cache),
* or search the cache and attach the cache'd acl list to the ip.
* In either case, maintain the proper reference count on the cached entry.
*
* Parameters:
* ip - Ptr to the inode which needs the acl list loaded
* cr - Ptr to credentials
*
* Returns: 0 - Success
* N - From errno.h
*/
int
/*
* ip parent inode in
* cr credentials in
*/
{
vsecattr_t vsecattr = {
(uint_t)0,
(int)0,
(void *)NULL,
(int)0,
(void *)NULL};
int err;
int numacls;
int shadow;
int usecnt;
return (ENOSYS);
return (EIO);
return (EIO);
/*
* XXX Check cache. If in cache, link to it and increment
* the reference count, then return.
*/
si_cachehit++;
return (0);
}
/* Get the shadow inode */
return (err);
}
goto alldone;
}
}
/* Read the acl's and other stuff from disk */
UIO_SYSSPACE, (int *)0, cr);
if (err)
goto alldone;
/*
* Convert from disk format
* Result is a vsecattr struct which we then convert to the
* si struct.
*/
break;
case FSD_ACL:
sizeof (ufs_acl_t));
aclp++;
numacls--;
}
break;
case FSD_DFACL:
sizeof (ufs_acl_t));
aclp++;
numacls--;
}
break;
}
}
/* Sort the lists */
if (vsecattr.vsa_aclentp) {
goto alldone;
}
}
if (vsecattr.vsa_dfaclentp) {
goto alldone;
}
}
/* ignore shadow inodes without ACLs */
err = 0;
goto alldone;
}
/* Convert from vsecattr struct to ufs_acl_entry struct */
goto alldone;
}
/* There aren't filled in by vsecattr2aclentry */
/* XXX Might make a duplicate */
/* Signal anyone waiting on this shadow to be loaded */
err = 0;
si_cachemiss++;
/*
* Common exit point. Mark shadow inode as ISTALE
* if we detect an internal inconsistency, to
* prevent stray inodes appearing in the cache.
*/
if (err) {
}
/*
* Cleanup of data structures allocated
* on the fly.
*/
if (acldata)
if (vsecattr.vsa_aclentp)
if (vsecattr.vsa_dfaclentp)
return (err);
}
/*
* Check the inode's ACL's to see if this mode of access is
* allowed; return 0 if allowed, EACCES if not.
*
* We follow the procedure defined in Sec. 3.3.5, ACL Access
* Check Algorithm, of the POSIX 1003.6 Draft Standard.
*/
int
/*
* ip parent inode
* cr credentials
*/
{
int gperm = 0;
int ngroup = 0;
if (ismask)
else
mask = -1;
/*
* (1) If user owns the file, obey user mode bits
*/
}
/*
* (2) Obey any matching ACL_USER entry
*/
}
}
/*
* (3) If user belongs to file's group, obey group mode bits
* if no ACL mask is defined; if there is an ACL mask, we look
* at both the group mode bits and any ACL_GROUP entries.
*/
ngroup++;
if (!ismask)
}
/*
* (4) Accumulate the permissions in matching ACL_GROUP entries
*/
{
ngroup++;
}
}
if (ngroup != 0)
/*
* (5) Finally, use the "other" mode bits
*/
}
/*ARGSUSED2*/
int
{
/* XXX Range check, sanity check, shadow check */
/* If an ACL is present, get the data from the shadow inode info */
/*
* If no ACLs are present, fabricate one from the mode bits.
* This code is almost identical to fs_fab_acl(), but we
* already have the mode bits handy, so we'll avoid going
* through VOP_GETATTR() again.
*/
vsap->vsa_aclcnt = 0;
KM_SLEEP);
return (ENOMEM);
/* Owner */
aclentp++;
/* Group */
aclentp++;
/* Other */
aclentp++;
/* Class */
}
return (0);
}
/*ARGSUSED2*/
int
{
int err;
return (ENOSYS);
/*
* only the owner of the file or privileged users can change the ACLs
*/
return (EPERM);
/* Convert from vsecattr struct to ufs_acl_entry struct */
return (err);
/*
* Make the user & group objs in the acl list follow what's
* in the inode.
*/
#ifdef DEBUG
}
#endif /* DEBUG */
/*
* Write and cache the new acl list
*/
return (err);
}
/*
* XXX Scan sorted array of acl's, checking for:
* 1) Any duplicate/conflicting entries (same type and id)
* 2) More than 1 of USER_OBJ, GROUP_OBJ, OTHER_OBJ, CLASS_OBJ
* 3) More than 1 of DEF_USER_OBJ, DEF_GROUP_OBJ, DEF_OTHER_OBJ, DEF_CLASS_OBJ
*
* Parameters:
* aclentp - ptr to sorted list of acl entries.
* nentries - # acl entries on the list
* list contains regular acls, default acls, or both.
*
* Returns: 0 - Success
* EINVAL - Invalid list (dups or multiple entries of type USER_OBJ, etc)
*/
static int
{
int i;
int nuser_objs = 0;
int ngroup_objs = 0;
int nother_objs = 0;
int nclass_objs = 0;
int ndef_user_objs = 0;
int ndef_group_objs = 0;
int ndef_other_objs = 0;
int ndef_class_objs = 0;
int nusers = 0;
int ngroups = 0;
int ndef_users = 0;
int ndef_groups = 0;
int numdefs = 0;
/* Null list or list of one */
return (0);
if (nentries <= 0)
return (EINVAL);
for (i = 1; i < nentries; i++) {
return (EINVAL);
}
}
return (EINVAL);
/* Count types */
for (i = 0; i < nentries; i++) {
case USER_OBJ: /* Owner */
nuser_objs++;
break;
case GROUP_OBJ: /* Group */
ngroup_objs++;
break;
case OTHER_OBJ: /* Other */
nother_objs++;
break;
case CLASS_OBJ: /* Mask */
nclass_objs++;
break;
case DEF_USER_OBJ: /* Default Owner */
break;
case DEF_GROUP_OBJ: /* Default Group */
break;
case DEF_OTHER_OBJ: /* Default Other */
break;
case DEF_CLASS_OBJ: /* Default Mask */
break;
case USER: /* Users */
nusers++;
break;
case GROUP: /* Groups */
ngroups++;
break;
case DEF_USER: /* Default Users */
ndef_users++;
break;
case DEF_GROUP: /* Default Groups */
ndef_groups++;
break;
default: /* Unknown type */
return (EINVAL);
}
}
/*
* For normal acl's, we require there be one (and only one)
* USER_OBJ, GROUP_OBJ and OTHER_OBJ. There is either zero
* or one CLASS_OBJ.
*/
return (EINVAL);
}
/*
* If there are ANY group acls, there MUST be a
* class_obj(mask) acl (1003.6/D12 p. 29 lines 75-80).
*/
if (ngroups && !nclass_objs) {
return (EINVAL);
}
return (EINVAL);
}
/*
* For default acl's, we require that there be either one (and only one)
* DEF_USER_OBJ, DEF_GROUP_OBJ and DEF_OTHER_OBJ
* or there be none of them.
*/
if (flag & DEF_ACL_CHECK) {
return (EINVAL);
}
return (EINVAL);
}
/*
* If there are ANY def_group acls, there MUST be a
* def_class_obj(mask) acl (1003.6/D12 P. 29 lines 75-80).
* XXX(jimh) This is inferred.
*/
if (ndef_groups && !ndef_class_objs) {
return (EINVAL);
}
if ((ndef_users || ndef_groups) &&
return (EINVAL);
}
return (EINVAL);
}
return (0);
}
static int
{
return (0);
}
/*
* XXX - Make more efficient
* Convert from the vsecattr struct, used by the VOP interface, to
* the ufs_acl_entry struct used for in-core storage of acl's.
*
* Parameters:
* vsap - Ptr to array of security attributes.
* spp - Ptr to ptr to si struct for the results
*
* Returns: 0 - Success
* N - From errno.h
*/
static int
{
int err;
int i;
/* Sort & validate the lists on the vsap */
return (err);
return (err);
/* Create new si struct and hang acl's off it */
/* Process acl list */
for (i = 0; i < vsap->vsa_aclcnt; i++) {
case USER_OBJ: /* Owner */
goto error;
break;
case GROUP_OBJ: /* Group */
goto error;
break;
case OTHER_OBJ: /* Other */
goto error;
break;
case USER:
goto error;
break;
case CLASS_OBJ: /* Mask */
break;
case GROUP:
goto error;
break;
default:
break;
}
aclentp--;
}
/* Process default acl list */
for (i = 0; i < vsap->vsa_dfaclcnt; i++) {
case DEF_USER_OBJ: /* Default Owner */
goto error;
break;
case DEF_GROUP_OBJ: /* Default Group */
goto error;
break;
case DEF_OTHER_OBJ: /* Default Other */
goto error;
break;
case DEF_USER:
goto error;
break;
case DEF_CLASS_OBJ: /* Default Mask */
break;
case DEF_GROUP:
goto error;
break;
default:
break;
}
aclentp--;
}
return (0);
return (err);
}
void
{
(*aclentpp)++;
}
}
/*
* XXX - Make more efficient
* Convert from the ufs_acl_entry struct used for in-core storage of acl's
* to the vsecattr struct, used by the VOP interface.
*
* Parameters:
* sp - Ptr to si struct with the acls
* vsap - Ptr to a vsecattr struct which will take the results.
*
* Returns: 0 - Success
* N - From errno table
*/
static int
{
int numacls = 0;
int err;
numacls++;
if (numacls == 0)
goto do_defaults;
KM_SLEEP);
aclentp++;
}
/* Sort the acl list */
/* Check the acl list */
return (err);
}
}
/* Process Defaults */
numacls++;
if (numacls == 0)
goto do_others;
aclentp++;
}
/* Sort the default acl list */
return (err);
}
}
return (0);
}
static void
{
}
}
/*
* ufs_si_free_mem will discard the sp, and the acl hanging off of the
* sp. It is required that the sp not be locked, and not be in the
* cache.
*
* input: pointer to sp to discard.
*
* return - nothing.
*
*/
static void
{
/*
* remove from the cache
* free the acl entries
*/
}
void
{
return;
}
}
}
/*
* ufs_si_inherit takes a parent acl structure (saclp) and the inode
* of the object that is inheriting an acl and returns the inode
* with the acl linked to it. It also writes the acl to disk if
* it is a unique inode.
*
* ip - pointer to inode of object inheriting the acl (contents lock)
* tdp - parent inode (rw_lock and contents lock)
* mode - creation modes
* cr - credentials pointer
*/
int
{
int error;
int mask;
/*
* or moved to a directory with a default acl do not allow inheritance
* just return.
*/
return (0);
/* lock the parent security information */
if (mask == 0) {
return (0);
}
if (mask != 7) {
return (EINVAL);
}
/* copy the default acls */
/*
* set the owner, group, and other values from the master
* inode.
*/
}
/* copy default acl if necessary */
}
/*
* save the new 9 mode bits in the inode (ip->ic_smode) for
* ufs_getattr. Be sure the mode can be recovered if the store
* fails.
*/
/*
* store the acl, and get back a new security anchor if
* it is a duplicate.
*/
/*
* Suppress out of inodes messages if instructed in the
* tdp inode.
*/
}
return (error);
}
si_t *
{
return (dsp);
}
int
{
int error = 0;
return (0);
/*
* if no regular acl's, nothing to do, so let's get out
*/
return (0);
/*
* set the mask to the group permissions if a mask entry
* exists. Otherwise, set the group obj bits to the group
* permissions. Since non-trivial ACLs always have a mask,
* and the mask is the final arbiter of group permissions,
* setting the mask has the effect of changing the effective
* group permissions, even if the group_obj permissions in
* the ACL aren't changed. Posix P1003.1e states that when
* an ACL mask exists, chmod(2) must set the acl mask (NOT the
* group_obj permissions) to the requested group permissions.
*/
else
}
/* Caller has verified our privileges */
}
}
return (error);
}
static int
{
int count;
;
return (count);
}
/*
* Takes as input a security structure and generates a buffer
* with fsd's in a form which be written to the shadow inode.
*/
static int
{
/*
* Calc size of buffer to hold all the acls
*/
acl_size++;
/* Convert to bytes */
/* Add fsd header */
if (acl_size)
acl_size += 2 * sizeof (int);
/*
* Calc size of buffer to hold all the default acls
*/
def_acl_size++;
/*
* Convert to bytes
*/
def_acl_size *= sizeof (ufs_acl_t);
/*
* Add fsd header
*/
if (def_acl_size)
def_acl_size += 2 * sizeof (int);
if (acl_size + def_acl_size == 0)
return (0);
if (acl_size == 0)
goto wrtdefs;
/* create fsd and copy acls */
bufaclp++;
}
if (def_acl_size == 0)
goto alldone;
/* if defaults exist then create fsd and copy default acls */
bufaclp++;
}
return (0);
}
/*
* free a shadow inode on disk and in memory
*/
int
{
int shadow;
int err = 0;
int refcnt;
int signature;
/*
* Decrement link count on the shadow inode,
* and decrement reference count on the sip.
*/
/* Decrement link count */
/*
* bug #1264710 assertion failure below
*/
/* Dec ref counts on si referenced by this ip */
/*
* Release s_lock before calling VN_RELE
* (which may want to acquire i_contents).
*/
} else {
/* Dec ref counts on si referenced by this ip */
}
if (refcnt == 0)
return (err);
}
/*
* Seach the si cache for an si structure by inode #.
* Returns a locked si structure.
*
* Parameters:
* ip - Ptr to an inode on this fs
* spp - Ptr to ptr to si struct for the results, if found.
*
* Returns: 0 - Success (results in spp)
* 1 - Failure (spp undefined)
*/
static int
{
loop:
break;
/* Not in cache */
return (1);
}
/* Found it */
return (0);
}
/*
* Seach the si cache by si structure (ie duplicate of the one passed in).
* In order for a match the signatures must be the same and
* the devices must be the same, the acls must match and
* link count of the cached shadow must be less than the
* size of ic_nlink - 1. MAXLINK - 1 is used to allow the count
* to be incremented one more time by the caller.
* Returns a locked si structure.
*
* Parameters:
* ip - Ptr to an inode on this fs
* spi - Ptr to si the struct we're searching the cache for.
* spp - Ptr to ptr to si struct for the results, if found.
*
* Returns: 0 - Success (results in spp)
* 1 - Failure (spp undefined)
*/
static int
{
loop:
break;
}
/* Cache miss */
return (1);
}
/* Found it */
return (0);
}
/*
* Place an si structure in the si cache. May cause duplicates.
*
* Parameters:
* sp - Ptr to the si struct to add to the cache.
*
* Returns: Nothing (void)
*/
static void
{
if (!sp->s_signature)
/* The 'by acl' chains */
/* The 'by inode' chains */
}
/*
* The sp passed in is a candidate for deletion from the cache. We acquire
* the cache lock first, so no cache searches can be done. Then we search
* for the acl in the cache, and if we find it we can lock it and check that
* nobody else attached to it while we were acquiring the locks. If the acl
* is in the cache and still has a zero reference count, then we remove it
* from the cache and deallocate it. If the reference count is non-zero or
* it is not found in the cache, then someone else attached to it or has
* already freed it, so we just return.
*
* Parameters:
* sp - Ptr to the sp struct which is the candicate for deletion.
* signature - the signature for the acl for lookup in the hash table
*
* Returns: Nothing (void)
*/
void
{
int hash;
int foundacl = 0;
/*
* Unlink & free the sp from the other queues, then destroy it.
* Search the 'by acl' chain first, then the 'by inode' chain
* after the acl is locked.
*/
/*
* Wait to grab the acl lock until after the acl has
* been found in the cache. Otherwise it might try to
* grab a lock that has already been destroyed, or
* delete an acl that has already been freed.
*/
/* See if someone else attached to it */
return;
}
foundacl = 1;
break;
}
}
/*
* If the acl was not in the cache, we assume another thread has
* deleted it already. This could happen if another thread attaches to
* the acl and then releases it after this thread has already found the
* reference count to be zero but has not yet taken the cache lock.
* Both threads end up seeing a reference count of zero, and call into
* si_cache_del. See bug 4244827 for details on the race condition.
*/
if (foundacl == 0) {
return;
}
/* Now check the 'by inode' chain */
break;
}
}
/*
* At this point, we can unlock everything because this si
* is no longer in the cache, thus cannot be attached to.
*/
(void) ufs_si_free_mem(sp);
}
/*
* Alloc the hash buckets for the si cache & initialize
* the unreferenced anchor and the cache lock.
*/
void
si_cache_init(void)
{
/* The 'by acl' headers */
/* The 'by inode' headers */
}
/*
* aclcksum takes an acl and generates a checksum. It takes as input
* the acl to start at.
*
* s_aclp - pointer to starting acl
*
* returns checksum
*/
static int
{
int signature = 0;
}
return (signature);
}
/*
* Generate a unique signature for an si structure. Used by the
* search routine si_cachea_get() to quickly identify candidates
* prior to calling si_cmp().
* Parameters:
* sp - Ptr to the si struct to generate the signature for.
*
* Returns: A signature for the si struct (really a checksum)
*/
static int
{
return (signature);
}
/*
* aclcmp compares to acls to see if they are identical.
*
* sp1 is source
* sp2 is sourceb
*
* returns 0 if equal and 1 if not equal
*/
static int
{
/*
* if the starting pointers are equal then they are equal so
* just return.
*/
return (0);
/*
* check element by element
*/
return (1);
}
/*
* both must be zero (at the end of the acl)
*/
return (1);
return (0);
}
/*
* Do extensive, field-by-field compare of two si structures. Returns
* 0 if they are exactly identical, 1 otherwise.
*
* Paramters:
* sp1 - Ptr to 1st si struct
* sp2 - Ptr to 2nd si struct
*
* Returns:
* 0 - Not identical
* 1 - Identical
*/
static int
{
return (1);
return (1);
return (1);
return (1);
return (1);
return (1);
return (0);
}
/*
* Remove all acls associated with a device. All acls must have
* a reference count of zero.
*
* inputs:
* device - device to remove from the cache
*
* outputs:
* none
*/
void
{
int i;
for (i = 0; i < si_cachecnt; i++) {
while (*tspp) {
} else {
}
}
}
for (i = 0; i < si_cachecnt; i++) {
while (*tspp) {
} else {
}
}
}
}
/*
* ufs_si_del is used to unhook a sp from a inode in memory
*
* ip is the inode to remove the sp from.
*/
void
{
int refcnt;
int signature;
if (sp) {
if (refcnt == 0)
}
}