unix(4): Add SOL_LOCAL:LOCAL_CREDS_PERSISTENT

This option is intended to be semantically identical to Linux's
SOL_SOCKET:SO_PASSCRED.  For now, it is mutually exclusive with the
pre-existing sockopt SOL_LOCAL:LOCAL_CREDS.

Reviewed by:	markj (penultimate version)
Differential Revision:	https://reviews.freebsd.org/D27011
This commit is contained in:
Conrad Meyer 2020-11-03 01:17:45 +00:00
parent a98f03786e
commit 2de07e4096
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=367287
4 changed files with 58 additions and 21 deletions

View File

@ -28,7 +28,7 @@
.\" @(#)unix.4 8.1 (Berkeley) 6/9/93
.\" $FreeBSD$
.\"
.Dd August 3, 2020
.Dd November 2, 2020
.Dt UNIX 4
.Os
.Sh NAME
@ -201,7 +201,7 @@ which can be set with
.Xr setsockopt 2
and tested with
.Xr getsockopt 2 :
.Bl -tag -width ".Dv LOCAL_CONNWAIT"
.Bl -tag -width ".Dv LOCAL_CREDS_PERSISTENT"
.It Dv LOCAL_CREDS
This option may be enabled on
.Dv SOCK_DGRAM ,
@ -287,6 +287,19 @@ such as error messages.
Therefore, a message accompanied by a particular
.Fa sc_euid
value should not be trusted as being from that user.
.It Dv LOCAL_CREDS_PERSISTENT
This option is similar to
.Dv LOCAL_CREDS ,
except that socket credentials are passed on every read from a
.Dv SOCK_STREAM
or
.Dv SOCK_SEQPACKET
socket, instead of just the first read.
The
.Dv LOCAL_CREDS
and
.Dv LOCAL_CREDS_PERSISTENT
options are mutually exclusive.
.It Dv LOCAL_CONNWAIT
Used with
.Dv SOCK_STREAM

View File

