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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 /* 31 * Portions of this source code were derived from Berkeley 4.3 BSD 32 * under license from the Regents of the University of California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #include <sys/param.h> 38 #include <sys/isa_defs.h> 39 #include <sys/types.h> 40 #include <sys/sysmacros.h> 41 #include <sys/cred.h> 42 #include <sys/systm.h> 43 #include <sys/errno.h> 44 #include <sys/fcntl.h> 45 #include <sys/pathname.h> 46 #include <sys/vfs.h> 47 #include <sys/vnode.h> 48 #include <sys/file.h> 49 #include <sys/mode.h> 50 #include <sys/uio.h> 51 #include <sys/kmem.h> 52 #include <sys/filio.h> 53 #include <sys/acl.h> 54 #include <sys/cmn_err.h> 55 56 #include <sys/unistd.h> 57 #include <sys/debug.h> 58 59 static int cacl(int cmd, int nentries, void *aclbufp, 60 vnode_t *vp, int *rv); 61 62 /* 63 * Get/Set ACL of a file. 64 */ 65 int 66 acl(const char *fname, int cmd, int nentries, void *aclbufp) 67 { 68 struct vnode *vp; 69 int error; 70 int rv = 0; 71 72 /* Sanity check arguments */ 73 if (fname == NULL) 74 return (set_errno(EINVAL)); 75 lookup: 76 error = lookupname((char *)fname, UIO_USERSPACE, FOLLOW, NULLVPP, &vp); 77 if (error) { 78 if (error == ESTALE) 79 goto lookup; 80 return (set_errno(error)); 81 } 82 83 error = cacl(cmd, nentries, aclbufp, vp, &rv); 84 VN_RELE(vp); 85 if (error) { 86 if (error == ESTALE) 87 goto lookup; 88 return (set_errno(error)); 89 } 90 return (rv); 91 } 92 93 /* 94 * Get/Set ACL of a file with facl system call. 95 */ 96 int 97 facl(int fdes, int cmd, int nentries, void *aclbufp) 98 { 99 file_t *fp; 100 int error; 101 int rv = 0; 102 103 if ((fp = getf(fdes)) == NULL) 104 return (set_errno(EBADF)); 105 #ifdef C2_AUDIT 106 if (fp->f_flag & FREVOKED) { 107 releasef(fdes); 108 return (set_errno(EBADF)); 109 } 110 #endif /* C2_AUDIT */ 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 135 ASSERT(vp); 136 137 bzero(&vsecattr, sizeof (vsecattr_t)); 138 139 switch (cmd) { 140 141 case ACE_GETACLCNT: 142 case GETACLCNT: 143 if (cmd == GETACLCNT) 144 vsecattr.vsa_mask = VSA_ACLCNT | VSA_DFACLCNT; 145 else 146 vsecattr.vsa_mask = VSA_ACECNT; 147 if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED())) 148 return (error); 149 *rv = vsecattr.vsa_aclcnt + vsecattr.vsa_dfaclcnt; 150 if (vsecattr.vsa_aclcnt && vsecattr.vsa_aclentp) { 151 kmem_free(vsecattr.vsa_aclentp, 152 vsecattr.vsa_aclcnt * sizeof (aclent_t)); 153 } 154 if (vsecattr.vsa_dfaclcnt && vsecattr.vsa_dfaclentp) { 155 kmem_free(vsecattr.vsa_dfaclentp, 156 vsecattr.vsa_dfaclcnt * sizeof (aclent_t)); 157 } 158 break; 159 case GETACL: 160 /* 161 * Minimum ACL size is three entries so might as well 162 * bail out here. 163 */ 164 if (nentries < 3) 165 return (EINVAL); 166 /* 167 * NULL output buffer is also a pretty easy bail out. 168 */ 169 if (aclbufp == NULL) 170 return (EFAULT); 171 vsecattr.vsa_mask = VSA_ACL | VSA_ACLCNT | VSA_DFACL | 172 VSA_DFACLCNT; 173 if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED())) 174 return (error); 175 /* Check user's buffer is big enough */ 176 numacls = vsecattr.vsa_aclcnt + vsecattr.vsa_dfaclcnt; 177 aclbsize = vsecattr.vsa_aclcnt * sizeof (aclent_t); 178 dfaclbsize = vsecattr.vsa_dfaclcnt * sizeof (aclent_t); 179 if (numacls > nentries) { 180 error = ENOSPC; 181 goto errout; 182 } 183 /* Sort the acl & default acl lists */ 184 if (vsecattr.vsa_aclcnt > 1) 185 ksort((caddr_t)vsecattr.vsa_aclentp, 186 vsecattr.vsa_aclcnt, sizeof (aclent_t), cmp2acls); 187 if (vsecattr.vsa_dfaclcnt > 1) 188 ksort((caddr_t)vsecattr.vsa_dfaclentp, 189 vsecattr.vsa_dfaclcnt, sizeof (aclent_t), cmp2acls); 190 /* Copy out acl's */ 191 uaddrp = (caddr_t)aclbufp; 192 if (aclbsize > 0) { /* bug #1262490 */ 193 if (copyout(vsecattr.vsa_aclentp, uaddrp, aclbsize)) { 194 error = EFAULT; 195 goto errout; 196 } 197 } 198 /* Copy out default acl's */ 199 if (dfaclbsize > 0) { 200 uaddrp += aclbsize; 201 if (copyout(vsecattr.vsa_dfaclentp, 202 uaddrp, dfaclbsize)) { 203 error = EFAULT; 204 goto errout; 205 } 206 } 207 *rv = numacls; 208 if (vsecattr.vsa_aclcnt) { 209 kmem_free(vsecattr.vsa_aclentp, 210 vsecattr.vsa_aclcnt * sizeof (aclent_t)); 211 } 212 if (vsecattr.vsa_dfaclcnt) { 213 kmem_free(vsecattr.vsa_dfaclentp, 214 vsecattr.vsa_dfaclcnt * sizeof (aclent_t)); 215 } 216 break; 217 218 case ACE_GETACL: 219 if (nentries < 3) 220 return (EINVAL); 221 222 if (aclbufp == NULL) 223 return (EFAULT); 224 225 vsecattr.vsa_mask = VSA_ACE | VSA_ACECNT; 226 if (error = VOP_GETSECATTR(vp, &vsecattr, 0, CRED())) 227 return (error); 228 229 aclbsize = vsecattr.vsa_aclcnt * sizeof (ace_t); 230 if (vsecattr.vsa_aclcnt > nentries) { 231 error = ENOSPC; 232 goto errout; 233 } 234 235 if (aclbsize > 0) { 236 if ((error = copyout(vsecattr.vsa_aclentp, 237 aclbufp, aclbsize)) != 0) { 238 goto errout; 239 } 240 } 241 242 *rv = vsecattr.vsa_aclcnt; 243 if (vsecattr.vsa_aclcnt) { 244 kmem_free(vsecattr.vsa_aclentp, 245 vsecattr.vsa_aclcnt * sizeof (ace_t)); 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())) { 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 < 3 || nentries > (MAX_ACL_ENTRIES * 2)) 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 uaddrp = (caddr_t)aclbufp; 332 if (copyin(uaddrp, vsecattr.vsa_aclentp, aclbsize)) { 333 kmem_free(aaclp, aclbsize); 334 return (EFAULT); 335 } 336 (void) VOP_RWLOCK(vp, V_WRITELOCK_TRUE, NULL); 337 if (error = VOP_SETSECATTR(vp, &vsecattr, 0, CRED())) { 338 kmem_free(aaclp, aclbsize); 339 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 340 return (error); 341 } 342 *rv = 0; 343 kmem_free(aaclp, aclbsize); 344 VOP_RWUNLOCK(vp, V_WRITELOCK_TRUE, NULL); 345 break; 346 347 default: 348 return (EINVAL); 349 } 350 351 return (0); 352 353 errout: 354 if (aclbsize && vsecattr.vsa_aclentp) 355 kmem_free(vsecattr.vsa_aclentp, aclbsize); 356 if (dfaclbsize && vsecattr.vsa_dfaclentp) 357 kmem_free(vsecattr.vsa_dfaclentp, dfaclbsize); 358 return (error); 359 } 360 361 362 /* 363 * Generic shellsort, from K&R (1st ed, p 58.), somewhat modified. 364 * v = Ptr to array/vector of objs 365 * n = # objs in the array 366 * s = size of each obj (must be multiples of a word size) 367 * f = ptr to function to compare two objs 368 * returns (-1 = less than, 0 = equal, 1 = greater than 369 */ 370 void 371 ksort(caddr_t v, int n, int s, int (*f)()) 372 { 373 int g, i, j, ii; 374 unsigned int *p1, *p2; 375 unsigned int tmp; 376 377 /* No work to do */ 378 if (v == NULL || n <= 1) 379 return; 380 381 /* Sanity check on arguments */ 382 ASSERT(((uintptr_t)v & 0x3) == 0 && (s & 0x3) == 0); 383 ASSERT(s > 0); 384 for (g = n / 2; g > 0; g /= 2) { 385 for (i = g; i < n; i++) { 386 for (j = i - g; j >= 0 && 387 (*f)(v + j * s, v + (j + g) * s) == 1; 388 j -= g) { 389 p1 = (unsigned *)(v + j * s); 390 p2 = (unsigned *)(v + (j + g) * s); 391 for (ii = 0; ii < s / 4; ii++) { 392 tmp = *p1; 393 *p1++ = *p2; 394 *p2++ = tmp; 395 } 396 } 397 } 398 } 399 } 400 401 /* 402 * Compare two acls, all fields. Returns: 403 * -1 (less than) 404 * 0 (equal) 405 * +1 (greater than) 406 */ 407 int 408 cmp2acls(void *a, void *b) 409 { 410 aclent_t *x = (aclent_t *)a; 411 aclent_t *y = (aclent_t *)b; 412 413 /* Compare types */ 414 if (x->a_type < y->a_type) 415 return (-1); 416 if (x->a_type > y->a_type) 417 return (1); 418 /* Equal types; compare id's */ 419 if (x->a_id < y->a_id) 420 return (-1); 421 if (x->a_id > y->a_id) 422 return (1); 423 /* Equal ids; compare perms */ 424 if (x->a_perm < y->a_perm) 425 return (-1); 426 if (x->a_perm > y->a_perm) 427 return (1); 428 /* Totally equal */ 429 return (0); 430 } 431