summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--share/man/man4/unix.450
-rw-r--r--sys/kern/uipc_usrreq.c120
-rw-r--r--sys/sys/socket.h19
-rw-r--r--sys/sys/un.h4
-rw-r--r--sys/sys/unpcb.h2
5 files changed, 179 insertions, 16 deletions
diff --git a/share/man/man4/unix.4 b/share/man/man4/unix.4
index a192fa0..c556185 100644
--- a/share/man/man4/unix.4
+++ b/share/man/man4/unix.4
@@ -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
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 256c4fb..e3ebf9b 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -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 @@ bad:
}
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 @@ out:
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
diff --git a/sys/sys/socket.h b/sys/sys/socket.h
index 27e5294..176343d 100644
--- a/sys/sys/socket.h
+++ b/sys/sys/socket.h
@@ -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 */
diff --git a/sys/sys/un.h b/sys/sys/un.h
index e8caf35..97aebb5 100644
--- a/sys/sys/un.h
+++ b/sys/sys/un.h
@@ -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;
diff --git a/sys/sys/unpcb.h b/sys/sys/unpcb.h
index b630570..a1186de 100644
--- a/sys/sys/unpcb.h
+++ b/sys/sys/unpcb.h
@@ -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))
OpenPOWER on IntegriCloud