@ -1040,7 +1040,7 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
break;
}
if (unp2->unp_flags & UNP_WANTCRED)
if (unp2->unp_flags & UNP_WANTCRED_MASK)
control = unp_addsockcred(td, control);
if (unp->unp_addr != NULL)
from = (struct sockaddr *)unp->unp_addr;
@ -1094,12 +1094,13 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
break;
}
SOCKBUF_LOCK(&so2->so_rcv);
if (unp2->unp_flags & UNP_WANTCRED) {
if (unp2->unp_flags & UNP_WANTCRED_MASK) {
/*
* Credentials are passed only once on SOCK_STREAM
* and SOCK_SEQPACKET.
* Credentials are passed only once on SOCK_STREAM and
* SOCK_SEQPACKET (LOCAL_CREDS => WANTCRED_ONESHOT), or
* forever (LOCAL_CREDS_PERSISTENT => WANTCRED_ALWAYS).
*/
unp2->unp_flags &= ~UNP_WANTCRED;
unp2->unp_flags &= ~UNP_WANTCRED_ONESHOT;
control = unp_addsockcred(td, control);
}
@ -1405,7 +1406,13 @@ uipc_ctloutput(struct socket *so, struct sockopt *sopt)
case LOCAL_CREDS:
/* Unlocked read. */
optval = unp->unp_flags & UNP_WANTCRED ? 1 : 0;
optval = unp->unp_flags & UNP_WANTCRED_ONESHOT ? 1 : 0;
error = sooptcopyout(sopt, &optval, sizeof(optval));
break;
case LOCAL_CREDS_PERSISTENT:
/* Unlocked read. */
optval = unp->unp_flags & UNP_WANTCRED_ALWAYS ? 1 : 0;
error = sooptcopyout(sopt, &optval, sizeof(optval));
break;
@ -1424,28 +1431,38 @@ uipc_ctloutput(struct socket *so, struct sockopt *sopt)
case SOPT_SET:
switch (sopt->sopt_name) {
case LOCAL_CREDS:
case LOCAL_CREDS_PERSISTENT:
case LOCAL_CONNWAIT:
error = sooptcopyin(sopt, &optval, sizeof(optval),
sizeof(optval));
if (error)
break;
#define OPTSET(bit) do { \
#define OPTSET(bit, exclusive) do { \
UNP_PCB_LOCK(unp); \
if (optval) \
unp->unp_flags |= bit; \
else \
unp->unp_flags &= ~bit; \
if (optval) { \
if ((unp->unp_flags & (exclusive)) != 0) { \
UNP_PCB_UNLOCK(unp); \
error = EINVAL; \
break; \
} \
unp->unp_flags |= (bit); \
} else \
unp->unp_flags &= ~(bit); \
UNP_PCB_UNLOCK(unp); \
} while (0)
switch (sopt->sopt_name) {
case LOCAL_CREDS:
OPTSET(UNP_WANTCRED);
OPTSET(UNP_WANTCRED_ONESHOT, UNP_WANTCRED_ALWAYS);
break;
case LOCAL_CREDS_PERSISTENT:
OPTSET(UNP_WANTCRED_ALWAYS, UNP_WANTCRED_ONESHOT);
break;
case LOCAL_CONNWAIT:
OPTSET(UNP_CONNWAIT);
OPTSET(UNP_CONNWAIT, 0);
break;
default:
@ -1651,8 +1668,7 @@ unp_copy_peercred(struct thread *td, struct unpcb *client_unp,
memcpy(&server_unp->unp_peercred, &listen_unp->unp_peercred,
sizeof(server_unp->unp_peercred));
server_unp->unp_flags |= UNP_HAVEPC;
if (listen_unp->unp_flags & UNP_WANTCRED)
client_unp->unp_flags |= UNP_WANTCRED;
client_unp->unp_flags |= (listen_unp->unp_flags & UNP_WANTCRED_MASK);
}
static int
@ -2853,8 +2869,12 @@ db_print_unpflags(int unp_flags)
db_printf("%sUNP_HAVEPC", comma ? ", " : "");
comma = 1;
}
if (unp_flags & UNP_WANTCRED) {
db_printf("%sUNP_WANTCRED", comma ? ", " : "");
if (unp_flags & UNP_WANTCRED_ALWAYS) {
db_printf("%sUNP_WANTCRED_ALWAYS", comma ? ", " : "");
comma = 1;
}
if (unp_flags & UNP_WANTCRED_ONESHOT) {
db_printf("%sUNP_WANTCRED_ONESHOT", comma ? ", " : "");
comma = 1;
}
if (unp_flags & UNP_CONNWAIT) {

View File

@ -67,6 +67,7 @@ struct sockaddr_un {
/* Socket options. */
#define LOCAL_PEERCRED 1 /* retrieve peer credentials */
#define LOCAL_CREDS 2 /* pass credentials to receiver */
#define LOCAL_CREDS_PERSISTENT 3 /* pass credentials to receiver */
#define LOCAL_CONNWAIT 4 /* connects block until accepted */
/* Start of reserved space for third-party socket options. */

View File

@ -107,10 +107,13 @@ struct unpcb {
* to determine whether the contents should be sent to the user or
* not.
*/
#define UNP_HAVEPC 0x001
#define UNP_WANTCRED 0x004 /* credentials wanted */
#define UNP_HAVEPC 0x001
#define UNP_WANTCRED_ALWAYS 0x002 /* credentials wanted always */
#define UNP_WANTCRED_ONESHOT 0x004 /* credentials wanted once */
#define UNP_CONNWAIT 0x008 /* connect blocks until accepted */
#define UNP_WANTCRED_MASK (UNP_WANTCRED_ONESHOT | UNP_WANTCRED_ALWAYS)
/*
* These flags are used to handle non-atomicity in connect() and bind()
* operations on a socket: in particular, to avoid races between multiple