From cc2212672110edda71758238f7cd994d3b4e0a12 Mon Sep 17 00:00:00 2001 From: thorpej Date: Wed, 7 Jan 1998 22:57:09 +0000 Subject: [PATCH] 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 gets credit for inspiring me to implement this. :-) --- sys/kern/uipc_usrreq.c | 191 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 188 insertions(+), 3 deletions(-) diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 65918a95dbc8..8302eeffa500 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -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;