/* $NetBSD: nfs_commonacl.c,v 1.2 2016/12/13 22:31:51 pgoyette Exp $ */
/*-
* Copyright (c) 2009 Rick Macklem, University of Guelph
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
/* local functions */
static int nfsrv_buildace(struct nfsrv_descript *, u_char *, int,
enum vtype, int, int, struct acl_entry *);
/*
* This function builds an NFS ace.
*/
static int
nfsrv_buildace(struct nfsrv_descript *nd, u_char *name, int namelen,
enum vtype type, int group, int owner, struct acl_entry *ace)
{
u_int32_t *tl, aceflag = 0x0, acemask = 0x0, acetype;
int full_len;
/*
* Fill in the ace type.
*/
if (ace->ae_entry_type & ACL_ENTRY_TYPE_ALLOW)
acetype = NFSV4ACE_ALLOWEDTYPE;
else if (ace->ae_entry_type & ACL_ENTRY_TYPE_DENY)
acetype = NFSV4ACE_DENIEDTYPE;
else if (ace->ae_entry_type & ACL_ENTRY_TYPE_AUDIT)
acetype = NFSV4ACE_AUDITTYPE;
else
acetype = NFSV4ACE_ALARMTYPE;
*tl++ = txdr_unsigned(acetype);
/*
* Set the flag bits from the ACL.
*/
if (ace->ae_flags & ACL_ENTRY_FILE_INHERIT)
aceflag |= NFSV4ACE_FILEINHERIT;
if (ace->ae_flags & ACL_ENTRY_DIRECTORY_INHERIT)
aceflag |= NFSV4ACE_DIRECTORYINHERIT;
if (ace->ae_flags & ACL_ENTRY_NO_PROPAGATE_INHERIT)
aceflag |= NFSV4ACE_NOPROPAGATEINHERIT;
if (ace->ae_flags & ACL_ENTRY_INHERIT_ONLY)
aceflag |= NFSV4ACE_INHERITONLY;
if (ace->ae_flags & ACL_ENTRY_SUCCESSFUL_ACCESS)
aceflag |= NFSV4ACE_SUCCESSFULACCESS;
if (ace->ae_flags & ACL_ENTRY_FAILED_ACCESS)
aceflag |= NFSV4ACE_FAILEDACCESS;
if (group)
aceflag |= NFSV4ACE_IDENTIFIERGROUP;
*tl++ = txdr_unsigned(aceflag);
if (type == VDIR) {
if (ace->ae_perm & ACL_LIST_DIRECTORY)
acemask |= NFSV4ACE_LISTDIRECTORY;
if (ace->ae_perm & ACL_ADD_FILE)
acemask |= NFSV4ACE_ADDFILE;
if (ace->ae_perm & ACL_ADD_SUBDIRECTORY)
acemask |= NFSV4ACE_ADDSUBDIRECTORY;
if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
acemask |= NFSV4ACE_READNAMEDATTR;
if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
acemask |= NFSV4ACE_WRITENAMEDATTR;
if (ace->ae_perm & ACL_EXECUTE)
acemask |= NFSV4ACE_SEARCH;
if (ace->ae_perm & ACL_DELETE_CHILD)
acemask |= NFSV4ACE_DELETECHILD;
if (ace->ae_perm & ACL_READ_ATTRIBUTES)
acemask |= NFSV4ACE_READATTRIBUTES;
if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
acemask |= NFSV4ACE_WRITEATTRIBUTES;
if (ace->ae_perm & ACL_DELETE)
acemask |= NFSV4ACE_DELETE;
if (ace->ae_perm & ACL_READ_ACL)
acemask |= NFSV4ACE_READACL;
if (ace->ae_perm & ACL_WRITE_ACL)
acemask |= NFSV4ACE_WRITEACL;
if (ace->ae_perm & ACL_WRITE_OWNER)
acemask |= NFSV4ACE_WRITEOWNER;
if (ace->ae_perm & ACL_SYNCHRONIZE)
acemask |= NFSV4ACE_SYNCHRONIZE;
} else {
if (ace->ae_perm & ACL_READ_DATA)
acemask |= NFSV4ACE_READDATA;
if (ace->ae_perm & ACL_WRITE_DATA)
acemask |= NFSV4ACE_WRITEDATA;
if (ace->ae_perm & ACL_APPEND_DATA)
acemask |= NFSV4ACE_APPENDDATA;
if (ace->ae_perm & ACL_READ_NAMED_ATTRS)
acemask |= NFSV4ACE_READNAMEDATTR;
if (ace->ae_perm & ACL_WRITE_NAMED_ATTRS)
acemask |= NFSV4ACE_WRITENAMEDATTR;
if (ace->ae_perm & ACL_EXECUTE)
acemask |= NFSV4ACE_EXECUTE;
if (ace->ae_perm & ACL_READ_ATTRIBUTES)
acemask |= NFSV4ACE_READATTRIBUTES;
if (ace->ae_perm & ACL_WRITE_ATTRIBUTES)
acemask |= NFSV4ACE_WRITEATTRIBUTES;
if (ace->ae_perm & ACL_DELETE)
acemask |= NFSV4ACE_DELETE;
if (ace->ae_perm & ACL_READ_ACL)
acemask |= NFSV4ACE_READACL;
if (ace->ae_perm & ACL_WRITE_ACL)
acemask |= NFSV4ACE_WRITEACL;
if (ace->ae_perm & ACL_WRITE_OWNER)
acemask |= NFSV4ACE_WRITEOWNER;
if (ace->ae_perm & ACL_SYNCHRONIZE)
acemask |= NFSV4ACE_SYNCHRONIZE;
}
*tl++ = txdr_unsigned(acemask);
*tl++ = txdr_unsigned(namelen);
if (full_len - namelen)
*(tl + (namelen / NFSX_UNSIGNED)) = 0x0;
NFSBCOPY(name, (caddr_t)tl, namelen);
return (full_len + 4 * NFSX_UNSIGNED);
}
/*
* Build an NFSv4 ACL.
*/
APPLESTATIC int
nfsrv_buildacl(struct nfsrv_descript *nd, NFSACL_T *aclp, enum vtype type,
NFSPROC_T *p)
{
int i, entrycnt = 0, retlen;
u_int32_t *entrycntp;
int isowner, isgroup, namelen, malloced;
u_char *name, namestr[NFSV4_SMALLSTR];
NFSM_BUILD(entrycntp, u_int32_t *, NFSX_UNSIGNED);
retlen = NFSX_UNSIGNED;
/*
* Loop through the acl entries, building each one.
*/
for (i = 0; i < aclp->acl_cnt; i++) {
isowner = isgroup = malloced = 0;
switch (aclp->acl_entry[i].ae_tag) {
case ACL_USER_OBJ:
isowner = 1;
name = "OWNER@";
namelen = 6;
break;
case ACL_GROUP_OBJ:
isgroup = 1;
name = "GROUP@";
namelen = 6;
break;
case ACL_EVERYONE:
name = "EVERYONE@";
namelen = 9;
break;
case ACL_USER:
name = namestr;
nfsv4_uidtostr(aclp->acl_entry[i].ae_id, &name,
&namelen, p);
if (name != namestr)
malloced = 1;
break;
case ACL_GROUP:
isgroup = 1;
name = namestr;
nfsv4_gidtostr((gid_t)aclp->acl_entry[i].ae_id, &name,
&namelen, p);
if (name != namestr)
malloced = 1;
break;
default:
continue;
}
retlen += nfsrv_buildace(nd, name, namelen, type, isgroup,
isowner, &aclp->acl_entry[i]);
entrycnt++;
if (malloced)
free(name, M_NFSSTRING);
}
*entrycntp = txdr_unsigned(entrycnt);
return (retlen);
}
/*
* Set an NFSv4 acl.
*/
APPLESTATIC int
nfsrv_setacl(vnode_t vp, NFSACL_T *aclp, struct ucred *cred,
NFSPROC_T *p)
{
int error;
if (nfsrv_useacl == 0 || nfs_supportsnfsv4acls(vp) == 0) {
error = NFSERR_ATTRNOTSUPP;
goto out;
}
/*
* With NFSv4 ACLs, chmod(2) may need to add additional entries.
* Make sure it has enough room for that - splitting every entry
* into two and appending "canonical six" entries at the end.
* Cribbed out of kern/vfs_acl.c - Rick M.
*/
if (aclp->acl_cnt > (ACL_MAX_ENTRIES - 6) / 2) {
error = NFSERR_ATTRNOTSUPP;
goto out;
}
error = VOP_SETACL(vp, ACL_TYPE_NFS4, aclp, cred, p);
out:
NFSEXITCODE(error);
return (error);
}
/*
* Compare two NFSv4 acls.
* Return 0 if they are the same, 1 if not the same.
*/
APPLESTATIC int
nfsrv_compareacl(NFSACL_T *aclp1, NFSACL_T *aclp2)
{
int i;
struct acl_entry *acep1, *acep2;
if (aclp1->acl_cnt != aclp2->acl_cnt)
return (1);
acep1 = aclp1->acl_entry;
acep2 = aclp2->acl_entry;
for (i = 0; i < aclp1->acl_cnt; i++) {
if (acep1->ae_tag != acep2->ae_tag)
return (1);
switch (acep1->ae_tag) {
case ACL_GROUP:
case ACL_USER:
if (acep1->ae_id != acep2->ae_id)
return (1);
/* fall through */
case ACL_USER_OBJ:
case ACL_GROUP_OBJ:
case ACL_OTHER:
if (acep1->ae_perm != acep2->ae_perm)
return (1);
}
acep1++;
acep2++;
}
return (0);
}