1 /*- 2 * Copyright (c) 1999-2006 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * This software was developed by Robert Watson for the TrustedBSD Project. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 /* 29 * Developed by the TrustedBSD Project. 30 * 31 * ACL system calls and other functions common across different ACL types. 32 * Type-specific routines go into subr_acl_<type>.c. 33 */ 34 35 #include <sys/cdefs.h> 36 __FBSDID("$FreeBSD$"); 37 38 #include "opt_mac.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/sysproto.h> 43 #include <sys/fcntl.h> 44 #include <sys/kernel.h> 45 #include <sys/malloc.h> 46 #include <sys/mount.h> 47 #include <sys/vnode.h> 48 #include <sys/lock.h> 49 #include <sys/mutex.h> 50 #include <sys/namei.h> 51 #include <sys/file.h> 52 #include <sys/filedesc.h> 53 #include <sys/proc.h> 54 #include <sys/sysent.h> 55 #include <sys/acl.h> 56 57 #include <security/mac/mac_framework.h> 58 59 #include <vm/uma.h> 60 61 uma_zone_t acl_zone; 62 static int vacl_set_acl(struct thread *td, struct vnode *vp, 63 acl_type_t type, struct acl *aclp); 64 static int vacl_get_acl(struct thread *td, struct vnode *vp, 65 acl_type_t type, struct acl *aclp); 66 static int vacl_aclcheck(struct thread *td, struct vnode *vp, 67 acl_type_t type, struct acl *aclp); 68 69 /* 70 * These calls wrap the real vnode operations, and are called by the syscall 71 * code once the syscall has converted the path or file descriptor to a vnode 72 * (unlocked). The aclp pointer is assumed still to point to userland, so 73 * this should not be consumed within the kernel except by syscall code. 74 * Other code should directly invoke VOP_{SET,GET}ACL. 75 */ 76 77 /* 78 * Given a vnode, set its ACL. 79 */ 80 static int 81 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 82 struct acl *aclp) 83 { 84 struct acl inkernacl; 85 struct mount *mp; 86 int error; 87 88 error = copyin(aclp, &inkernacl, sizeof(struct acl)); 89 if (error) 90 return(error); 91 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 92 if (error != 0) 93 return (error); 94 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 95 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 96 #ifdef MAC 97 error = mac_vnode_check_setacl(td->td_ucred, vp, type, &inkernacl); 98 if (error != 0) 99 goto out; 100 #endif 101 error = VOP_SETACL(vp, type, &inkernacl, td->td_ucred, td); 102 #ifdef MAC 103 out: 104 #endif 105 VOP_UNLOCK(vp, 0); 106 vn_finished_write(mp); 107 return(error); 108 } 109 110 /* 111 * Given a vnode, get its ACL. 112 */ 113 static int 114 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 115 struct acl *aclp) 116 { 117 struct acl inkernelacl; 118 int error; 119 120 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 121 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 122 #ifdef MAC 123 error = mac_vnode_check_getacl(td->td_ucred, vp, type); 124 if (error != 0) 125 goto out; 126 #endif 127 error = VOP_GETACL(vp, type, &inkernelacl, td->td_ucred, td); 128 #ifdef MAC 129 out: 130 #endif 131 VOP_UNLOCK(vp, 0); 132 if (error == 0) 133 error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 134 return (error); 135 } 136 137 /* 138 * Given a vnode, delete its ACL. 139 */ 140 static int 141 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 142 { 143 struct mount *mp; 144 int error; 145 146 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 147 if (error) 148 return (error); 149 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 150 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 151 #ifdef MAC 152 error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); 153 if (error) 154 goto out; 155 #endif 156 error = VOP_SETACL(vp, type, 0, td->td_ucred, td); 157 #ifdef MAC 158 out: 159 #endif 160 VOP_UNLOCK(vp, 0); 161 vn_finished_write(mp); 162 return (error); 163 } 164 165 /* 166 * Given a vnode, check whether an ACL is appropriate for it 167 */ 168 static int 169 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 170 struct acl *aclp) 171 { 172 struct acl inkernelacl; 173 int error; 174 175 error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 176 if (error) 177 return(error); 178 error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_ucred, td); 179 return (error); 180 } 181 182 /* 183 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't 184 * need to lock, as the vacl_ code will get/release any locks required. 185 */ 186 187 /* 188 * Given a file path, get an ACL for it 189 */ 190 int 191 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 192 { 193 struct nameidata nd; 194 int vfslocked, error; 195 196 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 197 error = namei(&nd); 198 vfslocked = NDHASGIANT(&nd); 199 if (error == 0) { 200 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 201 NDFREE(&nd, 0); 202 } 203 VFS_UNLOCK_GIANT(vfslocked); 204 return (error); 205 } 206 207 /* 208 * Given a file path, get an ACL for it; don't follow links. 209 */ 210 int 211 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 212 { 213 struct nameidata nd; 214 int vfslocked, error; 215 216 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 217 error = namei(&nd); 218 vfslocked = NDHASGIANT(&nd); 219 if (error == 0) { 220 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 221 NDFREE(&nd, 0); 222 } 223 VFS_UNLOCK_GIANT(vfslocked); 224 return (error); 225 } 226 227 /* 228 * Given a file path, set an ACL for it. 229 */ 230 int 231 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 232 { 233 struct nameidata nd; 234 int vfslocked, error; 235 236 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 237 error = namei(&nd); 238 vfslocked = NDHASGIANT(&nd); 239 if (error == 0) { 240 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 241 NDFREE(&nd, 0); 242 } 243 VFS_UNLOCK_GIANT(vfslocked); 244 return (error); 245 } 246 247 /* 248 * Given a file path, set an ACL for it; don't follow links. 249 */ 250 int 251 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 252 { 253 struct nameidata nd; 254 int vfslocked, error; 255 256 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 257 error = namei(&nd); 258 vfslocked = NDHASGIANT(&nd); 259 if (error == 0) { 260 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 261 NDFREE(&nd, 0); 262 } 263 VFS_UNLOCK_GIANT(vfslocked); 264 return (error); 265 } 266 267 /* 268 * Given a file descriptor, get an ACL for it. 269 */ 270 int 271 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 272 { 273 struct file *fp; 274 int vfslocked, error; 275 276 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 277 if (error == 0) { 278 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 279 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 280 fdrop(fp, td); 281 VFS_UNLOCK_GIANT(vfslocked); 282 } 283 return (error); 284 } 285 286 /* 287 * Given a file descriptor, set an ACL for it. 288 */ 289 int 290 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 291 { 292 struct file *fp; 293 int vfslocked, error; 294 295 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 296 if (error == 0) { 297 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 298 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 299 fdrop(fp, td); 300 VFS_UNLOCK_GIANT(vfslocked); 301 } 302 return (error); 303 } 304 305 /* 306 * Given a file path, delete an ACL from it. 307 */ 308 int 309 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 310 { 311 struct nameidata nd; 312 int vfslocked, error; 313 314 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 315 error = namei(&nd); 316 vfslocked = NDHASGIANT(&nd); 317 if (error == 0) { 318 error = vacl_delete(td, nd.ni_vp, uap->type); 319 NDFREE(&nd, 0); 320 } 321 VFS_UNLOCK_GIANT(vfslocked); 322 return (error); 323 } 324 325 /* 326 * Given a file path, delete an ACL from it; don't follow links. 327 */ 328 int 329 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 330 { 331 struct nameidata nd; 332 int vfslocked, error; 333 334 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 335 error = namei(&nd); 336 vfslocked = NDHASGIANT(&nd); 337 if (error == 0) { 338 error = vacl_delete(td, nd.ni_vp, uap->type); 339 NDFREE(&nd, 0); 340 } 341 VFS_UNLOCK_GIANT(vfslocked); 342 return (error); 343 } 344 345 /* 346 * Given a file path, delete an ACL from it. 347 */ 348 int 349 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 350 { 351 struct file *fp; 352 int vfslocked, error; 353 354 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 355 if (error == 0) { 356 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 357 error = vacl_delete(td, fp->f_vnode, uap->type); 358 fdrop(fp, td); 359 VFS_UNLOCK_GIANT(vfslocked); 360 } 361 return (error); 362 } 363 364 /* 365 * Given a file path, check an ACL for it. 366 */ 367 int 368 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 369 { 370 struct nameidata nd; 371 int vfslocked, error; 372 373 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 374 error = namei(&nd); 375 vfslocked = NDHASGIANT(&nd); 376 if (error == 0) { 377 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 378 NDFREE(&nd, 0); 379 } 380 VFS_UNLOCK_GIANT(vfslocked); 381 return (error); 382 } 383 384 /* 385 * Given a file path, check an ACL for it; don't follow links. 386 */ 387 int 388 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 389 { 390 struct nameidata nd; 391 int vfslocked, error; 392 393 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 394 error = namei(&nd); 395 vfslocked = NDHASGIANT(&nd); 396 if (error == 0) { 397 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 398 NDFREE(&nd, 0); 399 } 400 VFS_UNLOCK_GIANT(vfslocked); 401 return (error); 402 } 403 404 /* 405 * Given a file descriptor, check an ACL for it. 406 */ 407 int 408 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 409 { 410 struct file *fp; 411 int vfslocked, error; 412 413 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 414 if (error == 0) { 415 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 416 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 417 fdrop(fp, td); 418 VFS_UNLOCK_GIANT(vfslocked); 419 } 420 return (error); 421 } 422 423 /* ARGUSED */ 424 425 static void 426 aclinit(void *dummy __unused) 427 { 428 429 acl_zone = uma_zcreate("ACL UMA zone", sizeof(struct acl), 430 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 431 } 432 SYSINIT(acls, SI_SUB_ACL, SI_ORDER_FIRST, aclinit, NULL); 433