Home | History | Annotate | Download | only in syscall
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
     27 /*	  All Rights Reserved  	*/
     28 
     29 /*
     30  * Portions of this source code were derived from Berkeley 4.3 BSD
     31  * under license from the Regents of the University of California.
     32  */
     33 
     34 #pragma ident	"@(#)acl.c	1.26	07/12/26 SMI"
     35 
     36 #include <sys/param.h>
     37 #include <sys/isa_defs.h>
     38 #include <sys/types.h>
     39 #include <sys/sysmacros.h>
     40 #include <sys/cred.h>
     41 #include <sys/systm.h>
     42 #include <sys/errno.h>
     43 #include <sys/fcntl.h>
     44 #include <sys/pathname.h>
     45 #include <sys/vfs.h>
     46 #include <sys/vnode.h>
     47 #include <sys/file.h>
     48 #include <sys/mode.h>
     49 #include <sys/uio.h>
     50 #include <sys/kmem.h>
     51 #include <sys/filio.h>
     52 #include <sys/acl.h>
     53 #include <sys/cmn_err.h>
     54 #include <acl/acl_common.h>
     55 
     56 #include <sys/unistd.h>
     57 #include <sys/debug.h>
     58 #include <fs/fs_subr.h>
     59 
     60 static int cacl(int cmd, int nentries, void *aclbufp,
     61     vnode_t *vp, int *rv);
     62 
     63 /*
     64  * Get/Set ACL of a file.
     65  */
     66 int
     67 acl(const char *fname, int cmd, int nentries, void *aclbufp)
     68 {
     69 	struct vnode *vp;
     70 	int error;
     71 	int rv = 0;
     72 	int estale_retry = 0;
     73 
     74 	/* Sanity check arguments */
     75 	if (fname == NULL)
     76 		return (set_errno(EINVAL));
     77 lookup:
     78 	error = lookupname((char *)fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp);
     79 	if (error) {
     80 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
     81 			goto lookup;
     82 		return (set_errno(error));
     83 	}
     84 
     85 	error = cacl(cmd, nentries, aclbufp, vp, &rv);
     86 	VN_RELE(vp);
     87 	if (error) {
     88 		if ((error == ESTALE) && fs_need_estale_retry(estale_retry++))
     89 			goto lookup;
     90 		return (set_errno(error));
     91 	}
     92 	return (rv);
     93 }
     94 
     95 /*
     96  * Get/Set ACL of a file with facl system call.
     97  */
     98 int
     99 facl(int fdes, int cmd, int nentries, void *aclbufp)
    100 {
    101 	file_t *fp;
    102 	int error;
    103 	int rv = 0;
    104 
    105 	if ((fp = getf(fdes)) == NULL)
    106 		return (set_errno(EBADF));
    107 	if (fp->f_flag & FREVOKED) {
    108 		releasef(fdes);
    109 		return (set_errno(EBADF));
    110 	}
    111 
    112 	error = cacl(cmd, nentries, aclbufp, fp->f_vnode, &rv);
    113 	releasef(fdes);
    114 
    115 	if (error)
    116 		return (set_errno(error));
    117 	return (rv);
    118 }
    119 
    120 
    121 /*
    122  * Common code for acl() and facl().
    123  */
    124 static int
    125 cacl(int cmd, int nentries, void *aclbufp, vnode_t *vp, int *rv)
    126 {
    127 	int		error;
    128 	int		aclbsize;	/* size of acl list in bytes */
    129 	int		dfaclbsize;	/* size of default acl list in bytes */
    130 	int		numacls;
    131 	caddr_t		uaddrp;
    132 	aclent_t	*aclp, *aaclp;
    133 	vsecattr_t	vsecattr;
    134 	size_t		entry_size;
    135 
    136 	ASSERT(vp);
    137 
    138 	bzero(&vsecattr, sizeof (vsecattr_t));
    139 
    140 	switch (cmd) {
    141 
    142 	case ACE_GETACLCNT:
    143 	case GETACLCNT:
    144 		if (cmd == GETACLCNT) {
    145 			entry_size = sizeof (aclent_t);
    146 			vsecattr.vsa_mask = VSA_ACLCNT | VSA_DFACLCNT;
    147 		} else {
    148 			entry_size = sizeof (ace_t);
    149 			vsecattr.vsa_mask = VSA_ACECNT;
    150 		}
    151 		if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED(), NULL))
    152 			return (error);
    153 		*rv = vsecattr.vsa_aclcnt + vsecattr.vsa_dfaclcnt;
    154 		if (vsecattr.vsa_aclcnt && vsecattr.vsa_aclentp) {
    155 			kmem_free(vsecattr.vsa_aclentp,
    156 			    vsecattr.vsa_aclcnt * entry_size);
    157 		}
    158 		if (vsecattr.vsa_dfaclcnt && vsecattr.vsa_dfaclentp) {
    159 			kmem_free(vsecattr.vsa_dfaclentp,
    160 			    vsecattr.vsa_dfaclcnt * entry_size);
    161 		}
    162 		break;
    163 	case GETACL:
    164 		/*
    165 		 * Minimum ACL size is three entries so might as well
    166 		 * bail out here.
    167 		 */
    168 		if (nentries < 3)
    169 			return (EINVAL);
    170 		/*
    171 		 * NULL output buffer is also a pretty easy bail out.
    172 		 */
    173 		if (aclbufp == NULL)
    174 			return (EFAULT);
    175 		vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL |
    176 		    VSA_DFACLCNT;
    177 		if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED(), NULL))
    178 			return (error);
    179 		/* Check user's buffer is big enough */
    180 		numacls = vsecattr.vsa_aclcnt + vsecattr.vsa_dfaclcnt;
    181 		aclbsize = vsecattr.vsa_aclcnt * sizeof (aclent_t);
    182 		dfaclbsize = vsecattr.vsa_dfaclcnt * sizeof (aclent_t);
    183 		if (numacls > nentries) {
    184 			error = ENOSPC;
    185 			goto errout;
    186 		}
    187 		/* Sort the acl & default acl lists */
    188 		if (vsecattr.vsa_aclcnt > 1)
    189 			ksort((caddr_t)vsecattr.vsa_aclentp,
    190 			    vsecattr.vsa_aclcnt, sizeof (aclent_t), cmp2acls);
    191 		if (vsecattr.vsa_dfaclcnt > 1)
    192 			ksort((caddr_t)vsecattr.vsa_dfaclentp,
    193 			    vsecattr.vsa_dfaclcnt, sizeof (aclent_t), cmp2acls);
    194 		/* Copy out acl's */
    195 		uaddrp = (caddr_t)aclbufp;
    196 		if (aclbsize > 0) {	/* bug #1262490 */
    197 			if (copyout(vsecattr.vsa_aclentp, uaddrp, aclbsize)) {
    198 				error = EFAULT;
    199 				goto errout;
    200 			}
    201 		}
    202 		/* Copy out default acl's */
    203 		if (dfaclbsize > 0) {
    204 			uaddrp += aclbsize;
    205 			if (copyout(vsecattr.vsa_dfaclentp,
    206 			    uaddrp, dfaclbsize)) {
    207 				error = EFAULT;
    208 				goto errout;
    209 			}
    210 		}
    211 		*rv = numacls;
    212 		if (vsecattr.vsa_aclcnt) {
    213 			kmem_free(vsecattr.vsa_aclentp,
    214 			    vsecattr.vsa_aclcnt * sizeof (aclent_t));
    215 		}
    216 		if (vsecattr.vsa_dfaclcnt) {
    217 			kmem_free(vsecattr.vsa_dfaclentp,
    218 			    vsecattr.vsa_dfaclcnt * sizeof (aclent_t));
    219 		}
    220 		break;
    221 
    222 	case ACE_GETACL:
    223 		if (aclbufp == NULL)
    224 			return (EFAULT);
    225 
    226 		vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT;
    227 		if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED(), NULL))
    228 			return (error);
    229 
    230 		aclbsize = vsecattr.vsa_aclcnt * sizeof (ace_t);
    231 		if (vsecattr.vsa_aclcnt > nentries) {
    232 			error = ENOSPC;
    233 			goto errout;
    234 		}
    235 
    236 		if (aclbsize > 0) {
    237 			if ((error = copyout(vsecattr.vsa_aclentp,
    238 			    aclbufp, aclbsize)) != 0) {
    239 				goto errout;
    240 			}
    241 		}
    242 
    243 		*rv = vsecattr.vsa_aclcnt;
    244 		if (vsecattr.vsa_aclcnt) {
    245 			kmem_free(vsecattr.vsa_aclentp, vsecattr.vsa_aclentsz);
    246 		}
    247 		break;
    248 
    249 	case SETACL:
    250 		/*
    251 		 * Minimum ACL size is three entries so might as well
    252 		 * bail out here.  Also limit request size to prevent user
    253 		 * from allocating too much kernel memory.  Maximum size
    254 		 * is MAX_ACL_ENTRIES for the ACL part and MAX_ACL_ENTRIES
    255 		 * for the default ACL part. (bug 4058667)
    256 		 */
    257 		if (nentries < 3 || nentries > (MAX_ACL_ENTRIES * 2))
    258 			return (EINVAL);
    259 		/*
    260 		 * NULL output buffer is also an easy bail out.
    261 		 */
    262 		if (aclbufp == NULL)
    263 			return (EFAULT);
    264 		vsecattr.vsa_mask = VSA_ACL;
    265 		aclbsize = nentries * sizeof (aclent_t);
    266 		vsecattr.vsa_aclentp = kmem_alloc(aclbsize, KM_SLEEP);
    267 		aaclp = vsecattr.vsa_aclentp;
    268 		vsecattr.vsa_aclcnt = nentries;
    269 		uaddrp = (caddr_t)aclbufp;
    270 		if (copyin(uaddrp, vsecattr.vsa_aclentp, aclbsize)) {
    271 			kmem_free(aaclp, aclbsize);
    272 			return (EFAULT);
    273 		}
    274 		/* Sort the acl list */
    275 		ksort((caddr_t)vsecattr.vsa_aclentp,
    276 		    vsecattr.vsa_aclcnt, sizeof (aclent_t), cmp2acls);
    277 
    278 		/* Break into acl and default acl lists */
    279 		for (numacls = 0, aclp = vsecattr.vsa_aclentp;
    280 		    numacls < vsecattr.vsa_aclcnt;
    281 		    aclp++, numacls++) {
    282 			if (aclp->a_type & ACL_DEFAULT)
    283 				break;
    284 		}
    285 
    286 		/* Find where defaults start (if any) */
    287 		if (numacls < vsecattr.vsa_aclcnt) {
    288 			vsecattr.vsa_mask |= VSA_DFACL;
    289 			vsecattr.vsa_dfaclcnt = nentries - numacls;
    290 			vsecattr.vsa_dfaclentp = aclp;
    291 			vsecattr.vsa_aclcnt = numacls;
    292 		}
    293 		/* Adjust if they're all defaults */
    294 		if (vsecattr.vsa_aclcnt == 0) {
    295 			vsecattr.vsa_mask &= ~VSA_ACL;
    296 			vsecattr.vsa_aclentp = NULL;
    297 		}
    298 		/* Only directories can have defaults */
    299 		if (vsecattr.vsa_dfaclcnt && vp->v_type != VDIR) {
    300 			kmem_free(aaclp, aclbsize);
    301 			return (ENOTDIR);
    302 		}
    303 		(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
    304 		if (error = VOP_SETSECATTR(vp, &vsecattr, 0, CRED(), NULL)) {
    305 			kmem_free(aaclp, aclbsize);
    306 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
    307 			return (error);
    308 		}
    309 
    310 		/*
    311 		 * Should return 0 upon success according to the man page
    312 		 * and SVR4 semantics. (Bug #1214399: SETACL returns wrong rc)
    313 		 */
    314 		*rv = 0;
    315 		kmem_free(aaclp, aclbsize);
    316 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
    317 		break;
    318 
    319 	case ACE_SETACL:
    320 		if (nentries < 1 || nentries > MAX_ACL_ENTRIES)
    321 			return (EINVAL);
    322 
    323 		if (aclbufp == NULL)
    324 			return (EFAULT);
    325 
    326 		vsecattr.vsa_mask = VSA_ACE;
    327 		aclbsize = nentries * sizeof (ace_t);
    328 		vsecattr.vsa_aclentp = kmem_alloc(aclbsize, KM_SLEEP);
    329 		aaclp = vsecattr.vsa_aclentp;
    330 		vsecattr.vsa_aclcnt = nentries;
    331 		vsecattr.vsa_aclentsz = aclbsize;
    332 		uaddrp = (caddr_t)aclbufp;
    333 		if (copyin(uaddrp, vsecattr.vsa_aclentp, aclbsize)) {
    334 			kmem_free(aaclp, aclbsize);
    335 			return (EFAULT);
    336 		}
    337 		(void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL);
    338 		if (error = VOP_SETSECATTR(vp, &vsecattr, 0, CRED(), NULL)) {
    339 			kmem_free(aaclp, aclbsize);
    340 			VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
    341 			return (error);
    342 		}
    343 		*rv = 0;
    344 		kmem_free(aaclp, aclbsize);
    345 		VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL);
    346 		break;
    347 
    348 	default:
    349 		return (EINVAL);
    350 	}
    351 
    352 	return (0);
    353 
    354 errout:
    355 	if (aclbsize && vsecattr.vsa_aclentp)
    356 		kmem_free(vsecattr.vsa_aclentp, aclbsize);
    357 	if (dfaclbsize && vsecattr.vsa_dfaclentp)
    358 		kmem_free(vsecattr.vsa_dfaclentp, dfaclbsize);
    359 	return (error);
    360 }
    361