2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 1999-2001 Robert N. M. Watson
7 * This software was developed by Robert Watson for the TrustedBSD Project.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/capsicum.h>
38 #include <sys/mount.h>
39 #include <sys/mutex.h>
40 #include <sys/sysproto.h>
41 #include <sys/fcntl.h>
42 #include <sys/namei.h>
43 #include <sys/filedesc.h>
44 #include <sys/limits.h>
45 #include <sys/vnode.h>
47 #include <sys/extattr.h>
49 #include <security/audit/audit.h>
50 #include <security/mac/mac_framework.h>
53 * Syscall to push extended attribute configuration information into the VFS.
54 * Accepts a path, which it converts to a mountpoint, as well as a command
55 * (int cmd), and attribute name and misc data.
57 * Currently this is used only by UFS1 extended attributes.
60 sys_extattrctl(td, uap)
62 struct extattrctl_args /* {
70 struct vnode *filename_vp;
72 struct mount *mp, *mp_writable;
73 char attrname[EXTATTR_MAXNAMELEN];
76 AUDIT_ARG_CMD(uap->cmd);
77 AUDIT_ARG_VALUE(uap->attrnamespace);
79 * uap->attrname is not always defined. We check again later when we
80 * invoke the VFS call so as to pass in NULL there if needed.
82 if (uap->attrname != NULL) {
83 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN,
88 AUDIT_ARG_TEXT(attrname);
92 if (uap->filename != NULL) {
93 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE2,
94 UIO_USERSPACE, uap->filename, td);
98 filename_vp = nd.ni_vp;
99 NDFREE(&nd, NDF_NO_VP_RELE);
102 /* uap->path is always defined. */
103 NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF | AUDITVNODE1,
104 UIO_USERSPACE, uap->path, td);
108 mp = nd.ni_vp->v_mount;
109 error = vfs_busy(mp, 0);
115 VOP_UNLOCK(nd.ni_vp, 0);
116 error = vn_start_write(nd.ni_vp, &mp_writable, V_WAIT | PCATCH);
117 NDFREE(&nd, NDF_NO_VP_UNLOCK);
120 if (filename_vp != NULL) {
122 * uap->filename is not always defined. If it is,
123 * grab a vnode lock, which VFS_EXTATTRCTL() will
126 error = vn_lock(filename_vp, LK_EXCLUSIVE);
128 vn_finished_write(mp_writable);
133 error = VFS_EXTATTRCTL(mp, uap->cmd, filename_vp, uap->attrnamespace,
134 uap->attrname != NULL ? attrname : NULL);
136 vn_finished_write(mp_writable);
142 * VFS_EXTATTRCTL will have unlocked, but not de-ref'd, filename_vp,
143 * so vrele it if it is defined.
145 if (filename_vp != NULL)
151 * Set a named extended attribute on a file or directory
153 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
154 * kernelspace string pointer "attrname", userspace buffer
155 * pointer "data", buffer length "nbytes", thread "td".
156 * Returns: 0 on success, an error number otherwise
158 * References: vp must be a valid reference for the duration of the call
161 extattr_set_vp(struct vnode *vp, int attrnamespace, const char *attrname,
162 void *data, size_t nbytes, struct thread *td)
170 if (nbytes > IOSIZE_MAX)
173 error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
176 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
178 aiov.iov_base = data;
179 aiov.iov_len = nbytes;
180 auio.uio_iov = &aiov;
183 auio.uio_resid = nbytes;
184 auio.uio_rw = UIO_WRITE;
185 auio.uio_segflg = UIO_USERSPACE;
190 error = mac_vnode_check_setextattr(td->td_ucred, vp, attrnamespace,
196 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, &auio,
198 cnt -= auio.uio_resid;
199 td->td_retval[0] = cnt;
205 vn_finished_write(mp);
210 sys_extattr_set_fd(td, uap)
212 struct extattr_set_fd_args /* {
215 const char *attrname;
221 char attrname[EXTATTR_MAXNAMELEN];
225 AUDIT_ARG_FD(uap->fd);
226 AUDIT_ARG_VALUE(uap->attrnamespace);
227 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
230 AUDIT_ARG_TEXT(attrname);
232 error = getvnode(td, uap->fd,
233 cap_rights_init(&rights, CAP_EXTATTR_SET), &fp);
237 error = extattr_set_vp(fp->f_vnode, uap->attrnamespace,
238 attrname, uap->data, uap->nbytes, td);
245 sys_extattr_set_file(td, uap)
247 struct extattr_set_file_args /* {
250 const char *attrname;
256 char attrname[EXTATTR_MAXNAMELEN];
259 AUDIT_ARG_VALUE(uap->attrnamespace);
260 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
263 AUDIT_ARG_TEXT(attrname);
265 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE,
270 NDFREE(&nd, NDF_ONLY_PNBUF);
272 error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
273 uap->data, uap->nbytes, td);
280 sys_extattr_set_link(td, uap)
282 struct extattr_set_link_args /* {
285 const char *attrname;
291 char attrname[EXTATTR_MAXNAMELEN];
294 AUDIT_ARG_VALUE(uap->attrnamespace);
295 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
298 AUDIT_ARG_TEXT(attrname);
300 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE,
305 NDFREE(&nd, NDF_ONLY_PNBUF);
307 error = extattr_set_vp(nd.ni_vp, uap->attrnamespace, attrname,
308 uap->data, uap->nbytes, td);
315 * Get a named extended attribute on a file or directory
317 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
318 * kernelspace string pointer "attrname", userspace buffer
319 * pointer "data", buffer length "nbytes", thread "td".
320 * Returns: 0 on success, an error number otherwise
322 * References: vp must be a valid reference for the duration of the call
325 extattr_get_vp(struct vnode *vp, int attrnamespace, const char *attrname,
326 void *data, size_t nbytes, struct thread *td)
328 struct uio auio, *auiop;
334 if (nbytes > IOSIZE_MAX)
337 vn_lock(vp, LK_SHARED | LK_RETRY);
340 * Slightly unusual semantics: if the user provides a NULL data
341 * pointer, they don't want to receive the data, just the maximum
348 aiov.iov_base = data;
349 aiov.iov_len = nbytes;
350 auio.uio_iov = &aiov;
353 auio.uio_resid = nbytes;
354 auio.uio_rw = UIO_READ;
355 auio.uio_segflg = UIO_USERSPACE;
363 error = mac_vnode_check_getextattr(td->td_ucred, vp, attrnamespace,
369 error = VOP_GETEXTATTR(vp, attrnamespace, attrname, auiop, sizep,
373 cnt -= auio.uio_resid;
374 td->td_retval[0] = cnt;
376 td->td_retval[0] = size;
385 sys_extattr_get_fd(td, uap)
387 struct extattr_get_fd_args /* {
390 const char *attrname;
396 char attrname[EXTATTR_MAXNAMELEN];
400 AUDIT_ARG_FD(uap->fd);
401 AUDIT_ARG_VALUE(uap->attrnamespace);
402 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
405 AUDIT_ARG_TEXT(attrname);
407 error = getvnode(td, uap->fd,
408 cap_rights_init(&rights, CAP_EXTATTR_GET), &fp);
412 error = extattr_get_vp(fp->f_vnode, uap->attrnamespace,
413 attrname, uap->data, uap->nbytes, td);
420 sys_extattr_get_file(td, uap)
422 struct extattr_get_file_args /* {
425 const char *attrname;
431 char attrname[EXTATTR_MAXNAMELEN];
434 AUDIT_ARG_VALUE(uap->attrnamespace);
435 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
438 AUDIT_ARG_TEXT(attrname);
440 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
444 NDFREE(&nd, NDF_ONLY_PNBUF);
446 error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
447 uap->data, uap->nbytes, td);
454 sys_extattr_get_link(td, uap)
456 struct extattr_get_link_args /* {
459 const char *attrname;
465 char attrname[EXTATTR_MAXNAMELEN];
468 AUDIT_ARG_VALUE(uap->attrnamespace);
469 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
472 AUDIT_ARG_TEXT(attrname);
474 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path,
479 NDFREE(&nd, NDF_ONLY_PNBUF);
481 error = extattr_get_vp(nd.ni_vp, uap->attrnamespace, attrname,
482 uap->data, uap->nbytes, td);
489 * extattr_delete_vp(): Delete a named extended attribute on a file or
492 * Arguments: unlocked vnode "vp", attribute namespace "attrnamespace",
493 * kernelspace string pointer "attrname", proc "p"
494 * Returns: 0 on success, an error number otherwise
496 * References: vp must be a valid reference for the duration of the call
499 extattr_delete_vp(struct vnode *vp, int attrnamespace, const char *attrname,
505 error = vn_start_write(vp, &mp, V_WAIT | PCATCH);
508 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
511 error = mac_vnode_check_deleteextattr(td->td_ucred, vp, attrnamespace,
517 error = VOP_DELETEEXTATTR(vp, attrnamespace, attrname, td->td_ucred,
519 if (error == EOPNOTSUPP)
520 error = VOP_SETEXTATTR(vp, attrnamespace, attrname, NULL,
526 vn_finished_write(mp);
531 sys_extattr_delete_fd(td, uap)
533 struct extattr_delete_fd_args /* {
536 const char *attrname;
540 char attrname[EXTATTR_MAXNAMELEN];
544 AUDIT_ARG_FD(uap->fd);
545 AUDIT_ARG_VALUE(uap->attrnamespace);
546 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
549 AUDIT_ARG_TEXT(attrname);
551 error = getvnode(td, uap->fd,
552 cap_rights_init(&rights, CAP_EXTATTR_DELETE), &fp);
556 error = extattr_delete_vp(fp->f_vnode, uap->attrnamespace,
563 sys_extattr_delete_file(td, uap)
565 struct extattr_delete_file_args /* {
568 const char *attrname;
572 char attrname[EXTATTR_MAXNAMELEN];
575 AUDIT_ARG_VALUE(uap->attrnamespace);
576 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
579 AUDIT_ARG_TEXT(attrname);
581 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
585 NDFREE(&nd, NDF_ONLY_PNBUF);
587 error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td);
593 sys_extattr_delete_link(td, uap)
595 struct extattr_delete_link_args /* {
598 const char *attrname;
602 char attrname[EXTATTR_MAXNAMELEN];
605 AUDIT_ARG_VALUE(uap->attrnamespace);
606 error = copyinstr(uap->attrname, attrname, EXTATTR_MAXNAMELEN, NULL);
609 AUDIT_ARG_TEXT(attrname);
611 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
615 NDFREE(&nd, NDF_ONLY_PNBUF);
617 error = extattr_delete_vp(nd.ni_vp, uap->attrnamespace, attrname, td);
623 * Retrieve a list of extended attributes on a file or directory.
625 * Arguments: unlocked vnode "vp", attribute namespace 'attrnamespace",
626 * userspace buffer pointer "data", buffer length "nbytes",
628 * Returns: 0 on success, an error number otherwise
630 * References: vp must be a valid reference for the duration of the call
633 extattr_list_vp(struct vnode *vp, int attrnamespace, void *data,
634 size_t nbytes, struct thread *td)
636 struct uio auio, *auiop;
642 if (nbytes > IOSIZE_MAX)
645 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
651 aiov.iov_base = data;
652 aiov.iov_len = nbytes;
653 auio.uio_iov = &aiov;
656 auio.uio_resid = nbytes;
657 auio.uio_rw = UIO_READ;
658 auio.uio_segflg = UIO_USERSPACE;
666 error = mac_vnode_check_listextattr(td->td_ucred, vp, attrnamespace);
671 error = VOP_LISTEXTATTR(vp, attrnamespace, auiop, sizep,
675 cnt -= auio.uio_resid;
676 td->td_retval[0] = cnt;
678 td->td_retval[0] = size;
688 sys_extattr_list_fd(td, uap)
690 struct extattr_list_fd_args /* {
701 AUDIT_ARG_FD(uap->fd);
702 AUDIT_ARG_VALUE(uap->attrnamespace);
703 error = getvnode(td, uap->fd,
704 cap_rights_init(&rights, CAP_EXTATTR_LIST), &fp);
708 error = extattr_list_vp(fp->f_vnode, uap->attrnamespace, uap->data,
716 sys_extattr_list_file(td, uap)
718 struct extattr_list_file_args /* {
728 AUDIT_ARG_VALUE(uap->attrnamespace);
729 NDINIT(&nd, LOOKUP, FOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path, td);
733 NDFREE(&nd, NDF_ONLY_PNBUF);
735 error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data,
743 sys_extattr_list_link(td, uap)
745 struct extattr_list_link_args /* {
755 AUDIT_ARG_VALUE(uap->attrnamespace);
756 NDINIT(&nd, LOOKUP, NOFOLLOW | AUDITVNODE1, UIO_USERSPACE, uap->path,
761 NDFREE(&nd, NDF_ONLY_PNBUF);
763 error = extattr_list_vp(nd.ni_vp, uap->attrnamespace, uap->data,