Page MenuHomeFreeBSD

D57423.diff
No OneTemporary

D57423.diff

diff --git a/lib/libsys/getsockopt.2 b/lib/libsys/getsockopt.2
--- a/lib/libsys/getsockopt.2
+++ b/lib/libsys/getsockopt.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 21, 2026
+.Dd June 3, 2026
.Dt GETSOCKOPT 2
.Os
.Sh NAME
@@ -192,6 +192,7 @@
.It Dv SO_NO_OFFLOAD Ta "disables protocol offloads"
.It Dv SO_NO_DDP Ta "disables direct data placement offload"
.It Dv SO_SPLICE Ta "splice two sockets together"
+.It Dv SO_PASSRIGHTS Ta "enables passing of SCM_RIGHTS over unix(4) sockets"
.El
.Pp
.Dv SO_DEBUG
diff --git a/lib/libsys/send.2 b/lib/libsys/send.2
--- a/lib/libsys/send.2
+++ b/lib/libsys/send.2
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd April 27, 2020
+.Dd June 3, 2026
.Dt SEND 2
.Os
.Sh NAME
@@ -250,6 +250,15 @@
socket was jailed and the source
address specified in the IP header did not match the IP
address bound to the prison.
+.It Bq Er EPERM
+The
+.Fa msg
+contained an
+.Dv SCM_RIGHTS
+control message, and the receiving
+.Xr unix 4
+socket is configured to reject new
+.Dv SCM_RIGHTS .
.It Bq Er EPIPE
The socket is unable to send anymore data
.Dv ( SBS_CANTSENDMORE
@@ -265,7 +274,8 @@
.Xr select 2 ,
.Xr socket 2 ,
.Xr write 2 ,
-.Xr CMSG_DATA 3
+.Xr CMSG_DATA 3 ,
+.Xr unix 4
.Sh HISTORY
The
.Fn send
diff --git a/share/man/man4/unix.4 b/share/man/man4/unix.4
--- a/share/man/man4/unix.4
+++ b/share/man/man4/unix.4
@@ -25,7 +25,7 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd October 31, 2024
+.Dd June 3, 2026
.Dt UNIX 4
.Os
.Sh NAME
@@ -163,9 +163,20 @@
is passed in the
.Xr recvmsg 2
call.
+.Pp
Descriptors that are awaiting delivery, or that are
purposely not received, are automatically closed by the system
when the destination socket is closed.
+The receiving socket can reject
+.Dv SCM_RIGHTS
+at the
+.Xr sendmsg 2
+call with the socket option
+.Dv SO_PASSRIGHTS ,
+which can be set with
+.Xr setsockopt 2
+and tested with
+.Xr getsockopt 2 .
.Pp
Credentials of the sending process can be transmitted explicitly using a
control message of type
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -303,9 +303,10 @@
static void unp_discard(struct file *);
static void unp_freerights(struct filedescent **, int);
static int unp_internalize(struct mbuf *, struct mchain *,
- struct thread *);
+ struct thread *, int *);
static void unp_internalize_fp(struct file *);
-static int unp_externalize(struct mbuf *, struct mbuf **, int);
+static int unp_externalize(const struct socket *, struct mbuf *,
+ struct mbuf **, int);
static int unp_externalize_fp(struct file *);
static void unp_addsockcred(struct thread *, struct mchain *, int);
static void unp_process_defers(void * __unused, int);
@@ -527,6 +528,7 @@
UNP_PCB_LOCK_INIT(unp);
unp->unp_socket = so;
so->so_pcb = unp;
+ so->so_options |= SO_PASSRIGHTS;
refcount_init(&unp->unp_refcount, 1);
unp->unp_mode = ACCESSPERMS;
@@ -1112,7 +1114,7 @@
struct mchain mc, cmc;
size_t resid, sent;
bool nonblock, eor, aio;
- int error;
+ int error, needsopts;
MPASS((uio0 != NULL && m == NULL) || (m != NULL && uio0 == NULL));
MPASS(m == NULL || c == NULL);
@@ -1128,9 +1130,11 @@
cmc = MCHAIN_INITIALIZER(&cmc);
sent = 0;
aio = false;
+ needsopts = 0;
if (m == NULL) {
- if (c != NULL && (error = unp_internalize(c, &cmc, td)))
+ if (c != NULL &&
+ (error = unp_internalize(c, &cmc, td, &needsopts)))
goto out;
/*
* This function may read more data from the uio than it would
@@ -1176,6 +1180,14 @@
if (__predict_false((error = uipc_lock_peer(so, &unp2)) != 0))
goto out3;
+ /* Check for SO_PASS* flags */
+ so2 = unp2->unp_socket;
+ if ((so2->so_options & needsopts) != needsopts) {
+ error = EPERM;
+ UNP_PCB_UNLOCK(unp2);
+ goto out3;
+ }
+
if (unp2->unp_flags & UNP_WANTCRED_MASK) {
/*
* Credentials are passed only once on SOCK_STREAM and
@@ -1193,7 +1205,6 @@
* observe the SBS_CANTRCVMORE and our sorele() will finalize peer's
* socket destruction.
*/
- so2 = unp2->unp_socket;
soref(so2);
UNP_PCB_UNLOCK(unp2);
sb = &so2->so_rcv;
@@ -1560,7 +1571,7 @@
* is fine that we need to perform pretty complex
* operation here to reconstruct the buffer.
*/
- error = unp_externalize(control, controlp, flags);
+ error = unp_externalize(so, control, controlp, flags);
control = m_free(control);
if (__predict_false(error && control != NULL)) {
struct mchain cmc;
@@ -1959,11 +1970,11 @@
struct mbuf *f;
u_int cc, ctl, mbcnt;
u_int dcc __diagused, dctl __diagused, dmbcnt __diagused;
- int error;
+ int error, needsopts;
MPASS((uio != NULL && m == NULL) || (m != NULL && uio == NULL));
- error = 0;
+ error = needsopts = 0;
f = NULL;
if (__predict_false(flags & MSG_OOB)) {
@@ -1983,7 +1994,8 @@
f = m_gethdr(M_WAITOK, MT_SONAME);
cc = m->m_pkthdr.len;
mbcnt = MSIZE + m->m_pkthdr.memlen;
- if (c != NULL && (error = unp_internalize(c, &cmc, td)))
+ if (c != NULL &&
+ (error = unp_internalize(c, &cmc, td, &needsopts)))
goto out;
} else {
struct mchain mc;
@@ -2049,6 +2061,13 @@
}
}
+ /* Check for SO_PASS* flags */
+ so2 = unp2->unp_socket;
+ if ((so2->so_options & needsopts) != needsopts) {
+ error = EPERM;
+ goto out4;
+ }
+
if (unp2->unp_flags & UNP_WANTCRED_MASK)
unp_addsockcred(td, &cmc, unp2->unp_flags);
if (unp->unp_addr != NULL)
@@ -2117,7 +2136,6 @@
* would accumulate counters from all connected buffers potentially
* having sb_ccc > sb_hiwat or sb_mbcnt > sb_mbmax.
*/
- so2 = unp2->unp_socket;
sb = (addr == NULL) ? &so->so_snd : &so2->so_rcv;
SOCK_RECVBUF_LOCK(so2);
if (uipc_dgram_sbspace(sb, cc + ctl, mbcnt)) {
@@ -2143,6 +2161,7 @@
}
}
+out4:
if (addr != NULL)
unp_disconnect(unp, unp2);
else
@@ -2345,7 +2364,7 @@
* without MT_DATA mbufs.
*/
while (m != NULL && m->m_type == MT_CONTROL) {
- error = unp_externalize(m, controlp, flags);
+ error = unp_externalize(so, m, controlp, flags);
m = m_free(m);
if (error != 0) {
SOCK_IO_RECV_UNLOCK(so);
@@ -3494,7 +3513,8 @@
}
static int
-unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
+unp_externalize(const struct socket *so, struct mbuf *control,
+ struct mbuf **controlp, int flags)
{
struct thread *td = curthread; /* XXX */
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
@@ -3526,8 +3546,17 @@
goto next;
fdep = data;
- /* If we're not outputting the descriptors free them. */
- if (error || controlp == NULL) {
+ /*
+ * If we're not outputting the descriptors, free them.
+ *
+ * In the case of having revoked SCM_PASSRIGHTS, the
+ * receiver must have toggled it before trying to
+ * receive control messages- we'll take that as a signal
+ * that they didn't want these, but they raced against
+ * the sender trying to pass files anyways.
+ */
+ if (error || controlp == NULL ||
+ (so->so_options & SO_PASSRIGHTS) == 0) {
unp_freerights(fdep, newfds);
goto next;
}
@@ -3672,7 +3701,8 @@
}
static int
-unp_internalize(struct mbuf *control, struct mchain *mc, struct thread *td)
+unp_internalize(struct mbuf *control, struct mchain *mc, struct thread *td,
+ int *needsopts)
{
struct proc *p;
struct filedesc *fdesc;
@@ -3728,6 +3758,7 @@
break;
case SCM_RIGHTS:
+ *needsopts |= SO_PASSRIGHTS;
oldfds = datalen / sizeof (int);
if (oldfds == 0)
continue;
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -147,6 +147,7 @@
#define SO_NO_DDP 0x00008000 /* disable direct data placement */
#define SO_REUSEPORT_LB 0x00010000 /* reuse with load balancing */
#define SO_RERROR 0x00020000 /* keep track of receive errors */
+#define SO_PASSRIGHTS 0x00040000 /* unix(4) accepts SCM_RIGHTS */
/*
* Additional options, not kept in so_options.

File Metadata

Mime Type
text/plain
Expires
Thu, Jun 11, 9:07 PM (4 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33858980
Default Alt Text
D57423.diff (7 KB)

Event Timeline