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 static MALLOC_DEFINE(M_ACL, "acl", "Access Control Lists"); 60 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 *inkernelacl; 84 struct mount *mp; 85 int error; 86 87 inkernelacl = acl_alloc(M_WAITOK); 88 error = copyin(aclp, inkernelacl, sizeof(struct acl)); 89 if (error) 90 goto out; 91 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 92 if (error != 0) 93 goto out; 94 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 95 #ifdef MAC 96 error = mac_vnode_check_setacl(td->td_ucred, vp, type, inkernelacl); 97 if (error != 0) 98 goto out_unlock; 99 #endif 100 error = VOP_SETACL(vp, type, inkernelacl, td->td_ucred, td); 101 #ifdef MAC 102 out_unlock: 103 #endif 104 VOP_UNLOCK(vp, 0); 105 vn_finished_write(mp); 106 out: 107 acl_free(inkernelacl); 108 return(error); 109 } 110 111 /* 112 * Given a vnode, get its ACL. 113 */ 114 static int 115 vacl_get_acl(struct thread *td, struct vnode *vp, acl_type_t type, 116 struct acl *aclp) 117 { 118 struct acl *inkernelacl; 119 int error; 120 121 inkernelacl = acl_alloc(M_WAITOK); 122 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 123 #ifdef MAC 124 error = mac_vnode_check_getacl(td->td_ucred, vp, type); 125 if (error != 0) 126 goto out; 127 #endif 128 error = VOP_GETACL(vp, type, inkernelacl, td->td_ucred, td); 129 #ifdef MAC 130 out: 131 #endif 132 VOP_UNLOCK(vp, 0); 133 if (error == 0) 134 error = copyout(inkernelacl, aclp, sizeof(struct acl)); 135 acl_free(inkernelacl); 136 return (error); 137 } 138 139 /* 140 * Given a vnode, delete its ACL. 141 */ 142 static int 143 vacl_delete(struct thread *td, struct vnode *vp, acl_type_t type) 144 { 145 struct mount *mp; 146 int error; 147 148 error = vn_start_write(vp, &mp, V_WAIT | PCATCH); 149 if (error) 150 return (error); 151 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 152 #ifdef MAC 153 error = mac_vnode_check_deleteacl(td->td_ucred, vp, type); 154 if (error) 155 goto out; 156 #endif 157 error = VOP_SETACL(vp, type, 0, td->td_ucred, td); 158 #ifdef MAC 159 out: 160 #endif 161 VOP_UNLOCK(vp, 0); 162 vn_finished_write(mp); 163 return (error); 164 } 165 166 /* 167 * Given a vnode, check whether an ACL is appropriate for it 168 */ 169 static int 170 vacl_aclcheck(struct thread *td, struct vnode *vp, acl_type_t type, 171 struct acl *aclp) 172 { 173 struct acl *inkernelacl; 174 int error; 175 176 inkernelacl = acl_alloc(M_WAITOK); 177 error = copyin(aclp, inkernelacl, sizeof(struct acl)); 178 if (error) 179 goto out; 180 error = VOP_ACLCHECK(vp, type, inkernelacl, td->td_ucred, td); 181 out: 182 acl_free(inkernelacl); 183 return (error); 184 } 185 186 /* 187 * syscalls -- convert the path/fd to a vnode, and call vacl_whatever. Don't 188 * need to lock, as the vacl_ code will get/release any locks required. 189 */ 190 191 /* 192 * Given a file path, get an ACL for it 193 */ 194 int 195 __acl_get_file(struct thread *td, struct __acl_get_file_args *uap) 196 { 197 struct nameidata nd; 198 int vfslocked, error; 199 200 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 201 error = namei(&nd); 202 vfslocked = NDHASGIANT(&nd); 203 if (error == 0) { 204 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 205 NDFREE(&nd, 0); 206 } 207 VFS_UNLOCK_GIANT(vfslocked); 208 return (error); 209 } 210 211 /* 212 * Given a file path, get an ACL for it; don't follow links. 213 */ 214 int 215 __acl_get_link(struct thread *td, struct __acl_get_link_args *uap) 216 { 217 struct nameidata nd; 218 int vfslocked, error; 219 220 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 221 error = namei(&nd); 222 vfslocked = NDHASGIANT(&nd); 223 if (error == 0) { 224 error = vacl_get_acl(td, nd.ni_vp, uap->type, uap->aclp); 225 NDFREE(&nd, 0); 226 } 227 VFS_UNLOCK_GIANT(vfslocked); 228 return (error); 229 } 230 231 /* 232 * Given a file path, set an ACL for it. 233 */ 234 int 235 __acl_set_file(struct thread *td, struct __acl_set_file_args *uap) 236 { 237 struct nameidata nd; 238 int vfslocked, error; 239 240 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 241 error = namei(&nd); 242 vfslocked = NDHASGIANT(&nd); 243 if (error == 0) { 244 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 245 NDFREE(&nd, 0); 246 } 247 VFS_UNLOCK_GIANT(vfslocked); 248 return (error); 249 } 250 251 /* 252 * Given a file path, set an ACL for it; don't follow links. 253 */ 254 int 255 __acl_set_link(struct thread *td, struct __acl_set_link_args *uap) 256 { 257 struct nameidata nd; 258 int vfslocked, error; 259 260 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 261 error = namei(&nd); 262 vfslocked = NDHASGIANT(&nd); 263 if (error == 0) { 264 error = vacl_set_acl(td, nd.ni_vp, uap->type, uap->aclp); 265 NDFREE(&nd, 0); 266 } 267 VFS_UNLOCK_GIANT(vfslocked); 268 return (error); 269 } 270 271 /* 272 * Given a file descriptor, get an ACL for it. 273 */ 274 int 275 __acl_get_fd(struct thread *td, struct __acl_get_fd_args *uap) 276 { 277 struct file *fp; 278 int vfslocked, error; 279 280 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 281 if (error == 0) { 282 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 283 error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp); 284 fdrop(fp, td); 285 VFS_UNLOCK_GIANT(vfslocked); 286 } 287 return (error); 288 } 289 290 /* 291 * Given a file descriptor, set an ACL for it. 292 */ 293 int 294 __acl_set_fd(struct thread *td, struct __acl_set_fd_args *uap) 295 { 296 struct file *fp; 297 int vfslocked, error; 298 299 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 300 if (error == 0) { 301 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 302 error = vacl_set_acl(td, fp->f_vnode, uap->type, uap->aclp); 303 fdrop(fp, td); 304 VFS_UNLOCK_GIANT(vfslocked); 305 } 306 return (error); 307 } 308 309 /* 310 * Given a file path, delete an ACL from it. 311 */ 312 int 313 __acl_delete_file(struct thread *td, struct __acl_delete_file_args *uap) 314 { 315 struct nameidata nd; 316 int vfslocked, error; 317 318 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 319 error = namei(&nd); 320 vfslocked = NDHASGIANT(&nd); 321 if (error == 0) { 322 error = vacl_delete(td, nd.ni_vp, uap->type); 323 NDFREE(&nd, 0); 324 } 325 VFS_UNLOCK_GIANT(vfslocked); 326 return (error); 327 } 328 329 /* 330 * Given a file path, delete an ACL from it; don't follow links. 331 */ 332 int 333 __acl_delete_link(struct thread *td, struct __acl_delete_link_args *uap) 334 { 335 struct nameidata nd; 336 int vfslocked, error; 337 338 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 339 error = namei(&nd); 340 vfslocked = NDHASGIANT(&nd); 341 if (error == 0) { 342 error = vacl_delete(td, nd.ni_vp, uap->type); 343 NDFREE(&nd, 0); 344 } 345 VFS_UNLOCK_GIANT(vfslocked); 346 return (error); 347 } 348 349 /* 350 * Given a file path, delete an ACL from it. 351 */ 352 int 353 __acl_delete_fd(struct thread *td, struct __acl_delete_fd_args *uap) 354 { 355 struct file *fp; 356 int vfslocked, error; 357 358 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 359 if (error == 0) { 360 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 361 error = vacl_delete(td, fp->f_vnode, uap->type); 362 fdrop(fp, td); 363 VFS_UNLOCK_GIANT(vfslocked); 364 } 365 return (error); 366 } 367 368 /* 369 * Given a file path, check an ACL for it. 370 */ 371 int 372 __acl_aclcheck_file(struct thread *td, struct __acl_aclcheck_file_args *uap) 373 { 374 struct nameidata nd; 375 int vfslocked, error; 376 377 NDINIT(&nd, LOOKUP, MPSAFE|FOLLOW, UIO_USERSPACE, uap->path, td); 378 error = namei(&nd); 379 vfslocked = NDHASGIANT(&nd); 380 if (error == 0) { 381 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 382 NDFREE(&nd, 0); 383 } 384 VFS_UNLOCK_GIANT(vfslocked); 385 return (error); 386 } 387 388 /* 389 * Given a file path, check an ACL for it; don't follow links. 390 */ 391 int 392 __acl_aclcheck_link(struct thread *td, struct __acl_aclcheck_link_args *uap) 393 { 394 struct nameidata nd; 395 int vfslocked, error; 396 397 NDINIT(&nd, LOOKUP, MPSAFE|NOFOLLOW, UIO_USERSPACE, uap->path, td); 398 error = namei(&nd); 399 vfslocked = NDHASGIANT(&nd); 400 if (error == 0) { 401 error = vacl_aclcheck(td, nd.ni_vp, uap->type, uap->aclp); 402 NDFREE(&nd, 0); 403 } 404 VFS_UNLOCK_GIANT(vfslocked); 405 return (error); 406 } 407 408 /* 409 * Given a file descriptor, check an ACL for it. 410 */ 411 int 412 __acl_aclcheck_fd(struct thread *td, struct __acl_aclcheck_fd_args *uap) 413 { 414 struct file *fp; 415 int vfslocked, error; 416 417 error = getvnode(td->td_proc->p_fd, uap->filedes, &fp); 418 if (error == 0) { 419 vfslocked = VFS_LOCK_GIANT(fp->f_vnode->v_mount); 420 error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp); 421 fdrop(fp, td); 422 VFS_UNLOCK_GIANT(vfslocked); 423 } 424 return (error); 425 } 426 427 struct acl * 428 acl_alloc(int flags) 429 { 430 struct acl *aclp; 431 432 aclp = malloc(sizeof(*aclp), M_ACL, flags); 433 434 return (aclp); 435 } 436 437 void 438 acl_free(struct acl *aclp) 439 { 440 441 free(aclp, M_ACL); 442 } 443