mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-15 15:06:42 +00:00
Implement unix(4) socket options LOCAL_CREDS and LOCAL_CONNWAIT.
- Add unp_addsockcred() (for LOCAL_CREDS). - Add an argument to unp_connect2() to differentiate between PRU_CONNECT and PRU_CONNECT2. (for LOCAL_CONNWAIT) Obtained from: NetBSD (with some changes)
This commit is contained in:
parent
87efd4d58a
commit
6a2989fd54
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=144978
@ -194,6 +194,56 @@ system call (e.g.,
|
||||
or
|
||||
.Xr listen 2 )
|
||||
under different effective credentials.
|
||||
.Pp
|
||||
|
||||
.Tn UNIX
|
||||
domain sockets support a number of socket options which can be set with
|
||||
.Xr setsockopt 2
|
||||
and tested with
|
||||
.Xr getsockopt 2 :
|
||||
.Bl -tag -width ".Dv LOCAL_CONNWAIT"
|
||||
.It Dv LOCAL_CREDS
|
||||
This option may be enabled on a
|
||||
.Dv SOCK_DGRAM
|
||||
or a
|
||||
.Dv SOCK_STREAM
|
||||
socket. This option provides a mechanism for the receiver to
|
||||
receive the credentials of the process as a
|
||||
.Xr recvmsg 2
|
||||
control message. The msg_control field in the msghdr structure points
|
||||
to a buffer that contains a cmsghdr structure followed by a variable
|
||||
length sockcred structure, defined in
|
||||
.Pa \*[Lt]sys/socket.h\*[Gt]
|
||||
as follows:
|
||||
.Bd -literal
|
||||
struct sockcred {
|
||||
id_t sc_uid; /* real user id */
|
||||
uid_t sc_euid; /* effective user id */
|
||||
gid_t sc_gid; /* real group id */
|
||||
gid_t sc_egid; /* effective group id */
|
||||
int sc_ngroups; /* number of supplemental groups */
|
||||
gid_t sc_groups[1]; /* variable length */
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The
|
||||
.Fn SOCKCREDSIZE
|
||||
macro computes the size of the sockcred structure for a specified number
|
||||
of groups.
|
||||
The cmsghdr fields have the following values:
|
||||
.Bd -literal
|
||||
cmsg_len = sizeof(struct cmsghdr) + SOCKCREDSIZE(ngroups)
|
||||
cmsg_level = SOL_SOCKET
|
||||
cmsg_type = SCM_CREDS
|
||||
.Ed
|
||||
.It Dv LOCAL_CONNWAIT
|
||||
Used with
|
||||
.Dv SOCK_STREAM
|
||||
sockets, this option causes the
|
||||
.Xr connect 2
|
||||
function to block until
|
||||
.Xr accept 2
|
||||
has been called on the listening socket.
|
||||
.Sh SEE ALSO
|
||||
.Xr socket 2 ,
|
||||
.Xr intro 4
|
||||
|
@ -81,6 +81,7 @@ static struct unp_head unp_shead, unp_dhead;
|
||||
*/
|
||||
static const struct sockaddr sun_noname = { sizeof(sun_noname), AF_LOCAL };
|
||||
static ino_t unp_ino; /* prototype for fake inode numbers */
|
||||
struct mbuf *unp_addsockcred(struct thread *, struct mbuf *);
|
||||
|
||||
/*
|
||||
* Currently, UNIX domain sockets are protected by a single subsystem lock,
|
||||
@ -114,7 +115,7 @@ static int unp_attach(struct socket *);
|
||||
static void unp_detach(struct unpcb *);
|
||||
static int unp_bind(struct unpcb *,struct sockaddr *, struct thread *);
|
||||
static int unp_connect(struct socket *,struct sockaddr *, struct thread *);
|
||||
static int unp_connect2(struct socket *so, struct socket *so2);
|
||||
static int unp_connect2(struct socket *so, struct socket *so2, int);
|
||||
static void unp_disconnect(struct unpcb *);
|
||||
static void unp_shutdown(struct unpcb *);
|
||||
static void unp_drop(struct unpcb *, int);
|
||||
@ -233,7 +234,7 @@ uipc_connect2(struct socket *so1, struct socket *so2)
|
||||
UNP_UNLOCK();
|
||||
return (EINVAL);
|
||||
}
|
||||
error = unp_connect2(so1, so2);
|
||||
error = unp_connect2(so1, so2, PRU_CONNECT2);
|
||||
UNP_UNLOCK();
|
||||
return (error);
|
||||
}
|
||||
@ -421,6 +422,8 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
|
||||
from = (struct sockaddr *)unp->unp_addr;
|
||||
else
|
||||
from = &sun_noname;
|
||||
if (unp->unp_conn->unp_flags & UNP_WANTCRED)
|
||||
control = unp_addsockcred(td, control);
|
||||
SOCKBUF_LOCK(&so2->so_rcv);
|
||||
if (sbappendaddr_locked(&so2->so_rcv, from, m, control)) {
|
||||
sorwakeup_locked(so2);
|
||||
@ -462,6 +465,14 @@ uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
|
||||
panic("uipc_send connected but no connection?");
|
||||
so2 = unp->unp_conn->unp_socket;
|
||||
SOCKBUF_LOCK(&so2->so_rcv);
|
||||
if (unp->unp_conn->unp_flags & UNP_WANTCRED) {
|
||||
/*
|
||||
* Credentials are passed only once on
|
||||
* SOCK_STREAM.
|
||||
*/
|
||||
unp->unp_conn->unp_flags &= ~UNP_WANTCRED;
|
||||
control = unp_addsockcred(td, control);
|
||||
}
|
||||
/*
|
||||
* Send to paired receive port, and then reduce
|
||||
* send buffer hiwater marks to maintain backpressure.
|
||||
@ -604,20 +615,20 @@ uipc_ctloutput(struct socket *so, struct sockopt *sopt)
|
||||
{
|
||||
struct unpcb *unp;
|
||||
struct xucred xu;
|
||||
int error;
|
||||
int error, optval;
|
||||
|
||||
UNP_LOCK();
|
||||
unp = sotounpcb(so);
|
||||
if (unp == NULL) {
|
||||
UNP_UNLOCK();
|
||||
return (EINVAL);
|
||||
}
|
||||
error = 0;
|
||||
|
||||
switch (sopt->sopt_dir) {
|
||||
case SOPT_GET:
|
||||
switch (sopt->sopt_name) {
|
||||
case LOCAL_PEERCRED:
|
||||
error = 0;
|
||||
UNP_LOCK();
|
||||
unp = sotounpcb(so);
|
||||
if (unp == NULL) {
|
||||
UNP_UNLOCK();
|
||||
error = EINVAL;
|
||||
break;
|
||||
}
|
||||
if (unp->unp_flags & UNP_HAVEPC)
|
||||
xu = unp->unp_peercred;
|
||||
else {
|
||||
@ -626,20 +637,58 @@ uipc_ctloutput(struct socket *so, struct sockopt *sopt)
|
||||
else
|
||||
error = EINVAL;
|
||||
}
|
||||
UNP_UNLOCK();
|
||||
if (error == 0)
|
||||
error = sooptcopyout(sopt, &xu, sizeof(xu));
|
||||
break;
|
||||
case LOCAL_CREDS:
|
||||
optval = unp->unp_flags & UNP_WANTCRED ? 1 : 0;
|
||||
error = sooptcopyout(sopt, &optval, sizeof(optval));
|
||||
break;
|
||||
case LOCAL_CONNWAIT:
|
||||
optval = unp->unp_flags & UNP_CONNWAIT ? 1 : 0;
|
||||
error = sooptcopyout(sopt, &optval, sizeof(optval));
|
||||
break;
|
||||
default:
|
||||
error = EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SOPT_SET:
|
||||
switch (sopt->sopt_name) {
|
||||
case LOCAL_CREDS:
|
||||
case LOCAL_CONNWAIT:
|
||||
error = sooptcopyin(sopt, &optval, sizeof(optval),
|
||||
sizeof(optval));
|
||||
if (error)
|
||||
break;
|
||||
|
||||
#define OPTSET(bit) \
|
||||
if (optval) \
|
||||
unp->unp_flags |= bit; \
|
||||
else \
|
||||
unp->unp_flags &= ~bit;
|
||||
|
||||
switch (sopt->sopt_name) {
|
||||
case LOCAL_CREDS:
|
||||
OPTSET(UNP_WANTCRED);
|
||||
break;
|
||||
case LOCAL_CONNWAIT:
|
||||
OPTSET(UNP_CONNWAIT);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#undef OPTSET
|
||||
default:
|
||||
error = ENOPROTOOPT;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
error = EOPNOTSUPP;
|
||||
break;
|
||||
}
|
||||
UNP_UNLOCK();
|
||||
return (error);
|
||||
}
|
||||
|
||||
@ -965,7 +1014,7 @@ unp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||
|
||||
so2 = so3;
|
||||
}
|
||||
error = unp_connect2(so, so2);
|
||||
error = unp_connect2(so, so2, PRU_CONNECT);
|
||||
bad2:
|
||||
UNP_UNLOCK();
|
||||
mtx_lock(&Giant);
|
||||
@ -980,7 +1029,7 @@ unp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
||||
}
|
||||
|
||||
static int
|
||||
unp_connect2(struct socket *so, struct socket *so2)
|
||||
unp_connect2(struct socket *so, struct socket *so2, int req)
|
||||
{
|
||||
struct unpcb *unp = sotounpcb(so);
|
||||
struct unpcb *unp2;
|
||||
@ -1000,7 +1049,11 @@ unp_connect2(struct socket *so, struct socket *so2)
|
||||
|
||||
case SOCK_STREAM:
|
||||
unp2->unp_conn = unp;
|
||||
soisconnected(so);
|
||||
if (req == PRU_CONNECT &&
|
||||
((unp->unp_flags | unp2->unp_flags) & UNP_CONNWAIT))
|
||||
soisconnecting(so);
|
||||
else
|
||||
soisconnected(so);
|
||||
soisconnected(so2);
|
||||
break;
|
||||
|
||||
@ -1484,6 +1537,43 @@ unp_internalize(struct mbuf **controlp, struct thread *td)
|
||||
return (error);
|
||||
}
|
||||
|
||||
struct mbuf *
|
||||
unp_addsockcred(struct thread *td, struct mbuf *control)
|
||||
{
|
||||
struct mbuf *m, *n;
|
||||
struct sockcred *sc;
|
||||
int ngroups;
|
||||
int i;
|
||||
|
||||
ngroups = MIN(td->td_ucred->cr_ngroups, CMGROUP_MAX);
|
||||
|
||||
m = sbcreatecontrol(NULL, SOCKCREDSIZE(ngroups), SCM_CREDS, SOL_SOCKET);
|
||||
if (m == NULL)
|
||||
return (control);
|
||||
m->m_next = NULL;
|
||||
|
||||
sc = (struct sockcred *) CMSG_DATA(mtod(m, struct cmsghdr *));
|
||||
sc->sc_uid = td->td_ucred->cr_ruid;
|
||||
sc->sc_euid = td->td_ucred->cr_uid;
|
||||
sc->sc_gid = td->td_ucred->cr_rgid;
|
||||
sc->sc_egid = td->td_ucred->cr_gid;
|
||||
sc->sc_ngroups = ngroups;
|
||||
for (i = 0; i < sc->sc_ngroups; i++)
|
||||
sc->sc_groups[i] = td->td_ucred->cr_groups[i];
|
||||
|
||||
/*
|
||||
* If a control message already exists, append us to the end.
|
||||
*/
|
||||
if (control != NULL) {
|
||||
for (n = control; n->m_next != NULL; n = n->m_next)
|
||||
;
|
||||
n->m_next = m;
|
||||
} else
|
||||
control = m;
|
||||
|
||||
return (control);
|
||||
}
|
||||
|
||||
/*
|
||||
* unp_defer is thread-local during garbage collection, and does not require
|
||||
* explicit synchronization. unp_gcing prevents other threads from entering
|
||||
|
@ -439,6 +439,25 @@ struct cmsgcred {
|
||||
short cmcred_ngroups; /* number or groups */
|
||||
gid_t cmcred_groups[CMGROUP_MAX]; /* groups */
|
||||
};
|
||||
|
||||
/*
|
||||
* Socket credentials.
|
||||
*/
|
||||
struct sockcred {
|
||||
uid_t sc_uid; /* real user id */
|
||||
uid_t sc_euid; /* effective user id */
|
||||
gid_t sc_gid; /* real group id */
|
||||
gid_t sc_egid; /* effective group id */
|
||||
int sc_ngroups; /* number of supplemental groups */
|
||||
gid_t sc_groups[1]; /* variable length */
|
||||
};
|
||||
|
||||
/*
|
||||
* Compute size of a sockcred structure with groups.
|
||||
*/
|
||||
#define SOCKCREDSIZE(ngrps) \
|
||||
(sizeof(struct sockcred) + (sizeof(gid_t) * ((ngrps) - 1)))
|
||||
|
||||
#endif /* __BSD_VISIBLE */
|
||||
|
||||
/* given pointer to struct cmsghdr, return pointer to data */
|
||||
|
@ -53,7 +53,9 @@ struct sockaddr_un {
|
||||
#if __BSD_VISIBLE
|
||||
|
||||
/* Socket options. */
|
||||
#define LOCAL_PEERCRED 0x001 /* retrieve peer credentails */
|
||||
#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */
|
||||
#define LOCAL_CREDS 0x002 /* pass credentials to receiver */
|
||||
#define LOCAL_CONNWAIT 0x004 /* connects block until accepted */
|
||||
|
||||
#ifdef _KERNEL
|
||||
struct mbuf;
|
||||
|
@ -95,6 +95,8 @@ struct unpcb {
|
||||
*/
|
||||
#define UNP_HAVEPC 0x001
|
||||
#define UNP_HAVEPCCACHED 0x002
|
||||
#define UNP_WANTCRED 0x004 /* credentials wanted */
|
||||
#define UNP_CONNWAIT 0x008 /* connect blocks until accepted */
|
||||
|
||||
#define sotounpcb(so) ((struct unpcb *)((so)->so_pcb))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user