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/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/mount.h> 46 #include <sys/vnode.h> 47 #include <sys/lock.h> 48 #include <sys/mutex.h> 49 #include <sys/namei.h> 50 #include <sys/file.h> 51 #include <sys/filedesc.h> 52 #include <sys/proc.h> 53 #include <sys/sysent.h> 54 #include <sys/acl.h> 55 56 #include <security/mac/mac_framework.h> 57 58 #include <vm/uma.h> 59 60 uma_zone_t acl_zone; 61 static int vacl_set_acl(struct thread *td, struct vnode *vp, 62 acl_type_t type, struct acl *aclp); 63 static int vacl_get_acl(struct thread *td, struct vnode *vp, 64 acl_type_t type, struct acl *aclp); 65 static int vacl_aclcheck(struct thread *td, struct vnode *vp, 66 acl_type_t type, struct acl *aclp); 67 68 /* 69 * These calls wrap the real vnode operations, and are called by the syscall 70 * code once the syscall has converted the path or file descriptor to a vnode 71 * (unlocked). The aclp pointer is assumed still to point to userland, so 72 * this should not be consumed within the kernel except by syscall code. 73 * Other code should directly invoke VOP_{SET,GET}ACL. 74 */ 75 76 /* 77 * Given a vnode, set its ACL. 78 */ 79 static int 80 vacl_set_acl(struct thread *td, struct vnode *vp, acl_type_t type, 81 struct acl *aclp) 82 { 83 struct acl inkernacl; 84 struct mount *mp; 85 int error; 86 87 error = copyin(aclp, &inkernacl, sizeof(struct acl)); 88 if (error) 89 return(error); 90 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 91 if (error != 0) 92 return (error); 93 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 94 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 95 #ifdef MAC 96 error = mac_check_vnode_setacl(td->td_ucred, vp, type, &inkernacl); 97 if (error != 0) 98 goto out; 99 #endif 100 error = VOP_SETACL(vp, type, &inkernacl, td->td_ucred, td); 101 #ifdef MAC 102 out: 103 #endif 104 VOP_UNLOCK(vp, 0, td); 105 vn_finished_write(mp); 106 return(error); 107 } 108 109 /* 110 * Given a vnode, get its ACL. 111 */ 112 static int 113 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 114 struct acl *aclp) 115 { 116 struct acl inkernelacl; 117 int error; 118 119 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 120 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 121 #ifdef MAC 122 error = mac_check_vnode_getacl(td->td_ucred, vp, type); 123 if (error != 0) 124 goto out; 125 #endif 126 error = VOP_GETACL(vp, type, &inkernelacl, td->td_ucred, td); 127 #ifdef MAC 128 out: 129 #endif 130 VOP_UNLOCK(vp, 0, td); 131 if (error == 0) 132 error = copyout(&inkernelacl, aclp, sizeof(struct acl)); 133 return (error); 134 } 135 136 /* 137 * Given a vnode, delete its ACL. 138 */ 139 static int 140 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 141 { 142 struct mount *mp; 143 int error; 144 145 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 146 if (error) 147 return (error); 148 VOP_LEASE(vp, td, td->td_ucred, LEASE_WRITE); 149 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td); 150 #ifdef MAC 151 error = mac_check_vnode_deleteacl(td->td_ucred, vp, type); 152 if (error) 153 goto out; 154 #endif 155 error = VOP_SETACL(vp, type, 0, td->td_ucred, td); 156 #ifdef MAC 157 out: 158 #endif 159 VOP_UNLOCK(vp, 0, td); 160 vn_finished_write(mp); 161 return (error); 162 } 163 164 /* 165 * Given a vnode, check whether an ACL is appropriate for it 166 */ 167 static int 168 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 169 struct acl *aclp) 170 { 171 struct acl inkernelacl; 172 int error; 173 174 error = copyin(aclp, &inkernelacl, sizeof(struct acl)); 175 if (error) 176 return(error); 177 error = VOP_ACLCHECK(vp, type, &inkernelacl, td->td_ucred, td); 178 return (error); 179 } 180 181 /* 182 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't 183 * need to lock, as the vacl_ code will get/release any locks required. 184 */ 185 186 /* 187 * Given a file path, get an ACL for it 188 */ 189 int 190 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 191 { 192 struct nameidata nd; 193 int vfslocked, error; 194 195 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 196 error = namei(&nd); 197 vfslocked = NDHASGIANT(&nd); 198 if (error == 0) { 199 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 200 NDFREE(&nd, 0); 201 } 202 VFS_UNLOCK_GIANT(vfslocked); 203 return (error); 204 } 205 206 /* 207 * Given a file path, get an ACL for it; don't follow links. 208 */ 209 int 210 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 211 { 212 struct nameidata nd; 213 int vfslocked, error; 214 215 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 216 error = namei(&nd); 217 vfslocked = NDHASGIANT(&nd); 218 if (error == 0) { 219 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 220 NDFREE(&nd, 0); 221 } 222 VFS_UNLOCK_GIANT(vfslocked); 223 return (error); 224 } 225 226 /* 227 * Given a file path, set an ACL for it. 228 */ 229 int 230 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 231 { 232 struct nameidata nd; 233 int vfslocked, error; 234 235 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 236 error = namei(&nd); 237 vfslocked = NDHASGIANT(&nd); 238 if (error == 0) { 239 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 240 NDFREE(&nd, 0); 241 } 242 VFS_UNLOCK_GIANT(vfslocked); 243 return (error); 244 } 245 246 /* 247 * Given a file path, set an ACL for it; don't follow links. 248 */ 249 int 250 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 251 { 252 struct nameidata nd; 253 int vfslocked, error; 254 255 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 256 error = namei(&nd); 257 vfslocked = NDHASGIANT(&nd); 258 if (error == 0) { 259 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 260 NDFREE(&nd, 0); 261 } 262 VFS_UNLOCK_GIANT(vfslocked); 263 return (error); 264 } 265 266 /* 267 * Given a file descriptor, get an ACL for it. 268 */ 269 int 270 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 271 { 272 struct file *fp; 273 int vfslocked, error; 274 275 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 276 if (error == 0) { 277 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 278 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 279 fdrop(fp, td); 280 VFS_UNLOCK_GIANT(vfslocked); 281 } 282 return (error); 283 } 284 285 /* 286 * Given a file descriptor, set an ACL for it. 287 */ 288 int 289 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 290 { 291 struct file *fp; 292 int vfslocked, error; 293 294 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 295 if (error == 0) { 296 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 297 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 298 fdrop(fp, td); 299 VFS_UNLOCK_GIANT(vfslocked); 300 } 301 return (error); 302 } 303 304 /* 305 * Given a file path, delete an ACL from it. 306 */ 307 int 308 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 309 { 310 struct nameidata nd; 311 int vfslocked, error; 312 313 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 314 error = namei(&nd); 315 vfslocked = NDHASGIANT(&nd); 316 if (error == 0) { 317 error = vacl_delete(td, nd.ni_vp, uap->type); 318 NDFREE(&nd, 0); 319 } 320 VFS_UNLOCK_GIANT(vfslocked); 321 return (error); 322 } 323 324 /* 325 * Given a file path, delete an ACL from it; don't follow links. 326 */ 327 int 328 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 329 { 330 struct nameidata nd; 331 int vfslocked, error; 332 333 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 334 error = namei(&nd); 335 vfslocked = NDHASGIANT(&nd); 336 if (error == 0) { 337 error = vacl_delete(td, nd.ni_vp, uap->type); 338 NDFREE(&nd, 0); 339 } 340 VFS_UNLOCK_GIANT(vfslocked); 341 return (error); 342 } 343 344 /* 345 * Given a file path, delete an ACL from it. 346 */ 347 int 348 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 349 { 350 struct file *fp; 351 int vfslocked, error; 352 353 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 354 if (error == 0) { 355 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 356 error = vacl_delete(td, fp->f_vnode, uap->type); 357 fdrop(fp, td); 358 VFS_UNLOCK_GIANT(vfslocked); 359 } 360 return (error); 361 } 362 363 /* 364 * Given a file path, check an ACL for it. 365 */ 366 int 367 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 368 { 369 struct nameidata nd; 370 int vfslocked, error; 371 372 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 373 error = namei(&nd); 374 vfslocked = NDHASGIANT(&nd); 375 if (error == 0) { 376 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 377 NDFREE(&nd, 0); 378 } 379 VFS_UNLOCK_GIANT(vfslocked); 380 return (error); 381 } 382 383 /* 384 * Given a file path, check an ACL for it; don't follow links. 385 */ 386 int 387 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 388 { 389 struct nameidata nd; 390 int vfslocked, error; 391 392 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 393 error = namei(&nd); 394 vfslocked = NDHASGIANT(&nd); 395 if (error == 0) { 396 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 397 NDFREE(&nd, 0); 398 } 399 VFS_UNLOCK_GIANT(vfslocked); 400 return (error); 401 } 402 403 /* 404 * Given a file descriptor, check an ACL for it. 405 */ 406 int 407 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 408 { 409 struct file *fp; 410 int vfslocked, error; 411 412 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 413 if (error == 0) { 414 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 415 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 416 fdrop(fp, td); 417 VFS_UNLOCK_GIANT(vfslocked); 418 } 419 return (error); 420 } 421 422 /* ARGUSED */ 423 424 static void 425 aclinit(void *dummy __unused) 426 { 427 428 acl_zone = uma_zcreate("ACL UMA zone", sizeof(struct acl), 429 NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, 0); 430 } 431 SYSINIT(acls, SI_SUB_ACL, SI_ORDER_FIRST, aclinit, NULL) 432