Implement passing credentials as ancillary data on Unix domain sockets,

enabled with the LOCAL_CREDS socket option on the listener.  Semantics are
similar to BSD/OS's:
- Creds are available with first data on SOCK_STREAM, and with every datagram
  on SOCK_DGRAM.
- It is not possible to forge credentials.

Different in that:
- Different credential data structure (ours does not rely on the format
  of internal kernel data structures, and does not pass the login name).
- We can pass creds and file descriptors at the same time (this does not
  work in BSD/OS).

Luke Mewburn <lukem@netbsd.org> gets credit for inspiring me to implement
this.  :-)
This commit is contained in:
thorpej 1998-01-07 22:57:09 +00:00
parent 11cfadcaa1
commit cc22126721
1 changed files with 188 additions and 3 deletions

View File

@ -1,4 +1,41 @@
/* $NetBSD: uipc_usrreq.c,v 1.29 1998/01/07 04:03:38 thorpej Exp $ */
/* $NetBSD: uipc_usrreq.c,v 1.30 1998/01/07 22:57:09 thorpej Exp $ */
/*-
* Copyright (c) 1998 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
* NASA Ames Research Center.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
*/
/*
* Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
@ -63,10 +100,13 @@
struct sockaddr_un sun_noname = { sizeof(sun_noname), AF_UNIX };
ino_t unp_ino; /* prototype for fake inode numbers */
struct mbuf *unp_addsockcred __P((struct proc *, struct mbuf *));
int
unp_output(m, control, unp)
unp_output(m, control, unp, p)
struct mbuf *m, *control;
struct unpcb *unp;
struct proc *p;
{
struct socket *so2;
struct sockaddr_un *sun;
@ -76,6 +116,8 @@ unp_output(m, control, unp)
sun = unp->unp_addr;
else
sun = &sun_noname;
if (unp->unp_conn->unp_flags & UNP_WANTCRED)
control = unp_addsockcred(p, control);
if (sbappendaddr(&so2->so_rcv, (struct sockaddr *)sun, m,
control) == 0) {
m_freem(control);
@ -221,6 +263,12 @@ uipc_usrreq(so, req, m, nam, control, p)
break;
case PRU_SEND:
/*
* Note: unp_internalize() rejects any control message
* other than SCM_RIGHTS, and only allows one. This
* has the side-effect of preventing a caller from
* forging SCM_CREDS.
*/
if (control && (error = unp_internalize(control, p)))
break;
switch (so->so_type) {
@ -244,7 +292,7 @@ uipc_usrreq(so, req, m, nam, control, p)
goto die;
}
}
error = unp_output(m, control, unp);
error = unp_output(m, control, unp, p);
if (nam)
unp_disconnect(unp);
break;
@ -256,6 +304,14 @@ uipc_usrreq(so, req, m, nam, control, p)
if (unp->unp_conn == 0)
panic("uipc 3");
so2 = unp->unp_conn->unp_socket;
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(p, control);
}
/*
* Send to paired receive port, and then reduce
* send buffer hiwater marks to maintain backpressure.
@ -326,6 +382,82 @@ release:
return (error);
}
/*
* Unix domain socket option processing.
*/
int
uipc_ctloutput(op, so, level, optname, mp)
int op;
struct socket *so;
int level, optname;
struct mbuf **mp;
{
struct unpcb *unp = sotounpcb(so);
struct mbuf *m = *mp;
int optval = 0, error = 0;
if (level != 0) {
error = EINVAL;
if (op == PRCO_SETOPT && m)
(void) m_free(m);
} else switch (op) {
case PRCO_SETOPT:
switch (optname) {
case LOCAL_CREDS:
if (m == NULL || m->m_len != sizeof(int))
error = EINVAL;
else {
optval = *mtod(m, int *);
switch (optname) {
#define OPTSET(bit) \
if (optval) \
unp->unp_flags |= (bit); \
else \
unp->unp_flags &= ~(bit);
case LOCAL_CREDS:
OPTSET(UNP_WANTCRED);
break;
}
}
break;
#undef OPTSET
default:
error = ENOPROTOOPT;
break;
}
if (m)
(void) m_free(m);
break;
case PRCO_GETOPT:
switch (optname) {
case LOCAL_CREDS:
*mp = m = m_get(M_WAIT, MT_SOOPTS);
m->m_len = sizeof(int);
switch (optname) {
#define OPTBIT(bit) (unp->unp_flags & (bit) ? 1 : 0)
case LOCAL_CREDS:
optval = OPTBIT(UNP_WANTCRED);
break;
}
*mtod(m, int *) = optval;
break;
#undef OPTBIT
default:
error = ENOPROTOOPT;
break;
}
break;
}
return (error);
}
/*
* Both send and receive buffers are allocated PIPSIZ bytes of buffering
* for stream sockets, although the total for sender and receiver is
@ -534,6 +666,7 @@ unp_connect(so, nam, p)
unp2->unp_addrlen);
unp3->unp_addrlen = unp2->unp_addrlen;
}
unp3->unp_flags = unp2->unp_flags;
so2 = so3;
}
error = unp_connect2(so, so2);
@ -775,6 +908,58 @@ morespace:
return (0);
}
struct mbuf *
unp_addsockcred(p, control)
struct proc *p;
struct mbuf *control;
{
struct cmsghdr *cmp;
struct sockcred *sc;
struct mbuf *m, *n;
int len, i;
len = sizeof(struct cmsghdr) + SOCKCREDSIZE(p->p_ucred->cr_ngroups);
m = m_get(M_WAIT, MT_CONTROL);
if (len > MLEN) {
if (len > MCLBYTES)
MEXTMALLOC(m, len, M_WAITOK);
else
MCLGET(m, M_WAIT);
if ((m->m_flags & M_EXT) == 0) {
m_free(m);
return (control);
}
}
m->m_len = len;
m->m_next = NULL;
cmp = mtod(m, struct cmsghdr *);
sc = (struct sockcred *)CMSG_DATA(cmp);
cmp->cmsg_len = len;
cmp->cmsg_level = SOL_SOCKET;
cmp->cmsg_type = SCM_CREDS;
sc->sc_uid = p->p_cred->p_ruid;
sc->sc_euid = p->p_ucred->cr_uid;
sc->sc_gid = p->p_cred->p_rgid;
sc->sc_egid = p->p_ucred->cr_gid;
sc->sc_ngroups = p->p_ucred->cr_ngroups;
for (i = 0; i < sc->sc_ngroups; i++)
sc->sc_groups[i] = p->p_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);
}
int unp_defer, unp_gcing;
extern struct domain unixdomain;