Index: head/share/man/man4/rights.4 =================================================================== --- head/share/man/man4/rights.4 (revision 333119) +++ head/share/man/man4/rights.4 (revision 333120) @@ -1,713 +1,727 @@ .\" .\" Copyright (c) 2008-2010 Robert N. M. Watson .\" Copyright (c) 2012-2013 The FreeBSD Foundation .\" All rights reserved. .\" .\" This software was developed at the University of Cambridge Computer .\" Laboratory with support from a grant from Google, Inc. .\" .\" Portions of this documentation were written by Pawel Jakub Dawidek .\" under sponsorship from the FreeBSD Foundation. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" $FreeBSD$ .\" -.Dd August 17, 2016 +.Dd April 30, 2018 .Dt RIGHTS 4 .Os .Sh NAME .Nm Capability rights .Nd Capsicum capability rights for file descriptors .Sh DESCRIPTION When a file descriptor is created by a function such as .Xr accept 2 , .Xr accept4 2 , .Xr fhopen 2 , .Xr kqueue 2 , .Xr mq_open 2 , .Xr open 2 , .Xr openat 2 , .Xr pdfork 2 , .Xr pipe 2 , .Xr shm_open 2 , .Xr socket 2 or .Xr socketpair 2 , it is assigned all capability rights. Those rights can be reduced (but never expanded) by using the .Xr cap_rights_limit 2 , .Xr cap_fcntls_limit 2 and .Xr cap_ioctls_limit 2 system calls. Once capability rights are reduced, operations on the file descriptor will be limited to those permitted by rights. .Pp The complete list of capability rights is provided below. The .Vt cap_rights_t type is used to store list of capability rights. The .Xr cap_rights_init 3 family of functions should be used to manage the structure. .Sh RIGHTS The following rights may be specified in a rights mask: .Bl -tag -width CAP_RENAMEAT_SOURCE .It Dv CAP_ACCEPT Permit .Xr accept 2 and .Xr accept4 2 . .It Dv CAP_ACL_CHECK Permit .Xr acl_valid_fd_np 3 . .It Dv CAP_ACL_DELETE Permit .Xr acl_delete_fd_np 3 . .It Dv CAP_ACL_GET Permit .Xr acl_get_fd 3 and .Xr acl_get_fd_np 3 . .It Dv CAP_ACL_SET Permit .Xr acl_set_fd 3 and .Xr acl_set_fd_np 3 . .It Dv CAP_BIND -Permit -.Xr bind 2 . +When not in capabilities mode, permit +.Xr bind 2 +and +.Xr bindat 2 +with special value +.Dv AT_FDCWD +in the +.Fa fd +parameter. Note that sockets can also become bound implicitly as a result of .Xr connect 2 or .Xr send 2 , and that socket options set with .Xr setsockopt 2 may also affect binding behavior. .It Dv CAP_BINDAT Permit .Xr bindat 2 . This right has to be present on the directory descriptor. This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_CHFLAGSAT An alias to .Dv CAP_FCHFLAGS and .Dv CAP_LOOKUP . .It Dv CAP_CONNECT -Permit -.Xr connect 2 ; -also required for +When not in capabilities mode, permit +.Xr connect 2 +and +.Xr connectat 2 +with special value +.Dv AT_FDCWD +in the +.Fa fd +parameter. +This right is also required for .Xr sendto 2 with a non-NULL destination address. .It Dv CAP_CONNECTAT Permit .Xr connectat 2 . This right has to be present on the directory descriptor. This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_CREATE Permit .Xr openat 2 with the .Dv O_CREAT flag. .It Dv CAP_EVENT Permit .Xr select 2 , .Xr poll 2 , and .Xr kevent 2 to be used in monitoring the file descriptor for events. .It Dv CAP_EXTATTR_DELETE Permit .Xr extattr_delete_fd 2 . .It Dv CAP_EXTATTR_GET Permit .Xr extattr_get_fd 2 . .It Dv CAP_EXTATTR_LIST Permit .Xr extattr_list_fd 2 . .It Dv CAP_EXTATTR_SET Permit .Xr extattr_set_fd 2 . .It Dv CAP_FCHDIR Permit .Xr fchdir 2 . .It Dv CAP_FCHFLAGS Permit .Xr fchflags 2 and .Xr chflagsat 2 if the .Dv CAP_LOOKUP right is also present. .It Dv CAP_FCHMOD Permit .Xr fchmod 2 and .Xr fchmodat 2 if the .Dv CAP_LOOKUP right is also present. .It Dv CAP_FCHMODAT An alias to .Dv CAP_FCHMOD and .Dv CAP_LOOKUP . .It Dv CAP_FCHOWN Permit .Xr fchown 2 and .Xr fchownat 2 if the .Dv CAP_LOOKUP right is also present. .It Dv CAP_FCHOWNAT An alias to .Dv CAP_FCHOWN and .Dv CAP_LOOKUP . .It Dv CAP_FCNTL Permit .Xr fcntl 2 . Note that only the .Dv F_GETFL , .Dv F_SETFL , .Dv F_GETOWN and .Dv F_SETOWN commands require this capability right. Also note that the list of permitted commands can be further limited with the .Xr cap_fcntls_limit 2 system call. .It Dv CAP_FEXECVE Permit .Xr fexecve 2 and .Xr openat 2 with the .Dv O_EXEC flag; .Dv CAP_READ is also required. .It Dv CAP_FLOCK Permit .Xr flock 2 , .Xr fcntl 2 (with .Dv F_GETLK , .Dv F_SETLK , .Dv F_SETLKW or .Dv F_SETLK_REMOTE flag) and .Xr openat 2 (with .Dv O_EXLOCK or .Dv O_SHLOCK flag). .It Dv CAP_FPATHCONF Permit .Xr fpathconf 2 . .It Dv CAP_FSCK Permit UFS background-fsck operations on the descriptor. .It Dv CAP_FSTAT Permit .Xr fstat 2 and .Xr fstatat 2 if the .Dv CAP_LOOKUP right is also present. .It Dv CAP_FSTATAT An alias to .Dv CAP_FSTAT and .Dv CAP_LOOKUP . .It Dv CAP_FSTATFS Permit .Xr fstatfs 2 . .It Dv CAP_FSYNC Permit .Xr aio_fsync 2 , .Xr fdatasync 2 , .Xr fsync 2 and .Xr openat 2 with .Dv O_FSYNC or .Dv O_SYNC flag. .It Dv CAP_FTRUNCATE Permit .Xr ftruncate 2 and .Xr openat 2 with the .Dv O_TRUNC flag. .It Dv CAP_FUTIMES Permit .Xr futimens 2 and .Xr futimes 2 , and permit .Xr futimesat 2 and .Xr utimensat 2 if the .Dv CAP_LOOKUP right is also present. .It Dv CAP_FUTIMESAT An alias to .Dv CAP_FUTIMES and .Dv CAP_LOOKUP . .It Dv CAP_GETPEERNAME Permit .Xr getpeername 2 . .It Dv CAP_GETSOCKNAME Permit .Xr getsockname 2 . .It Dv CAP_GETSOCKOPT Permit .Xr getsockopt 2 . .It Dv CAP_IOCTL Permit .Xr ioctl 2 . Be aware that this system call has enormous scope, including potentially global scope for some objects. The list of permitted ioctl commands can be further limited with the .Xr cap_ioctls_limit 2 system call. .It Dv CAP_KQUEUE An alias to .Dv CAP_KQUEUE_CHANGE and .Dv CAP_KQUEUE_EVENT . .It Dv CAP_KQUEUE_CHANGE Permit .Xr kevent 2 on a .Xr kqueue 2 descriptor that modifies list of monitored events (the .Fa changelist argument is non-NULL). .It Dv CAP_KQUEUE_EVENT Permit .Xr kevent 2 on a .Xr kqueue 2 descriptor that monitors events (the .Fa eventlist argument is non-NULL). .Dv CAP_EVENT is also required on file descriptors that will be monitored using .Xr kevent 2 . .It Dv CAP_LINKAT_SOURCE Permit .Xr linkat 2 on the source directory descriptor. This right includes the .Dv CAP_LOOKUP right. .Pp Warning: .Dv CAP_LINKAT_SOURCE makes it possible to link files in a directory for which file descriptors exist that have additional rights. For example, a file stored in a directory that does not allow .Dv CAP_READ may be linked in another directory that does allow .Dv CAP_READ , thereby granting read access to a file that is otherwise unreadable. .It Dv CAP_LINKAT_TARGET Permit .Xr linkat 2 on the target directory descriptor. This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_LISTEN Permit .Xr listen 2 ; not much use (generally) without .Dv CAP_BIND . .It Dv CAP_LOOKUP Permit the file descriptor to be used as a starting directory for calls such as .Xr linkat 2 , .Xr openat 2 , and .Xr unlinkat 2 . .It Dv CAP_MAC_GET Permit .Xr mac_get_fd 3 . .It Dv CAP_MAC_SET Permit .Xr mac_set_fd 3 . .It Dv CAP_MKDIRAT Permit .Xr mkdirat 2 . This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_MKFIFOAT Permit .Xr mkfifoat 2 . This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_MKNODAT Permit .Xr mknodat 2 . This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_MMAP Permit .Xr mmap 2 with the .Dv PROT_NONE protection. .It Dv CAP_MMAP_R Permit .Xr mmap 2 with the .Dv PROT_READ protection. This right includes the .Dv CAP_READ and .Dv CAP_SEEK rights. .It Dv CAP_MMAP_RW An alias to .Dv CAP_MMAP_R and .Dv CAP_MMAP_W . .It Dv CAP_MMAP_RWX An alias to .Dv CAP_MMAP_R , .Dv CAP_MMAP_W and .Dv CAP_MMAP_X . .It Dv CAP_MMAP_RX An alias to .Dv CAP_MMAP_R and .Dv CAP_MMAP_X . .It Dv CAP_MMAP_W Permit .Xr mmap 2 with the .Dv PROT_WRITE protection. This right includes the .Dv CAP_WRITE and .Dv CAP_SEEK rights. .It Dv CAP_MMAP_WX An alias to .Dv CAP_MMAP_W and .Dv CAP_MMAP_X . .It Dv CAP_MMAP_X Permit .Xr mmap 2 with the .Dv PROT_EXEC protection. This right includes the .Dv CAP_SEEK right. .It Dv CAP_PDGETPID Permit .Xr pdgetpid 2 . .It Dv CAP_PDKILL Permit .Xr pdkill 2 . .It Dv CAP_PDWAIT Permit .Xr pdwait4 2 . .It Dv CAP_PEELOFF Permit .Xr sctp_peeloff 2 . .It Dv CAP_PREAD An alias to .Dv CAP_READ and .Dv CAP_SEEK . .It Dv CAP_PWRITE An alias to .Dv CAP_SEEK and .Dv CAP_WRITE . .It Dv CAP_READ Permit .Xr aio_read 2 .Dv ( CAP_SEEK is also required), .Xr openat 2 with the .Dv O_RDONLY flag, .Xr read 2 , .Xr readv 2 , .Xr recv 2 , .Xr recvfrom 2 , .Xr recvmsg 2 , .Xr pread 2 .Dv ( CAP_SEEK is also required), .Xr preadv 2 .Dv ( CAP_SEEK is also required) and related system calls. .It Dv CAP_RECV An alias to .Dv CAP_READ . .It Dv CAP_RENAMEAT_SOURCE Permit .Xr renameat 2 on the source directory descriptor. This right includes the .Dv CAP_LOOKUP right. .Pp Warning: .Dv CAP_RENAMEAT_SOURCE makes it possible to move files to a directory for which file descriptors exist that have additional rights. For example, a file stored in a directory that does not allow .Dv CAP_READ may be moved to another directory that does allow .Dv CAP_READ , thereby granting read access to a file that is otherwise unreadable. .It Dv CAP_RENAMEAT_TARGET Permit .Xr renameat 2 on the target directory descriptor. This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_SEEK Permit operations that seek on the file descriptor, such as .Xr lseek 2 , but also required for I/O system calls that can read or write at any position in the file, such as .Xr pread 2 and .Xr pwrite 2 . .It Dv CAP_SEM_GETVALUE Permit .Xr sem_getvalue 3 . .It Dv CAP_SEM_POST Permit .Xr sem_post 3 . .It Dv CAP_SEM_WAIT Permit .Xr sem_wait 3 and .Xr sem_trywait 3 . .It Dv CAP_SEND An alias to .Dv CAP_WRITE . .It Dv CAP_SETSOCKOPT Permit .Xr setsockopt 2 ; this controls various aspects of socket behavior and may affect binding, connecting, and other behaviors with global scope. .It Dv CAP_SHUTDOWN Permit explicit .Xr shutdown 2 ; closing the socket will also generally shut down any connections on it. .It Dv CAP_SYMLINKAT Permit .Xr symlinkat 2 . This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_TTYHOOK Allow configuration of TTY hooks, such as .Xr snp 4 , on the file descriptor. .It Dv CAP_UNLINKAT Permit .Xr unlinkat 2 and .Xr renameat 2 . This right is only required for .Xr renameat 2 on the destination directory descriptor if the destination object already exists and will be removed by the rename. This right includes the .Dv CAP_LOOKUP right. .It Dv CAP_WRITE Allow .Xr aio_write 2 , .Xr openat 2 with .Dv O_WRONLY and .Dv O_APPEND flags set, .Xr send 2 , .Xr sendmsg 2 , .Xr sendto 2 , .Xr write 2 , .Xr writev 2 , .Xr pwrite 2 , .Xr pwritev 2 and related system calls. For .Xr sendto 2 with a non-NULL connection address, .Dv CAP_CONNECT is also required. For .Xr openat 2 with the .Dv O_WRONLY flag, but without the .Dv O_APPEND flag, .Dv CAP_SEEK is also required. For .Xr aio_write 2 , .Xr pwrite 2 and .Xr pwritev 2 .Dv CAP_SEEK is also required. .El .Sh SEE ALSO .Xr accept 2 , .Xr accept4 2 , .Xr aio_fsync 2 , .Xr aio_read 2 , .Xr aio_write 2 , .Xr bind 2 , .Xr bindat 2 , .Xr cap_enter 2 , .Xr cap_fcntls_limit 2 , .Xr cap_ioctls_limit 2 , .Xr cap_rights_limit 2 , .Xr chflagsat 2 , .Xr connect 2 , .Xr connectat 2 , .Xr extattr_delete_fd 2 , .Xr extattr_get_fd 2 , .Xr extattr_list_fd 2 , .Xr extattr_set_fd 2 , .Xr fchflags 2 , .Xr fchmod 2 , .Xr fchmodat 2 , .Xr fchown 2 , .Xr fchownat 2 , .Xr fcntl 2 , .Xr fexecve 2 , .Xr fhopen 2 , .Xr flock 2 , .Xr fpathconf 2 , .Xr fstat 2 , .Xr fstatat 2 , .Xr fstatfs 2 , .Xr fsync 2 , .Xr ftruncate 2 , .Xr futimes 2 , .Xr getpeername 2 , .Xr getsockname 2 , .Xr getsockopt 2 , .Xr ioctl 2 , .Xr kevent 2 , .Xr kqueue 2 , .Xr linkat 2 , .Xr listen 2 , .Xr mmap 2 , .Xr mq_open 2 , .Xr open 2 , .Xr openat 2 , .Xr pdfork 2 , .Xr pdgetpid 2 , .Xr pdkill 2 , .Xr pdwait4 2 , .Xr pipe 2 , .Xr poll 2 , .Xr pread 2 , .Xr preadv 2 , .Xr pwrite 2 , .Xr pwritev 2 , .Xr read 2 , .Xr readv 2 , .Xr recv 2 , .Xr recvfrom 2 , .Xr recvmsg 2 , .Xr renameat 2 , .Xr sctp_peeloff 2 , .Xr select 2 , .Xr send 2 , .Xr sendmsg 2 , .Xr sendto 2 , .Xr setsockopt 2 , .Xr shm_open 2 , .Xr shutdown 2 , .Xr socket 2 , .Xr socketpair 2 , .Xr symlinkat 2 , .Xr unlinkat 2 , .Xr write 2 , .Xr writev 2 , .Xr acl_delete_fd_np 3 , .Xr acl_get_fd 3 , .Xr acl_get_fd_np 3 , .Xr acl_set_fd 3 , .Xr acl_set_fd_np 3 , .Xr acl_valid_fd_np 3 , .Xr mac_get_fd 3 , .Xr mac_set_fd 3 , .Xr sem_getvalue 3 , .Xr sem_post 3 , .Xr sem_trywait 3 , .Xr sem_wait 3 , .Xr capsicum 4 , .Xr snp 4 .Sh HISTORY Support for capabilities and capabilities mode was developed as part of the .Tn TrustedBSD Project. .Sh AUTHORS .An -nosplit This manual page was created by .An Pawel Jakub Dawidek Aq Mt pawel@dawidek.net under sponsorship from the FreeBSD Foundation based on the .Xr cap_new 2 manual page by .An Robert Watson Aq Mt rwatson@FreeBSD.org . Index: head/sys/kern/uipc_syscalls.c =================================================================== --- head/sys/kern/uipc_syscalls.c (revision 333119) +++ head/sys/kern/uipc_syscalls.c (revision 333120) @@ -1,1564 +1,1574 @@ /*- * SPDX-License-Identifier: BSD-3-Clause * * Copyright (c) 1982, 1986, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)uipc_syscalls.c 8.4 (Berkeley) 2/21/94 */ #include __FBSDID("$FreeBSD$"); #include "opt_capsicum.h" #include "opt_inet.h" #include "opt_inet6.h" #include "opt_ktrace.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef KTRACE #include #endif #ifdef COMPAT_FREEBSD32 #include #endif #include #include #include static int sendit(struct thread *td, int s, struct msghdr *mp, int flags); static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp); static int accept1(struct thread *td, int s, struct sockaddr *uname, socklen_t *anamelen, int flags); static int getsockname1(struct thread *td, struct getsockname_args *uap, int compat); static int getpeername1(struct thread *td, struct getpeername_args *uap, int compat); static int sockargs(struct mbuf **, char *, socklen_t, int); /* * Convert a user file descriptor to a kernel file entry and check if required * capability rights are present. * If required copy of current set of capability rights is returned. * A reference on the file entry is held upon returning. */ int getsock_cap(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp, u_int *fflagp, struct filecaps *havecapsp) { struct file *fp; int error; error = fget_cap(td, fd, rightsp, &fp, havecapsp); if (error != 0) return (error); if (fp->f_type != DTYPE_SOCKET) { fdrop(fp, td); if (havecapsp != NULL) filecaps_free(havecapsp); return (ENOTSOCK); } if (fflagp != NULL) *fflagp = fp->f_flag; *fpp = fp; return (0); } /* * System call interface to the socket abstraction. */ #if defined(COMPAT_43) #define COMPAT_OLDSOCK #endif int sys_socket(struct thread *td, struct socket_args *uap) { return (kern_socket(td, uap->domain, uap->type, uap->protocol)); } int kern_socket(struct thread *td, int domain, int type, int protocol) { struct socket *so; struct file *fp; int fd, error, oflag, fflag; AUDIT_ARG_SOCKET(domain, type, protocol); oflag = 0; fflag = 0; if ((type & SOCK_CLOEXEC) != 0) { type &= ~SOCK_CLOEXEC; oflag |= O_CLOEXEC; } if ((type & SOCK_NONBLOCK) != 0) { type &= ~SOCK_NONBLOCK; fflag |= FNONBLOCK; } #ifdef MAC error = mac_socket_check_create(td->td_ucred, domain, type, protocol); if (error != 0) return (error); #endif error = falloc(td, &fp, &fd, oflag); if (error != 0) return (error); /* An extra reference on `fp' has been held for us by falloc(). */ error = socreate(domain, &so, type, protocol, td->td_ucred, td); if (error != 0) { fdclose(td, fp, fd); } else { finit(fp, FREAD | FWRITE | fflag, DTYPE_SOCKET, so, &socketops); if ((fflag & FNONBLOCK) != 0) (void) fo_ioctl(fp, FIONBIO, &fflag, td->td_ucred, td); td->td_retval[0] = fd; } fdrop(fp, td); return (error); } int sys_bind(struct thread *td, struct bind_args *uap) { struct sockaddr *sa; int error; error = getsockaddr(&sa, uap->name, uap->namelen); if (error == 0) { error = kern_bindat(td, AT_FDCWD, uap->s, sa); free(sa, M_SONAME); } return (error); } int kern_bindat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) { struct socket *so; struct file *fp; cap_rights_t rights; int error; +#ifdef CAPABILITY_MODE + if (IN_CAPABILITY_MODE(td) && (dirfd == AT_FDCWD)) + return (ECAPMODE); +#endif + AUDIT_ARG_FD(fd); AUDIT_ARG_SOCKADDR(td, dirfd, sa); error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_BIND), &fp, NULL, NULL); if (error != 0) return (error); so = fp->f_data; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(sa); #endif #ifdef MAC error = mac_socket_check_bind(td->td_ucred, so, sa); if (error == 0) { #endif if (dirfd == AT_FDCWD) error = sobind(so, sa, td); else error = sobindat(dirfd, so, sa, td); #ifdef MAC } #endif fdrop(fp, td); return (error); } int sys_bindat(struct thread *td, struct bindat_args *uap) { struct sockaddr *sa; int error; error = getsockaddr(&sa, uap->name, uap->namelen); if (error == 0) { error = kern_bindat(td, uap->fd, uap->s, sa); free(sa, M_SONAME); } return (error); } int sys_listen(struct thread *td, struct listen_args *uap) { return (kern_listen(td, uap->s, uap->backlog)); } int kern_listen(struct thread *td, int s, int backlog) { struct socket *so; struct file *fp; cap_rights_t rights; int error; AUDIT_ARG_FD(s); error = getsock_cap(td, s, cap_rights_init(&rights, CAP_LISTEN), &fp, NULL, NULL); if (error == 0) { so = fp->f_data; #ifdef MAC error = mac_socket_check_listen(td->td_ucred, so); if (error == 0) #endif error = solisten(so, backlog, td); fdrop(fp, td); } return (error); } /* * accept1() */ static int accept1(td, s, uname, anamelen, flags) struct thread *td; int s; struct sockaddr *uname; socklen_t *anamelen; int flags; { struct sockaddr *name; socklen_t namelen; struct file *fp; int error; if (uname == NULL) return (kern_accept4(td, s, NULL, NULL, flags, NULL)); error = copyin(anamelen, &namelen, sizeof (namelen)); if (error != 0) return (error); error = kern_accept4(td, s, &name, &namelen, flags, &fp); if (error != 0) return (error); if (error == 0 && uname != NULL) { #ifdef COMPAT_OLDSOCK if (flags & ACCEPT4_COMPAT) ((struct osockaddr *)name)->sa_family = name->sa_family; #endif error = copyout(name, uname, namelen); } if (error == 0) error = copyout(&namelen, anamelen, sizeof(namelen)); if (error != 0) fdclose(td, fp, td->td_retval[0]); fdrop(fp, td); free(name, M_SONAME); return (error); } int kern_accept(struct thread *td, int s, struct sockaddr **name, socklen_t *namelen, struct file **fp) { return (kern_accept4(td, s, name, namelen, ACCEPT4_INHERIT, fp)); } int kern_accept4(struct thread *td, int s, struct sockaddr **name, socklen_t *namelen, int flags, struct file **fp) { struct file *headfp, *nfp = NULL; struct sockaddr *sa = NULL; struct socket *head, *so; struct filecaps fcaps; cap_rights_t rights; u_int fflag; pid_t pgid; int error, fd, tmp; if (name != NULL) *name = NULL; AUDIT_ARG_FD(s); error = getsock_cap(td, s, cap_rights_init(&rights, CAP_ACCEPT), &headfp, &fflag, &fcaps); if (error != 0) return (error); head = headfp->f_data; if ((head->so_options & SO_ACCEPTCONN) == 0) { error = EINVAL; goto done; } #ifdef MAC error = mac_socket_check_accept(td->td_ucred, head); if (error != 0) goto done; #endif error = falloc_caps(td, &nfp, &fd, (flags & SOCK_CLOEXEC) ? O_CLOEXEC : 0, &fcaps); if (error != 0) goto done; SOCK_LOCK(head); if (!SOLISTENING(head)) { SOCK_UNLOCK(head); error = EINVAL; goto noconnection; } error = solisten_dequeue(head, &so, flags); if (error != 0) goto noconnection; /* An extra reference on `nfp' has been held for us by falloc(). */ td->td_retval[0] = fd; /* Connection has been removed from the listen queue. */ KNOTE_UNLOCKED(&head->so_rdsel.si_note, 0); if (flags & ACCEPT4_INHERIT) { pgid = fgetown(&head->so_sigio); if (pgid != 0) fsetown(pgid, &so->so_sigio); } else { fflag &= ~(FNONBLOCK | FASYNC); if (flags & SOCK_NONBLOCK) fflag |= FNONBLOCK; } finit(nfp, fflag, DTYPE_SOCKET, so, &socketops); /* Sync socket nonblocking/async state with file flags */ tmp = fflag & FNONBLOCK; (void) fo_ioctl(nfp, FIONBIO, &tmp, td->td_ucred, td); tmp = fflag & FASYNC; (void) fo_ioctl(nfp, FIOASYNC, &tmp, td->td_ucred, td); error = soaccept(so, &sa); if (error != 0) goto noconnection; if (sa == NULL) { if (name) *namelen = 0; goto done; } AUDIT_ARG_SOCKADDR(td, AT_FDCWD, sa); if (name) { /* check sa_len before it is destroyed */ if (*namelen > sa->sa_len) *namelen = sa->sa_len; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(sa); #endif *name = sa; sa = NULL; } noconnection: free(sa, M_SONAME); /* * close the new descriptor, assuming someone hasn't ripped it * out from under us. */ if (error != 0) fdclose(td, nfp, fd); /* * Release explicitly held references before returning. We return * a reference on nfp to the caller on success if they request it. */ done: if (nfp == NULL) filecaps_free(&fcaps); if (fp != NULL) { if (error == 0) { *fp = nfp; nfp = NULL; } else *fp = NULL; } if (nfp != NULL) fdrop(nfp, td); fdrop(headfp, td); return (error); } int sys_accept(td, uap) struct thread *td; struct accept_args *uap; { return (accept1(td, uap->s, uap->name, uap->anamelen, ACCEPT4_INHERIT)); } int sys_accept4(td, uap) struct thread *td; struct accept4_args *uap; { if (uap->flags & ~(SOCK_CLOEXEC | SOCK_NONBLOCK)) return (EINVAL); return (accept1(td, uap->s, uap->name, uap->anamelen, uap->flags)); } #ifdef COMPAT_OLDSOCK int oaccept(td, uap) struct thread *td; struct accept_args *uap; { return (accept1(td, uap->s, uap->name, uap->anamelen, ACCEPT4_INHERIT | ACCEPT4_COMPAT)); } #endif /* COMPAT_OLDSOCK */ int sys_connect(struct thread *td, struct connect_args *uap) { struct sockaddr *sa; int error; error = getsockaddr(&sa, uap->name, uap->namelen); if (error == 0) { error = kern_connectat(td, AT_FDCWD, uap->s, sa); free(sa, M_SONAME); } return (error); } int kern_connectat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) { struct socket *so; struct file *fp; cap_rights_t rights; int error, interrupted = 0; + +#ifdef CAPABILITY_MODE + if (IN_CAPABILITY_MODE(td) && (dirfd == AT_FDCWD)) + return (ECAPMODE); +#endif AUDIT_ARG_FD(fd); AUDIT_ARG_SOCKADDR(td, dirfd, sa); error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_CONNECT), &fp, NULL, NULL); if (error != 0) return (error); so = fp->f_data; if (so->so_state & SS_ISCONNECTING) { error = EALREADY; goto done1; } #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(sa); #endif #ifdef MAC error = mac_socket_check_connect(td->td_ucred, so, sa); if (error != 0) goto bad; #endif if (dirfd == AT_FDCWD) error = soconnect(so, sa, td); else error = soconnectat(dirfd, so, sa, td); if (error != 0) goto bad; if ((so->so_state & SS_NBIO) && (so->so_state & SS_ISCONNECTING)) { error = EINPROGRESS; goto done1; } SOCK_LOCK(so); while ((so->so_state & SS_ISCONNECTING) && so->so_error == 0) { error = msleep(&so->so_timeo, &so->so_lock, PSOCK | PCATCH, "connec", 0); if (error != 0) { if (error == EINTR || error == ERESTART) interrupted = 1; break; } } if (error == 0) { error = so->so_error; so->so_error = 0; } SOCK_UNLOCK(so); bad: if (!interrupted) so->so_state &= ~SS_ISCONNECTING; if (error == ERESTART) error = EINTR; done1: fdrop(fp, td); return (error); } int sys_connectat(struct thread *td, struct connectat_args *uap) { struct sockaddr *sa; int error; error = getsockaddr(&sa, uap->name, uap->namelen); if (error == 0) { error = kern_connectat(td, uap->fd, uap->s, sa); free(sa, M_SONAME); } return (error); } int kern_socketpair(struct thread *td, int domain, int type, int protocol, int *rsv) { struct file *fp1, *fp2; struct socket *so1, *so2; int fd, error, oflag, fflag; AUDIT_ARG_SOCKET(domain, type, protocol); oflag = 0; fflag = 0; if ((type & SOCK_CLOEXEC) != 0) { type &= ~SOCK_CLOEXEC; oflag |= O_CLOEXEC; } if ((type & SOCK_NONBLOCK) != 0) { type &= ~SOCK_NONBLOCK; fflag |= FNONBLOCK; } #ifdef MAC /* We might want to have a separate check for socket pairs. */ error = mac_socket_check_create(td->td_ucred, domain, type, protocol); if (error != 0) return (error); #endif error = socreate(domain, &so1, type, protocol, td->td_ucred, td); if (error != 0) return (error); error = socreate(domain, &so2, type, protocol, td->td_ucred, td); if (error != 0) goto free1; /* On success extra reference to `fp1' and 'fp2' is set by falloc. */ error = falloc(td, &fp1, &fd, oflag); if (error != 0) goto free2; rsv[0] = fd; fp1->f_data = so1; /* so1 already has ref count */ error = falloc(td, &fp2, &fd, oflag); if (error != 0) goto free3; fp2->f_data = so2; /* so2 already has ref count */ rsv[1] = fd; error = soconnect2(so1, so2); if (error != 0) goto free4; if (type == SOCK_DGRAM) { /* * Datagram socket connection is asymmetric. */ error = soconnect2(so2, so1); if (error != 0) goto free4; } finit(fp1, FREAD | FWRITE | fflag, DTYPE_SOCKET, fp1->f_data, &socketops); finit(fp2, FREAD | FWRITE | fflag, DTYPE_SOCKET, fp2->f_data, &socketops); if ((fflag & FNONBLOCK) != 0) { (void) fo_ioctl(fp1, FIONBIO, &fflag, td->td_ucred, td); (void) fo_ioctl(fp2, FIONBIO, &fflag, td->td_ucred, td); } fdrop(fp1, td); fdrop(fp2, td); return (0); free4: fdclose(td, fp2, rsv[1]); fdrop(fp2, td); free3: fdclose(td, fp1, rsv[0]); fdrop(fp1, td); free2: if (so2 != NULL) (void)soclose(so2); free1: if (so1 != NULL) (void)soclose(so1); return (error); } int sys_socketpair(struct thread *td, struct socketpair_args *uap) { int error, sv[2]; error = kern_socketpair(td, uap->domain, uap->type, uap->protocol, sv); if (error != 0) return (error); error = copyout(sv, uap->rsv, 2 * sizeof(int)); if (error != 0) { (void)kern_close(td, sv[0]); (void)kern_close(td, sv[1]); } return (error); } static int sendit(struct thread *td, int s, struct msghdr *mp, int flags) { struct mbuf *control; struct sockaddr *to; int error; #ifdef CAPABILITY_MODE if (IN_CAPABILITY_MODE(td) && (mp->msg_name != NULL)) return (ECAPMODE); #endif if (mp->msg_name != NULL) { error = getsockaddr(&to, mp->msg_name, mp->msg_namelen); if (error != 0) { to = NULL; goto bad; } mp->msg_name = to; } else { to = NULL; } if (mp->msg_control) { if (mp->msg_controllen < sizeof(struct cmsghdr) #ifdef COMPAT_OLDSOCK && mp->msg_flags != MSG_COMPAT #endif ) { error = EINVAL; goto bad; } error = sockargs(&control, mp->msg_control, mp->msg_controllen, MT_CONTROL); if (error != 0) goto bad; #ifdef COMPAT_OLDSOCK if (mp->msg_flags == MSG_COMPAT) { struct cmsghdr *cm; M_PREPEND(control, sizeof(*cm), M_WAITOK); cm = mtod(control, struct cmsghdr *); cm->cmsg_len = control->m_len; cm->cmsg_level = SOL_SOCKET; cm->cmsg_type = SCM_RIGHTS; } #endif } else { control = NULL; } error = kern_sendit(td, s, mp, flags, control, UIO_USERSPACE); bad: free(to, M_SONAME); return (error); } int kern_sendit(struct thread *td, int s, struct msghdr *mp, int flags, struct mbuf *control, enum uio_seg segflg) { struct file *fp; struct uio auio; struct iovec *iov; struct socket *so; cap_rights_t rights; #ifdef KTRACE struct uio *ktruio = NULL; #endif ssize_t len; int i, error; AUDIT_ARG_FD(s); cap_rights_init(&rights, CAP_SEND); if (mp->msg_name != NULL) { AUDIT_ARG_SOCKADDR(td, AT_FDCWD, mp->msg_name); cap_rights_set(&rights, CAP_CONNECT); } error = getsock_cap(td, s, &rights, &fp, NULL, NULL); if (error != 0) { m_freem(control); return (error); } so = (struct socket *)fp->f_data; #ifdef KTRACE if (mp->msg_name != NULL && KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(mp->msg_name); #endif #ifdef MAC if (mp->msg_name != NULL) { error = mac_socket_check_connect(td->td_ucred, so, mp->msg_name); if (error != 0) { m_freem(control); goto bad; } } error = mac_socket_check_send(td->td_ucred, so); if (error != 0) { m_freem(control); goto bad; } #endif auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; auio.uio_segflg = segflg; auio.uio_rw = UIO_WRITE; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; iov = mp->msg_iov; for (i = 0; i < mp->msg_iovlen; i++, iov++) { if ((auio.uio_resid += iov->iov_len) < 0) { error = EINVAL; m_freem(control); goto bad; } } #ifdef KTRACE if (KTRPOINT(td, KTR_GENIO)) ktruio = cloneuio(&auio); #endif len = auio.uio_resid; error = sosend(so, mp->msg_name, &auio, 0, control, flags, td); if (error != 0) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; /* Generation of SIGPIPE can be controlled per socket */ if (error == EPIPE && !(so->so_options & SO_NOSIGPIPE) && !(flags & MSG_NOSIGNAL)) { PROC_LOCK(td->td_proc); tdsignal(td, SIGPIPE); PROC_UNLOCK(td->td_proc); } } if (error == 0) td->td_retval[0] = len - auio.uio_resid; #ifdef KTRACE if (ktruio != NULL) { ktruio->uio_resid = td->td_retval[0]; ktrgenio(s, UIO_WRITE, ktruio, error); } #endif bad: fdrop(fp, td); return (error); } int sys_sendto(struct thread *td, struct sendto_args *uap) { struct msghdr msg; struct iovec aiov; msg.msg_name = uap->to; msg.msg_namelen = uap->tolen; msg.msg_iov = &aiov; msg.msg_iovlen = 1; msg.msg_control = 0; #ifdef COMPAT_OLDSOCK msg.msg_flags = 0; #endif aiov.iov_base = uap->buf; aiov.iov_len = uap->len; return (sendit(td, uap->s, &msg, uap->flags)); } #ifdef COMPAT_OLDSOCK int osend(struct thread *td, struct osend_args *uap) { struct msghdr msg; struct iovec aiov; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = 0; return (sendit(td, uap->s, &msg, uap->flags)); } int osendmsg(struct thread *td, struct osendmsg_args *uap) { struct msghdr msg; struct iovec *iov; int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error != 0) return (error); msg.msg_iov = iov; msg.msg_flags = MSG_COMPAT; error = sendit(td, uap->s, &msg, uap->flags); free(iov, M_IOV); return (error); } #endif int sys_sendmsg(struct thread *td, struct sendmsg_args *uap) { struct msghdr msg; struct iovec *iov; int error; error = copyin(uap->msg, &msg, sizeof (msg)); if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error != 0) return (error); msg.msg_iov = iov; #ifdef COMPAT_OLDSOCK msg.msg_flags = 0; #endif error = sendit(td, uap->s, &msg, uap->flags); free(iov, M_IOV); return (error); } int kern_recvit(struct thread *td, int s, struct msghdr *mp, enum uio_seg fromseg, struct mbuf **controlp) { struct uio auio; struct iovec *iov; struct mbuf *m, *control = NULL; caddr_t ctlbuf; struct file *fp; struct socket *so; struct sockaddr *fromsa = NULL; cap_rights_t rights; #ifdef KTRACE struct uio *ktruio = NULL; #endif ssize_t len; int error, i; if (controlp != NULL) *controlp = NULL; AUDIT_ARG_FD(s); error = getsock_cap(td, s, cap_rights_init(&rights, CAP_RECV), &fp, NULL, NULL); if (error != 0) return (error); so = fp->f_data; #ifdef MAC error = mac_socket_check_receive(td->td_ucred, so); if (error != 0) { fdrop(fp, td); return (error); } #endif auio.uio_iov = mp->msg_iov; auio.uio_iovcnt = mp->msg_iovlen; auio.uio_segflg = UIO_USERSPACE; auio.uio_rw = UIO_READ; auio.uio_td = td; auio.uio_offset = 0; /* XXX */ auio.uio_resid = 0; iov = mp->msg_iov; for (i = 0; i < mp->msg_iovlen; i++, iov++) { if ((auio.uio_resid += iov->iov_len) < 0) { fdrop(fp, td); return (EINVAL); } } #ifdef KTRACE if (KTRPOINT(td, KTR_GENIO)) ktruio = cloneuio(&auio); #endif len = auio.uio_resid; error = soreceive(so, &fromsa, &auio, NULL, (mp->msg_control || controlp) ? &control : NULL, &mp->msg_flags); if (error != 0) { if (auio.uio_resid != len && (error == ERESTART || error == EINTR || error == EWOULDBLOCK)) error = 0; } if (fromsa != NULL) AUDIT_ARG_SOCKADDR(td, AT_FDCWD, fromsa); #ifdef KTRACE if (ktruio != NULL) { ktruio->uio_resid = len - auio.uio_resid; ktrgenio(s, UIO_READ, ktruio, error); } #endif if (error != 0) goto out; td->td_retval[0] = len - auio.uio_resid; if (mp->msg_name) { len = mp->msg_namelen; if (len <= 0 || fromsa == NULL) len = 0; else { /* save sa_len before it is destroyed by MSG_COMPAT */ len = MIN(len, fromsa->sa_len); #ifdef COMPAT_OLDSOCK if (mp->msg_flags & MSG_COMPAT) ((struct osockaddr *)fromsa)->sa_family = fromsa->sa_family; #endif if (fromseg == UIO_USERSPACE) { error = copyout(fromsa, mp->msg_name, (unsigned)len); if (error != 0) goto out; } else bcopy(fromsa, mp->msg_name, len); } mp->msg_namelen = len; } if (mp->msg_control && controlp == NULL) { #ifdef COMPAT_OLDSOCK /* * We assume that old recvmsg calls won't receive access * rights and other control info, esp. as control info * is always optional and those options didn't exist in 4.3. * If we receive rights, trim the cmsghdr; anything else * is tossed. */ if (control && mp->msg_flags & MSG_COMPAT) { if (mtod(control, struct cmsghdr *)->cmsg_level != SOL_SOCKET || mtod(control, struct cmsghdr *)->cmsg_type != SCM_RIGHTS) { mp->msg_controllen = 0; goto out; } control->m_len -= sizeof (struct cmsghdr); control->m_data += sizeof (struct cmsghdr); } #endif len = mp->msg_controllen; m = control; mp->msg_controllen = 0; ctlbuf = mp->msg_control; while (m && len > 0) { unsigned int tocopy; if (len >= m->m_len) tocopy = m->m_len; else { mp->msg_flags |= MSG_CTRUNC; tocopy = len; } if ((error = copyout(mtod(m, caddr_t), ctlbuf, tocopy)) != 0) goto out; ctlbuf += tocopy; len -= tocopy; m = m->m_next; } mp->msg_controllen = ctlbuf - (caddr_t)mp->msg_control; } out: fdrop(fp, td); #ifdef KTRACE if (fromsa && KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(fromsa); #endif free(fromsa, M_SONAME); if (error == 0 && controlp != NULL) *controlp = control; else if (control) m_freem(control); return (error); } static int recvit(struct thread *td, int s, struct msghdr *mp, void *namelenp) { int error; error = kern_recvit(td, s, mp, UIO_USERSPACE, NULL); if (error != 0) return (error); if (namelenp != NULL) { error = copyout(&mp->msg_namelen, namelenp, sizeof (socklen_t)); #ifdef COMPAT_OLDSOCK if (mp->msg_flags & MSG_COMPAT) error = 0; /* old recvfrom didn't check */ #endif } return (error); } int sys_recvfrom(struct thread *td, struct recvfrom_args *uap) { struct msghdr msg; struct iovec aiov; int error; if (uap->fromlenaddr) { error = copyin(uap->fromlenaddr, &msg.msg_namelen, sizeof (msg.msg_namelen)); if (error != 0) goto done2; } else { msg.msg_namelen = 0; } msg.msg_name = uap->from; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; error = recvit(td, uap->s, &msg, uap->fromlenaddr); done2: return (error); } #ifdef COMPAT_OLDSOCK int orecvfrom(struct thread *td, struct recvfrom_args *uap) { uap->flags |= MSG_COMPAT; return (sys_recvfrom(td, uap)); } #endif #ifdef COMPAT_OLDSOCK int orecv(struct thread *td, struct orecv_args *uap) { struct msghdr msg; struct iovec aiov; msg.msg_name = 0; msg.msg_namelen = 0; msg.msg_iov = &aiov; msg.msg_iovlen = 1; aiov.iov_base = uap->buf; aiov.iov_len = uap->len; msg.msg_control = 0; msg.msg_flags = uap->flags; return (recvit(td, uap->s, &msg, NULL)); } /* * Old recvmsg. This code takes advantage of the fact that the old msghdr * overlays the new one, missing only the flags, and with the (old) access * rights where the control fields are now. */ int orecvmsg(struct thread *td, struct orecvmsg_args *uap) { struct msghdr msg; struct iovec *iov; int error; error = copyin(uap->msg, &msg, sizeof (struct omsghdr)); if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error != 0) return (error); msg.msg_flags = uap->flags | MSG_COMPAT; msg.msg_iov = iov; error = recvit(td, uap->s, &msg, &uap->msg->msg_namelen); if (msg.msg_controllen && error == 0) error = copyout(&msg.msg_controllen, &uap->msg->msg_accrightslen, sizeof (int)); free(iov, M_IOV); return (error); } #endif int sys_recvmsg(struct thread *td, struct recvmsg_args *uap) { struct msghdr msg; struct iovec *uiov, *iov; int error; error = copyin(uap->msg, &msg, sizeof (msg)); if (error != 0) return (error); error = copyiniov(msg.msg_iov, msg.msg_iovlen, &iov, EMSGSIZE); if (error != 0) return (error); msg.msg_flags = uap->flags; #ifdef COMPAT_OLDSOCK msg.msg_flags &= ~MSG_COMPAT; #endif uiov = msg.msg_iov; msg.msg_iov = iov; error = recvit(td, uap->s, &msg, NULL); if (error == 0) { msg.msg_iov = uiov; error = copyout(&msg, uap->msg, sizeof(msg)); } free(iov, M_IOV); return (error); } int sys_shutdown(struct thread *td, struct shutdown_args *uap) { return (kern_shutdown(td, uap->s, uap->how)); } int kern_shutdown(struct thread *td, int s, int how) { struct socket *so; struct file *fp; cap_rights_t rights; int error; AUDIT_ARG_FD(s); error = getsock_cap(td, s, cap_rights_init(&rights, CAP_SHUTDOWN), &fp, NULL, NULL); if (error == 0) { so = fp->f_data; error = soshutdown(so, how); /* * Previous versions did not return ENOTCONN, but 0 in * case the socket was not connected. Some important * programs like syslogd up to r279016, 2015-02-19, * still depend on this behavior. */ if (error == ENOTCONN && td->td_proc->p_osrel < P_OSREL_SHUTDOWN_ENOTCONN) error = 0; fdrop(fp, td); } return (error); } int sys_setsockopt(struct thread *td, struct setsockopt_args *uap) { return (kern_setsockopt(td, uap->s, uap->level, uap->name, uap->val, UIO_USERSPACE, uap->valsize)); } int kern_setsockopt(struct thread *td, int s, int level, int name, void *val, enum uio_seg valseg, socklen_t valsize) { struct socket *so; struct file *fp; struct sockopt sopt; cap_rights_t rights; int error; if (val == NULL && valsize != 0) return (EFAULT); if ((int)valsize < 0) return (EINVAL); sopt.sopt_dir = SOPT_SET; sopt.sopt_level = level; sopt.sopt_name = name; sopt.sopt_val = val; sopt.sopt_valsize = valsize; switch (valseg) { case UIO_USERSPACE: sopt.sopt_td = td; break; case UIO_SYSSPACE: sopt.sopt_td = NULL; break; default: panic("kern_setsockopt called with bad valseg"); } AUDIT_ARG_FD(s); error = getsock_cap(td, s, cap_rights_init(&rights, CAP_SETSOCKOPT), &fp, NULL, NULL); if (error == 0) { so = fp->f_data; error = sosetopt(so, &sopt); fdrop(fp, td); } return(error); } int sys_getsockopt(struct thread *td, struct getsockopt_args *uap) { socklen_t valsize; int error; if (uap->val) { error = copyin(uap->avalsize, &valsize, sizeof (valsize)); if (error != 0) return (error); } error = kern_getsockopt(td, uap->s, uap->level, uap->name, uap->val, UIO_USERSPACE, &valsize); if (error == 0) error = copyout(&valsize, uap->avalsize, sizeof (valsize)); return (error); } /* * Kernel version of getsockopt. * optval can be a userland or userspace. optlen is always a kernel pointer. */ int kern_getsockopt(struct thread *td, int s, int level, int name, void *val, enum uio_seg valseg, socklen_t *valsize) { struct socket *so; struct file *fp; struct sockopt sopt; cap_rights_t rights; int error; if (val == NULL) *valsize = 0; if ((int)*valsize < 0) return (EINVAL); sopt.sopt_dir = SOPT_GET; sopt.sopt_level = level; sopt.sopt_name = name; sopt.sopt_val = val; sopt.sopt_valsize = (size_t)*valsize; /* checked non-negative above */ switch (valseg) { case UIO_USERSPACE: sopt.sopt_td = td; break; case UIO_SYSSPACE: sopt.sopt_td = NULL; break; default: panic("kern_getsockopt called with bad valseg"); } AUDIT_ARG_FD(s); error = getsock_cap(td, s, cap_rights_init(&rights, CAP_GETSOCKOPT), &fp, NULL, NULL); if (error == 0) { so = fp->f_data; error = sogetopt(so, &sopt); *valsize = sopt.sopt_valsize; fdrop(fp, td); } return (error); } /* * getsockname1() - Get socket name. */ static int getsockname1(struct thread *td, struct getsockname_args *uap, int compat) { struct sockaddr *sa; socklen_t len; int error; error = copyin(uap->alen, &len, sizeof(len)); if (error != 0) return (error); error = kern_getsockname(td, uap->fdes, &sa, &len); if (error != 0) return (error); if (len != 0) { #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)sa)->sa_family = sa->sa_family; #endif error = copyout(sa, uap->asa, (u_int)len); } free(sa, M_SONAME); if (error == 0) error = copyout(&len, uap->alen, sizeof(len)); return (error); } int kern_getsockname(struct thread *td, int fd, struct sockaddr **sa, socklen_t *alen) { struct socket *so; struct file *fp; cap_rights_t rights; socklen_t len; int error; AUDIT_ARG_FD(fd); error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_GETSOCKNAME), &fp, NULL, NULL); if (error != 0) return (error); so = fp->f_data; *sa = NULL; CURVNET_SET(so->so_vnet); error = (*so->so_proto->pr_usrreqs->pru_sockaddr)(so, sa); CURVNET_RESTORE(); if (error != 0) goto bad; if (*sa == NULL) len = 0; else len = MIN(*alen, (*sa)->sa_len); *alen = len; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(*sa); #endif bad: fdrop(fp, td); if (error != 0 && *sa != NULL) { free(*sa, M_SONAME); *sa = NULL; } return (error); } int sys_getsockname(struct thread *td, struct getsockname_args *uap) { return (getsockname1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK int ogetsockname(struct thread *td, struct getsockname_args *uap) { return (getsockname1(td, uap, 1)); } #endif /* COMPAT_OLDSOCK */ /* * getpeername1() - Get name of peer for connected socket. */ static int getpeername1(struct thread *td, struct getpeername_args *uap, int compat) { struct sockaddr *sa; socklen_t len; int error; error = copyin(uap->alen, &len, sizeof (len)); if (error != 0) return (error); error = kern_getpeername(td, uap->fdes, &sa, &len); if (error != 0) return (error); if (len != 0) { #ifdef COMPAT_OLDSOCK if (compat) ((struct osockaddr *)sa)->sa_family = sa->sa_family; #endif error = copyout(sa, uap->asa, (u_int)len); } free(sa, M_SONAME); if (error == 0) error = copyout(&len, uap->alen, sizeof(len)); return (error); } int kern_getpeername(struct thread *td, int fd, struct sockaddr **sa, socklen_t *alen) { struct socket *so; struct file *fp; cap_rights_t rights; socklen_t len; int error; AUDIT_ARG_FD(fd); error = getsock_cap(td, fd, cap_rights_init(&rights, CAP_GETPEERNAME), &fp, NULL, NULL); if (error != 0) return (error); so = fp->f_data; if ((so->so_state & (SS_ISCONNECTED|SS_ISCONFIRMING)) == 0) { error = ENOTCONN; goto done; } *sa = NULL; CURVNET_SET(so->so_vnet); error = (*so->so_proto->pr_usrreqs->pru_peeraddr)(so, sa); CURVNET_RESTORE(); if (error != 0) goto bad; if (*sa == NULL) len = 0; else len = MIN(*alen, (*sa)->sa_len); *alen = len; #ifdef KTRACE if (KTRPOINT(td, KTR_STRUCT)) ktrsockaddr(*sa); #endif bad: if (error != 0 && *sa != NULL) { free(*sa, M_SONAME); *sa = NULL; } done: fdrop(fp, td); return (error); } int sys_getpeername(struct thread *td, struct getpeername_args *uap) { return (getpeername1(td, uap, 0)); } #ifdef COMPAT_OLDSOCK int ogetpeername(struct thread *td, struct ogetpeername_args *uap) { /* XXX uap should have type `getpeername_args *' to begin with. */ return (getpeername1(td, (struct getpeername_args *)uap, 1)); } #endif /* COMPAT_OLDSOCK */ static int sockargs(struct mbuf **mp, char *buf, socklen_t buflen, int type) { struct sockaddr *sa; struct mbuf *m; int error; if (buflen > MLEN) { #ifdef COMPAT_OLDSOCK if (type == MT_SONAME && buflen <= 112) buflen = MLEN; /* unix domain compat. hack */ else #endif if (buflen > MCLBYTES) return (EINVAL); } m = m_get2(buflen, M_WAITOK, type, 0); m->m_len = buflen; error = copyin(buf, mtod(m, void *), buflen); if (error != 0) (void) m_free(m); else { *mp = m; if (type == MT_SONAME) { sa = mtod(m, struct sockaddr *); #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN if (sa->sa_family == 0 && sa->sa_len < AF_MAX) sa->sa_family = sa->sa_len; #endif sa->sa_len = buflen; } } return (error); } int getsockaddr(struct sockaddr **namp, caddr_t uaddr, size_t len) { struct sockaddr *sa; int error; if (len > SOCK_MAXADDRLEN) return (ENAMETOOLONG); if (len < offsetof(struct sockaddr, sa_data[0])) return (EINVAL); sa = malloc(len, M_SONAME, M_WAITOK); error = copyin(uaddr, sa, len); if (error != 0) { free(sa, M_SONAME); } else { #if defined(COMPAT_OLDSOCK) && BYTE_ORDER != BIG_ENDIAN if (sa->sa_family == 0 && sa->sa_len < AF_MAX) sa->sa_family = sa->sa_len; #endif sa->sa_len = len; *namp = sa; } return (error); } Index: head/tests/sys/capsicum/Makefile =================================================================== --- head/tests/sys/capsicum/Makefile (revision 333119) +++ head/tests/sys/capsicum/Makefile (revision 333120) @@ -1,9 +1,12 @@ # $FreeBSD$ TESTSDIR= ${TESTSBASE}/sys/capsicum +ATF_TESTS_C+= bindat_connectat ATF_TESTS_C+= ioctls_test + +CFLAGS.bindat_connectat.c+= -I${SRCTOP}/tests WARNS?= 6 .include Index: head/tests/sys/capsicum/bindat_connectat.c =================================================================== --- head/tests/sys/capsicum/bindat_connectat.c (nonexistent) +++ head/tests/sys/capsicum/bindat_connectat.c (revision 333120) @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2017 Jan Kokemüller + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "freebsd_test_suite/macros.h" + +static int rootfd = -1; + +/* circumvent bug 215690 */ +int +open(const char *path, int flags, ...) +{ + mode_t mode = 0; + + if (flags & O_CREAT) { + va_list ap; + va_start(ap, flags); + mode = (mode_t) va_arg(ap, int); + va_end(ap); + } + + if (path && path[0] == '/' && rootfd >= 0) { + return (openat(rootfd, path + 1, flags, mode)); + } else { + return (openat(AT_FDCWD, path, flags, mode)); + } +} + +static void +check_capsicum(void) +{ + ATF_REQUIRE_FEATURE("security_capabilities"); + ATF_REQUIRE_FEATURE("security_capability_mode"); + + ATF_REQUIRE((rootfd = open("/", O_EXEC | O_CLOEXEC)) >= 0); +} + +typedef int (*socket_fun)(int, const struct sockaddr *, socklen_t); + +static int +connectat_fdcwd(int s, const struct sockaddr *name, socklen_t namelen) +{ + + return (connectat(AT_FDCWD, s, name, namelen)); +} + +static int +bindat_fdcwd(int s, const struct sockaddr *name, socklen_t namelen) +{ + + return (bindat(AT_FDCWD, s, name, namelen)); +} + + +ATF_TC(bindat_connectat_1); +ATF_TC_HEAD(bindat_connectat_1, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that connect/bind work in normal case"); +} + +static void +check_1(socket_fun f, int s, const struct sockaddr_in *name) +{ + + ATF_REQUIRE((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0); + ATF_REQUIRE_ERRNO(EAFNOSUPPORT, + f(s, (const struct sockaddr *)(name), + sizeof(struct sockaddr_in)) < 0); +} + +ATF_TC_BODY(bindat_connectat_1, tc) +{ + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(0); + sin.sin_addr.s_addr = htonl(0xE0000000); + + check_1(bindat_fdcwd, 0, &sin); + check_1(bind, 0, &sin); + check_1(connectat_fdcwd, 0, &sin); + check_1(connect, 0, &sin); +} + + +ATF_TC(bindat_connectat_2); +ATF_TC_HEAD(bindat_connectat_2, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Verify that connect/bind are disabled in cap-mode"); +} + +static void +check_2(socket_fun f, int s, const struct sockaddr_in *name) +{ + + ATF_REQUIRE_ERRNO(ECAPMODE, + f(s, (const struct sockaddr *)name, + sizeof(struct sockaddr_in)) < 0); +} + +ATF_TC_BODY(bindat_connectat_2, tc) +{ + int sock; + struct sockaddr_in sin; + + check_capsicum(); + + ATF_REQUIRE(cap_enter() >= 0); + + /* note: sock is created _after_ cap_enter() and contains all rights */ + ATF_REQUIRE((sock = socket(AF_INET, SOCK_STREAM, 0)) >= 0); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + /* dummy port and multicast address (224.0.0.0) to distinguish two + * cases: + * - ECAPMODE/ENOTCAPABLE --> call blocked by capsicum + * - EAFNOSUPPORT --> call went through to protocol layer + */ + sin.sin_port = htons(0); + sin.sin_addr.s_addr = htonl(0xE0000000); + + check_2(bindat_fdcwd, sock, &sin); + check_2(bind, sock, &sin); + check_2(connectat_fdcwd, sock, &sin); + check_2(connect, sock, &sin); +} + + +ATF_TC(bindat_connectat_3); +ATF_TC_HEAD(bindat_connectat_3, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Check that taking away CAP_BIND/CAP_CONNECT " + "sabotages bind/connect"); +} + +static void +check_3(socket_fun f, int s, const struct sockaddr_in *name, + cap_rights_t *rights, cap_rights_t *sub_rights) +{ + + ATF_REQUIRE((s = socket(AF_INET, SOCK_STREAM, 0)) >= 0); + ATF_REQUIRE(cap_rights_limit(s, rights) >= 0); + ATF_REQUIRE_ERRNO(EAFNOSUPPORT, + f(s, (const struct sockaddr *)name, + sizeof(struct sockaddr_in)) < 0); + ATF_REQUIRE(cap_rights_limit(s, + cap_rights_remove(rights, sub_rights)) >= 0); + ATF_REQUIRE_ERRNO(ENOTCAPABLE, + f(s, (const struct sockaddr *)name, + sizeof(struct sockaddr_in)) < 0); +} + +ATF_TC_BODY(bindat_connectat_3, tc) +{ + struct sockaddr_in sin; + cap_rights_t rights, sub_rights; + + check_capsicum(); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_port = htons(0); + sin.sin_addr.s_addr = htonl(0xE0000000); + + check_3(bindat_fdcwd, 0, &sin, + cap_rights_init(&rights, CAP_SOCK_SERVER), + cap_rights_init(&sub_rights, CAP_BIND)); + check_3(bind, 0, &sin, + cap_rights_init(&rights, CAP_SOCK_SERVER), + cap_rights_init(&sub_rights, CAP_BIND)); + check_3(connectat_fdcwd, 0, &sin, + cap_rights_init(&rights, CAP_SOCK_CLIENT), + cap_rights_init(&sub_rights, CAP_CONNECT)); + check_3(connect, 0, &sin, + cap_rights_init(&rights, CAP_SOCK_CLIENT), + cap_rights_init(&sub_rights, CAP_CONNECT)); +} + + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, bindat_connectat_1); + ATF_TP_ADD_TC(tp, bindat_connectat_2); + ATF_TP_ADD_TC(tp, bindat_connectat_3); + + return (atf_no_error()); +} Property changes on: head/tests/sys/capsicum/bindat_connectat.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property