1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1982, 1986, 1989, 1991, 1993
|
|
|
|
* The Regents of the University of California. All rights reserved.
|
|
|
|
*
|
|
|
|
* 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 University of
|
|
|
|
* California, Berkeley and its contributors.
|
|
|
|
* 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
|
|
|
|
*
|
1995-05-11 00:13:26 +00:00
|
|
|
* From: @(#)uipc_usrreq.c 8.3 (Berkeley) 1/4/94
|
1999-08-28 01:08:13 +00:00
|
|
|
* $FreeBSD$
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/kernel.h>
|
1997-03-23 03:37:54 +00:00
|
|
|
#include <sys/fcntl.h>
|
2001-05-01 08:13:21 +00:00
|
|
|
#include <sys/domain.h>
|
|
|
|
#include <sys/filedesc.h>
|
|
|
|
#include <sys/lock.h>
|
1997-11-23 10:43:49 +00:00
|
|
|
#include <sys/malloc.h> /* XXX must be before <sys/file.h> */
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/file.h>
|
2001-05-01 08:13:21 +00:00
|
|
|
#include <sys/mutex.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/proc.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/protosw.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
2000-06-22 22:27:16 +00:00
|
|
|
#include <sys/resourcevar.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/sysctl.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/un.h>
|
1998-05-15 20:11:40 +00:00
|
|
|
#include <sys/unpcb.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/vnode.h>
|
2001-02-21 06:39:57 +00:00
|
|
|
#include <sys/jail.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1998-05-15 20:11:40 +00:00
|
|
|
#include <vm/vm_zone.h>
|
|
|
|
|
1999-04-12 14:34:52 +00:00
|
|
|
static struct vm_zone *unp_zone;
|
1998-05-15 20:11:40 +00:00
|
|
|
static unp_gen_t unp_gencnt;
|
|
|
|
static u_int unp_count;
|
|
|
|
|
|
|
|
static struct unp_head unp_shead, unp_dhead;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Unix communications domain.
|
|
|
|
*
|
|
|
|
* TODO:
|
|
|
|
* SEQPACKET, RDM
|
|
|
|
* rethink name space problems
|
|
|
|
* need a proper out-of-band
|
1998-05-15 20:11:40 +00:00
|
|
|
* lock pushdown
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
1995-12-14 09:55:16 +00:00
|
|
|
static struct sockaddr sun_noname = { sizeof(sun_noname), AF_LOCAL };
|
|
|
|
static ino_t unp_ino; /* prototype for fake inode numbers */
|
|
|
|
|
|
|
|
static int unp_attach __P((struct socket *));
|
|
|
|
static void unp_detach __P((struct unpcb *));
|
1997-08-16 19:16:27 +00:00
|
|
|
static int unp_bind __P((struct unpcb *,struct sockaddr *, struct proc *));
|
|
|
|
static int unp_connect __P((struct socket *,struct sockaddr *,
|
|
|
|
struct proc *));
|
1995-12-14 09:55:16 +00:00
|
|
|
static void unp_disconnect __P((struct unpcb *));
|
|
|
|
static void unp_shutdown __P((struct unpcb *));
|
|
|
|
static void unp_drop __P((struct unpcb *, int));
|
|
|
|
static void unp_gc __P((void));
|
|
|
|
static void unp_scan __P((struct mbuf *, void (*)(struct file *)));
|
|
|
|
static void unp_mark __P((struct file *));
|
|
|
|
static void unp_discard __P((struct file *));
|
|
|
|
static int unp_internalize __P((struct mbuf *, struct proc *));
|
2001-08-17 22:01:18 +00:00
|
|
|
static int unp_listen __P((struct unpcb *, struct proc *));
|
1995-12-14 09:55:16 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
|
|
|
uipc_abort(struct socket *so)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
unp_drop(unp, ECONNABORTED);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_accept(struct socket *so, struct sockaddr **nam)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pass back name of connected socket,
|
|
|
|
* if it was bound and we are still connected
|
|
|
|
* (our peer may have closed already!).
|
|
|
|
*/
|
|
|
|
if (unp->unp_conn && unp->unp_conn->unp_addr) {
|
1997-08-16 19:16:27 +00:00
|
|
|
*nam = dup_sockaddr((struct sockaddr *)unp->unp_conn->unp_addr,
|
|
|
|
1);
|
1997-04-27 20:01:29 +00:00
|
|
|
} else {
|
1997-08-16 19:16:27 +00:00
|
|
|
*nam = dup_sockaddr((struct sockaddr *)&sun_noname, 1);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1997-04-27 20:01:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
|
|
|
uipc_attach(struct socket *so, int proto, struct proc *p)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp != 0)
|
|
|
|
return EISCONN;
|
|
|
|
return unp_attach(so);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_bind(struct socket *so, struct sockaddr *nam, struct proc *p)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
return unp_bind(unp, nam, p);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_connect(struct socket *so, struct sockaddr *nam, struct proc *p)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
return unp_connect(so, nam, curproc);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
|
|
|
uipc_connect2(struct socket *so1, struct socket *so2)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so1);
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
return unp_connect2(so1, so2);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
/* control is EOPNOTSUPP */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
|
|
|
uipc_detach(struct socket *so)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
|
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
unp_detach(unp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_disconnect(struct socket *so)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
|
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
unp_disconnect(unp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_listen(struct socket *so, struct proc *p)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
|
|
|
|
if (unp == 0 || unp->unp_vnode == 0)
|
|
|
|
return EINVAL;
|
2001-08-17 22:01:18 +00:00
|
|
|
return unp_listen(unp, p);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_peeraddr(struct socket *so, struct sockaddr **nam)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
|
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
1997-08-16 19:16:27 +00:00
|
|
|
if (unp->unp_conn && unp->unp_conn->unp_addr)
|
|
|
|
*nam = dup_sockaddr((struct sockaddr *)unp->unp_conn->unp_addr,
|
|
|
|
1);
|
1997-04-27 20:01:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_rcvd(struct socket *so, int flags)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
struct socket *so2;
|
2000-08-29 11:28:06 +00:00
|
|
|
u_long newhiwat;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
switch (so->so_type) {
|
|
|
|
case SOCK_DGRAM:
|
|
|
|
panic("uipc_rcvd DGRAM?");
|
|
|
|
/*NOTREACHED*/
|
|
|
|
|
|
|
|
case SOCK_STREAM:
|
|
|
|
if (unp->unp_conn == 0)
|
|
|
|
break;
|
|
|
|
so2 = unp->unp_conn->unp_socket;
|
|
|
|
/*
|
|
|
|
* Adjust backpressure on sender
|
|
|
|
* and wakeup any waiting to write.
|
|
|
|
*/
|
1999-09-17 21:38:24 +00:00
|
|
|
so2->so_snd.sb_mbmax += unp->unp_mbcnt - so->so_rcv.sb_mbcnt;
|
|
|
|
unp->unp_mbcnt = so->so_rcv.sb_mbcnt;
|
2000-08-29 11:28:06 +00:00
|
|
|
newhiwat = so2->so_snd.sb_hiwat + unp->unp_cc -
|
|
|
|
so->so_rcv.sb_cc;
|
2000-09-05 22:11:13 +00:00
|
|
|
(void)chgsbsize(so2->so_cred->cr_uidinfo, &so2->so_snd.sb_hiwat,
|
2000-08-29 11:28:06 +00:00
|
|
|
newhiwat, RLIM_INFINITY);
|
1999-09-17 21:38:24 +00:00
|
|
|
unp->unp_cc = so->so_rcv.sb_cc;
|
1997-04-27 20:01:29 +00:00
|
|
|
sowwakeup(so2);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
default:
|
|
|
|
panic("uipc_rcvd unknown socktype");
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
/* pru_rcvoob is EOPNOTSUPP */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
|
1997-04-27 20:01:29 +00:00
|
|
|
struct mbuf *control, struct proc *p)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
struct socket *so2;
|
2000-08-29 11:28:06 +00:00
|
|
|
u_long newhiwat;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp == 0) {
|
|
|
|
error = EINVAL;
|
|
|
|
goto release;
|
|
|
|
}
|
|
|
|
if (flags & PRUS_OOB) {
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
goto release;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (control && (error = unp_internalize(control, p)))
|
|
|
|
goto release;
|
|
|
|
|
|
|
|
switch (so->so_type) {
|
|
|
|
case SOCK_DGRAM:
|
|
|
|
{
|
|
|
|
struct sockaddr *from;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (nam) {
|
|
|
|
if (unp->unp_conn) {
|
|
|
|
error = EISCONN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
error = unp_connect(so, nam, p);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
if (unp->unp_conn == 0) {
|
|
|
|
error = ENOTCONN;
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
}
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
so2 = unp->unp_conn->unp_socket;
|
|
|
|
if (unp->unp_addr)
|
1997-08-16 19:16:27 +00:00
|
|
|
from = (struct sockaddr *)unp->unp_addr;
|
1997-04-27 20:01:29 +00:00
|
|
|
else
|
|
|
|
from = &sun_noname;
|
|
|
|
if (sbappendaddr(&so2->so_rcv, from, m, control)) {
|
1994-05-24 10:09:53 +00:00
|
|
|
sorwakeup(so2);
|
|
|
|
m = 0;
|
1997-04-27 20:01:29 +00:00
|
|
|
control = 0;
|
|
|
|
} else
|
|
|
|
error = ENOBUFS;
|
|
|
|
if (nam)
|
|
|
|
unp_disconnect(unp);
|
|
|
|
break;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
case SOCK_STREAM:
|
|
|
|
/* Connect if not connected yet. */
|
1995-02-07 02:01:16 +00:00
|
|
|
/*
|
1997-04-27 20:01:29 +00:00
|
|
|
* Note: A better implementation would complain
|
|
|
|
* if not equal to the peer's address.
|
1995-02-07 02:01:16 +00:00
|
|
|
*/
|
1997-04-27 20:01:29 +00:00
|
|
|
if ((so->so_state & SS_ISCONNECTED) == 0) {
|
|
|
|
if (nam) {
|
|
|
|
error = unp_connect(so, nam, p);
|
|
|
|
if (error)
|
|
|
|
break; /* XXX */
|
|
|
|
} else {
|
|
|
|
error = ENOTCONN;
|
|
|
|
break;
|
|
|
|
}
|
1995-02-07 02:01:16 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (so->so_state & SS_CANTSENDMORE) {
|
|
|
|
error = EPIPE;
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp->unp_conn == 0)
|
|
|
|
panic("uipc_send connected but no connection?");
|
|
|
|
so2 = unp->unp_conn->unp_socket;
|
|
|
|
/*
|
|
|
|
* Send to paired receive port, and then reduce
|
|
|
|
* send buffer hiwater marks to maintain backpressure.
|
|
|
|
* Wake up readers.
|
|
|
|
*/
|
|
|
|
if (control) {
|
1999-09-17 21:38:24 +00:00
|
|
|
if (sbappendcontrol(&so2->so_rcv, m, control))
|
1997-04-27 20:01:29 +00:00
|
|
|
control = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
} else
|
1999-09-17 21:38:24 +00:00
|
|
|
sbappend(&so2->so_rcv, m);
|
|
|
|
so->so_snd.sb_mbmax -=
|
|
|
|
so2->so_rcv.sb_mbcnt - unp->unp_conn->unp_mbcnt;
|
|
|
|
unp->unp_conn->unp_mbcnt = so2->so_rcv.sb_mbcnt;
|
2000-08-29 11:28:06 +00:00
|
|
|
newhiwat = so->so_snd.sb_hiwat -
|
|
|
|
(so2->so_rcv.sb_cc - unp->unp_conn->unp_cc);
|
2000-09-05 22:11:13 +00:00
|
|
|
(void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat,
|
2000-08-29 11:28:06 +00:00
|
|
|
newhiwat, RLIM_INFINITY);
|
1999-09-17 21:38:24 +00:00
|
|
|
unp->unp_conn->unp_cc = so2->so_rcv.sb_cc;
|
1997-04-27 20:01:29 +00:00
|
|
|
sorwakeup(so2);
|
|
|
|
m = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
1997-04-27 20:01:29 +00:00
|
|
|
panic("uipc_send unknown socktype");
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1997-04-27 20:01:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* SEND_EOF is equivalent to a SEND followed by
|
|
|
|
* a SHUTDOWN.
|
|
|
|
*/
|
|
|
|
if (flags & PRUS_EOF) {
|
|
|
|
socantsendmore(so);
|
|
|
|
unp_shutdown(unp);
|
|
|
|
}
|
|
|
|
|
1999-05-10 18:09:39 +00:00
|
|
|
if (control && error != 0)
|
|
|
|
unp_dispose(control);
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
release:
|
|
|
|
if (control)
|
|
|
|
m_freem(control);
|
|
|
|
if (m)
|
|
|
|
m_freem(m);
|
1997-04-27 20:01:29 +00:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_sense(struct socket *so, struct stat *sb)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
struct socket *so2;
|
|
|
|
|
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
sb->st_blksize = so->so_snd.sb_hiwat;
|
|
|
|
if (so->so_type == SOCK_STREAM && unp->unp_conn != 0) {
|
|
|
|
so2 = unp->unp_conn->unp_socket;
|
|
|
|
sb->st_blksize += so2->so_rcv.sb_cc;
|
|
|
|
}
|
Divorce "dev_t" from the "major|minor" bitmap, which is now called
udev_t in the kernel but still called dev_t in userland.
Provide functions to manipulate both types:
major() umajor()
minor() uminor()
makedev() umakedev()
dev2udev() udev2dev()
For now they're functions, they will become in-line functions
after one of the next two steps in this process.
Return major/minor/makedev to macro-hood for userland.
Register a name in cdevsw[] for the "filedescriptor" driver.
In the kernel the udev_t appears in places where we have the
major/minor number combination, (ie: a potential device: we
may not have the driver nor the device), like in inodes, vattr,
cdevsw registration and so on, whereas the dev_t appears where
we carry around a reference to a actual device.
In the future the cdevsw and the aliased-from vnode will be hung
directly from the dev_t, along with up to two softc pointers for
the device driver and a few houskeeping bits. This will essentially
replace the current "alias" check code (same buck, bigger bang).
A little stunt has been provided to try to catch places where the
wrong type is being used (dev_t vs udev_t), if you see something
not working, #undef DEVT_FASCIST in kern/kern_conf.c and see if
it makes a difference. If it does, please try to track it down
(many hands make light work) or at least try to reproduce it
as simply as possible, and describe how to do that.
Without DEVT_FASCIST I belive this patch is a no-op.
Stylistic/posixoid comments about the userland view of the <sys/*.h>
files welcome now, from userland they now contain the end result.
Next planned step: make all dev_t's refer to the same devsw[] which
means convert BLK's to CHR's at the perimeter of the vnodes and
other places where they enter the game (bootdev, mknod, sysctl).
1999-05-11 19:55:07 +00:00
|
|
|
sb->st_dev = NOUDEV;
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp->unp_ino == 0)
|
|
|
|
unp->unp_ino = unp_ino++;
|
|
|
|
sb->st_ino = unp->unp_ino;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_shutdown(struct socket *so)
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
|
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
|
|
|
socantsendmore(so);
|
|
|
|
unp_shutdown(unp);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_sockaddr(struct socket *so, struct sockaddr **nam)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
|
|
|
|
if (unp == 0)
|
|
|
|
return EINVAL;
|
1997-08-16 19:16:27 +00:00
|
|
|
if (unp->unp_addr)
|
|
|
|
*nam = dup_sockaddr((struct sockaddr *)unp->unp_addr, 1);
|
2001-04-24 19:09:23 +00:00
|
|
|
else
|
|
|
|
*nam = dup_sockaddr((struct sockaddr *)&sun_noname, 1);
|
1997-04-27 20:01:29 +00:00
|
|
|
return 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
struct pr_usrreqs uipc_usrreqs = {
|
|
|
|
uipc_abort, uipc_accept, uipc_attach, uipc_bind, uipc_connect,
|
|
|
|
uipc_connect2, pru_control_notsupp, uipc_detach, uipc_disconnect,
|
|
|
|
uipc_listen, uipc_peeraddr, uipc_rcvd, pru_rcvoob_notsupp,
|
|
|
|
uipc_send, uipc_sense, uipc_shutdown, uipc_sockaddr,
|
1997-09-14 02:52:18 +00:00
|
|
|
sosend, soreceive, sopoll
|
1997-04-27 20:01:29 +00:00
|
|
|
};
|
2001-08-17 22:01:18 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
uipc_ctloutput(so, sopt)
|
|
|
|
struct socket *so;
|
|
|
|
struct sockopt *sopt;
|
|
|
|
{
|
|
|
|
struct unpcb *unp = sotounpcb(so);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
switch (sopt->sopt_dir) {
|
|
|
|
case SOPT_GET:
|
|
|
|
switch (sopt->sopt_name) {
|
|
|
|
case LOCAL_PEERCRED:
|
|
|
|
if (unp->unp_flags & UNP_HAVEPC)
|
|
|
|
error = sooptcopyout(sopt, &unp->unp_peercred,
|
|
|
|
sizeof(unp->unp_peercred));
|
|
|
|
else {
|
|
|
|
if (so->so_type == SOCK_STREAM)
|
|
|
|
error = ENOTCONN;
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case SOPT_SET:
|
|
|
|
default:
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
1997-04-27 20:01:29 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
|
|
|
* Both send and receive buffers are allocated PIPSIZ bytes of buffering
|
|
|
|
* for stream sockets, although the total for sender and receiver is
|
|
|
|
* actually only PIPSIZ.
|
|
|
|
* Datagram sockets really use the sendspace as the maximum datagram size,
|
|
|
|
* and don't really want to reserve the sendspace. Their recvspace should
|
|
|
|
* be large enough for at least one max-size datagram plus address.
|
|
|
|
*/
|
1995-08-31 01:39:31 +00:00
|
|
|
#ifndef PIPSIZ
|
|
|
|
#define PIPSIZ 8192
|
|
|
|
#endif
|
1995-12-14 09:55:16 +00:00
|
|
|
static u_long unpst_sendspace = PIPSIZ;
|
|
|
|
static u_long unpst_recvspace = PIPSIZ;
|
|
|
|
static u_long unpdg_sendspace = 2*1024; /* really max datagram size */
|
|
|
|
static u_long unpdg_recvspace = 4*1024;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int unp_rights; /* file descriptors in flight */
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1999-02-16 10:49:55 +00:00
|
|
|
SYSCTL_DECL(_net_local_stream);
|
1997-02-24 20:30:58 +00:00
|
|
|
SYSCTL_INT(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW,
|
|
|
|
&unpst_sendspace, 0, "");
|
|
|
|
SYSCTL_INT(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW,
|
|
|
|
&unpst_recvspace, 0, "");
|
1999-02-16 10:49:55 +00:00
|
|
|
SYSCTL_DECL(_net_local_dgram);
|
1997-02-24 20:30:58 +00:00
|
|
|
SYSCTL_INT(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW,
|
|
|
|
&unpdg_sendspace, 0, "");
|
|
|
|
SYSCTL_INT(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW,
|
|
|
|
&unpdg_recvspace, 0, "");
|
1999-02-16 10:49:55 +00:00
|
|
|
SYSCTL_DECL(_net_local);
|
1997-02-24 20:30:58 +00:00
|
|
|
SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, "");
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_attach(so)
|
|
|
|
struct socket *so;
|
|
|
|
{
|
|
|
|
register struct unpcb *unp;
|
|
|
|
int error;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
|
|
|
|
switch (so->so_type) {
|
|
|
|
|
|
|
|
case SOCK_STREAM:
|
|
|
|
error = soreserve(so, unpst_sendspace, unpst_recvspace);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_DGRAM:
|
|
|
|
error = soreserve(so, unpdg_sendspace, unpdg_recvspace);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("unp_attach");
|
|
|
|
}
|
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
1998-05-15 20:11:40 +00:00
|
|
|
unp = zalloc(unp_zone);
|
1997-08-16 19:16:27 +00:00
|
|
|
if (unp == NULL)
|
1994-05-24 10:09:53 +00:00
|
|
|
return (ENOBUFS);
|
1997-08-16 19:16:27 +00:00
|
|
|
bzero(unp, sizeof *unp);
|
1998-05-15 20:11:40 +00:00
|
|
|
unp->unp_gencnt = ++unp_gencnt;
|
|
|
|
unp_count++;
|
|
|
|
LIST_INIT(&unp->unp_refs);
|
1994-05-24 10:09:53 +00:00
|
|
|
unp->unp_socket = so;
|
This Implements the mumbled about "Jail" feature.
This is a seriously beefed up chroot kind of thing. The process
is jailed along the same lines as a chroot does it, but with
additional tough restrictions imposed on what the superuser can do.
For all I know, it is safe to hand over the root bit inside a
prison to the customer living in that prison, this is what
it was developed for in fact: "real virtual servers".
Each prison has an ip number associated with it, which all IP
communications will be coerced to use and each prison has its own
hostname.
Needless to say, you need more RAM this way, but the advantage is
that each customer can run their own particular version of apache
and not stomp on the toes of their neighbors.
It generally does what one would expect, but setting up a jail
still takes a little knowledge.
A few notes:
I have no scripts for setting up a jail, don't ask me for them.
The IP number should be an alias on one of the interfaces.
mount a /proc in each jail, it will make ps more useable.
/proc/<pid>/status tells the hostname of the prison for
jailed processes.
Quotas are only sensible if you have a mountpoint per prison.
There are no privisions for stopping resource-hogging.
Some "#ifdef INET" and similar may be missing (send patches!)
If somebody wants to take it from here and develop it into
more of a "virtual machine" they should be most welcome!
Tools, comments, patches & documentation most welcome.
Have fun...
Sponsored by: http://www.rndassociates.com/
Run for almost a year by: http://www.servetheweb.com/
1999-04-28 11:38:52 +00:00
|
|
|
unp->unp_rvnode = curproc->p_fd->fd_rdir;
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? &unp_dhead
|
|
|
|
: &unp_shead, unp, unp_link);
|
|
|
|
so->so_pcb = (caddr_t)unp;
|
1994-05-24 10:09:53 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_detach(unp)
|
|
|
|
register struct unpcb *unp;
|
|
|
|
{
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_REMOVE(unp, unp_link);
|
|
|
|
unp->unp_gencnt = ++unp_gencnt;
|
|
|
|
--unp_count;
|
1994-05-24 10:09:53 +00:00
|
|
|
if (unp->unp_vnode) {
|
|
|
|
unp->unp_vnode->v_socket = 0;
|
|
|
|
vrele(unp->unp_vnode);
|
|
|
|
unp->unp_vnode = 0;
|
|
|
|
}
|
|
|
|
if (unp->unp_conn)
|
|
|
|
unp_disconnect(unp);
|
1999-11-16 10:56:05 +00:00
|
|
|
while (!LIST_EMPTY(&unp->unp_refs))
|
|
|
|
unp_drop(LIST_FIRST(&unp->unp_refs), ECONNRESET);
|
1994-05-24 10:09:53 +00:00
|
|
|
soisdisconnected(unp->unp_socket);
|
|
|
|
unp->unp_socket->so_pcb = 0;
|
|
|
|
if (unp_rights) {
|
|
|
|
/*
|
|
|
|
* Normally the receive buffer is flushed later,
|
|
|
|
* in sofree, but if our receive buffer holds references
|
|
|
|
* to descriptors that are now garbage, we will dispose
|
|
|
|
* of those descriptor references after the garbage collector
|
|
|
|
* gets them (resulting in a "panic: closef: count < 0").
|
|
|
|
*/
|
|
|
|
sorflush(unp->unp_socket);
|
|
|
|
unp_gc();
|
|
|
|
}
|
1997-08-16 19:16:27 +00:00
|
|
|
if (unp->unp_addr)
|
|
|
|
FREE(unp->unp_addr, M_SONAME);
|
1998-05-15 20:11:40 +00:00
|
|
|
zfree(unp_zone, unp);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_bind(unp, nam, p)
|
|
|
|
struct unpcb *unp;
|
1997-08-16 19:16:27 +00:00
|
|
|
struct sockaddr *nam;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct proc *p;
|
|
|
|
{
|
1997-08-16 19:16:27 +00:00
|
|
|
struct sockaddr_un *soun = (struct sockaddr_un *)nam;
|
2000-07-11 22:07:57 +00:00
|
|
|
struct vnode *vp;
|
|
|
|
struct mount *mp;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct vattr vattr;
|
1997-08-16 19:16:27 +00:00
|
|
|
int error, namelen;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct nameidata nd;
|
2001-08-18 02:53:50 +00:00
|
|
|
char *buf;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
|
|
|
if (unp->unp_vnode != NULL)
|
|
|
|
return (EINVAL);
|
1997-08-16 19:16:27 +00:00
|
|
|
namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
|
|
|
|
if (namelen <= 0)
|
|
|
|
return EINVAL;
|
2001-08-18 02:53:50 +00:00
|
|
|
buf = malloc(SOCK_MAXADDRLEN, M_TEMP, M_WAITOK);
|
1997-08-16 19:16:27 +00:00
|
|
|
strncpy(buf, soun->sun_path, namelen);
|
|
|
|
buf[namelen] = 0; /* null-terminate the string */
|
2000-07-11 22:07:57 +00:00
|
|
|
restart:
|
1999-09-29 21:09:41 +00:00
|
|
|
NDINIT(&nd, CREATE, NOFOLLOW | LOCKPARENT, UIO_SYSSPACE,
|
1997-08-16 19:16:27 +00:00
|
|
|
buf, p);
|
1994-05-24 10:09:53 +00:00
|
|
|
/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
|
1994-10-02 17:35:40 +00:00
|
|
|
error = namei(&nd);
|
2001-08-18 02:53:50 +00:00
|
|
|
if (error) {
|
1994-05-24 10:09:53 +00:00
|
|
|
return (error);
|
2001-08-18 02:53:50 +00:00
|
|
|
free(buf, M_TEMP);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
vp = nd.ni_vp;
|
2000-07-11 22:07:57 +00:00
|
|
|
if (vp != NULL || vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) {
|
1999-12-15 23:02:35 +00:00
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (nd.ni_dvp == vp)
|
|
|
|
vrele(nd.ni_dvp);
|
|
|
|
else
|
|
|
|
vput(nd.ni_dvp);
|
2000-07-11 22:07:57 +00:00
|
|
|
if (vp != NULL) {
|
|
|
|
vrele(vp);
|
2001-08-18 02:53:50 +00:00
|
|
|
free(buf, M_TEMP);
|
2000-07-11 22:07:57 +00:00
|
|
|
return (EADDRINUSE);
|
|
|
|
}
|
2001-08-18 02:53:50 +00:00
|
|
|
error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH);
|
|
|
|
if (error) {
|
|
|
|
free(buf, M_TEMP);
|
2000-07-11 22:07:57 +00:00
|
|
|
return (error);
|
2001-08-18 02:53:50 +00:00
|
|
|
}
|
2000-07-11 22:07:57 +00:00
|
|
|
goto restart;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
VATTR_NULL(&vattr);
|
|
|
|
vattr.va_type = VSOCK;
|
1997-04-27 20:01:29 +00:00
|
|
|
vattr.va_mode = (ACCESSPERMS & ~p->p_fd->fd_cmask);
|
1997-02-10 02:22:35 +00:00
|
|
|
VOP_LEASE(nd.ni_dvp, p, p->p_ucred, LEASE_WRITE);
|
1998-05-07 04:58:58 +00:00
|
|
|
error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
|
1999-12-15 23:02:35 +00:00
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
1998-05-07 04:58:58 +00:00
|
|
|
vput(nd.ni_dvp);
|
2001-08-18 02:53:50 +00:00
|
|
|
if (error) {
|
|
|
|
free(buf, M_TEMP);
|
1994-05-24 10:09:53 +00:00
|
|
|
return (error);
|
2001-08-18 02:53:50 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
vp = nd.ni_vp;
|
|
|
|
vp->v_socket = unp->unp_socket;
|
|
|
|
unp->unp_vnode = vp;
|
1997-08-16 19:16:27 +00:00
|
|
|
unp->unp_addr = (struct sockaddr_un *)dup_sockaddr(nam, 1);
|
1997-02-10 02:22:35 +00:00
|
|
|
VOP_UNLOCK(vp, 0, p);
|
2000-07-11 22:07:57 +00:00
|
|
|
vn_finished_write(mp);
|
2001-08-18 02:53:50 +00:00
|
|
|
free(buf, M_TEMP);
|
1994-05-24 10:09:53 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_connect(so, nam, p)
|
|
|
|
struct socket *so;
|
1997-08-16 19:16:27 +00:00
|
|
|
struct sockaddr *nam;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct proc *p;
|
|
|
|
{
|
1997-08-16 19:16:27 +00:00
|
|
|
register struct sockaddr_un *soun = (struct sockaddr_un *)nam;
|
1994-05-24 10:09:53 +00:00
|
|
|
register struct vnode *vp;
|
|
|
|
register struct socket *so2, *so3;
|
2001-08-17 22:01:18 +00:00
|
|
|
struct unpcb *unp, *unp2, *unp3;
|
1997-08-16 19:16:27 +00:00
|
|
|
int error, len;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct nameidata nd;
|
1997-08-16 19:16:27 +00:00
|
|
|
char buf[SOCK_MAXADDRLEN];
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-08-16 19:16:27 +00:00
|
|
|
len = nam->sa_len - offsetof(struct sockaddr_un, sun_path);
|
|
|
|
if (len <= 0)
|
|
|
|
return EINVAL;
|
|
|
|
strncpy(buf, soun->sun_path, len);
|
|
|
|
buf[len] = 0;
|
|
|
|
|
|
|
|
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE, buf, p);
|
1994-10-02 17:35:40 +00:00
|
|
|
error = namei(&nd);
|
|
|
|
if (error)
|
1994-05-24 10:09:53 +00:00
|
|
|
return (error);
|
|
|
|
vp = nd.ni_vp;
|
1999-12-15 23:02:35 +00:00
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
1994-05-24 10:09:53 +00:00
|
|
|
if (vp->v_type != VSOCK) {
|
|
|
|
error = ENOTSOCK;
|
|
|
|
goto bad;
|
|
|
|
}
|
1994-10-02 17:35:40 +00:00
|
|
|
error = VOP_ACCESS(vp, VWRITE, p->p_ucred, p);
|
|
|
|
if (error)
|
1994-05-24 10:09:53 +00:00
|
|
|
goto bad;
|
|
|
|
so2 = vp->v_socket;
|
|
|
|
if (so2 == 0) {
|
|
|
|
error = ECONNREFUSED;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (so->so_type != so2->so_type) {
|
|
|
|
error = EPROTOTYPE;
|
|
|
|
goto bad;
|
|
|
|
}
|
|
|
|
if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
|
|
|
|
if ((so2->so_options & SO_ACCEPTCONN) == 0 ||
|
1999-09-19 02:17:02 +00:00
|
|
|
(so3 = sonewconn3(so2, 0, p)) == 0) {
|
1994-05-24 10:09:53 +00:00
|
|
|
error = ECONNREFUSED;
|
|
|
|
goto bad;
|
|
|
|
}
|
2001-08-17 22:01:18 +00:00
|
|
|
unp = sotounpcb(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
unp2 = sotounpcb(so2);
|
|
|
|
unp3 = sotounpcb(so3);
|
|
|
|
if (unp2->unp_addr)
|
1997-08-16 19:16:27 +00:00
|
|
|
unp3->unp_addr = (struct sockaddr_un *)
|
|
|
|
dup_sockaddr((struct sockaddr *)
|
|
|
|
unp2->unp_addr, 1);
|
2001-08-17 22:01:18 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* unp_peercred management:
|
|
|
|
*
|
|
|
|
* The connecter's (client's) credentials are copied
|
|
|
|
* from its process structure at the time of connect()
|
|
|
|
* (which is now).
|
|
|
|
*/
|
|
|
|
memset(&unp3->unp_peercred, '\0', sizeof(unp3->unp_peercred));
|
|
|
|
unp3->unp_peercred.cr_uid = p->p_ucred->cr_uid;
|
|
|
|
unp3->unp_peercred.cr_ngroups = p->p_ucred->cr_ngroups;
|
|
|
|
memcpy(unp3->unp_peercred.cr_groups, p->p_ucred->cr_groups,
|
|
|
|
sizeof(unp3->unp_peercred.cr_groups));
|
|
|
|
unp3->unp_flags |= UNP_HAVEPC;
|
|
|
|
/*
|
|
|
|
* The receiver's (server's) credentials are copied
|
|
|
|
* from the unp_peercred member of socket on which the
|
|
|
|
* former called listen(); unp_listen() cached that
|
|
|
|
* process's credentials at that time so we can use
|
|
|
|
* them now.
|
|
|
|
*/
|
|
|
|
KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED,
|
|
|
|
("unp_connect: listener without cached peercred"));
|
|
|
|
memcpy(&unp->unp_peercred, &unp2->unp_peercred,
|
|
|
|
sizeof(unp->unp_peercred));
|
|
|
|
unp->unp_flags |= UNP_HAVEPC;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
so2 = so3;
|
|
|
|
}
|
|
|
|
error = unp_connect2(so, so2);
|
|
|
|
bad:
|
|
|
|
vput(vp);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
1994-05-25 09:21:21 +00:00
|
|
|
int
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_connect2(so, so2)
|
|
|
|
register struct socket *so;
|
|
|
|
register struct socket *so2;
|
|
|
|
{
|
|
|
|
register struct unpcb *unp = sotounpcb(so);
|
|
|
|
register struct unpcb *unp2;
|
|
|
|
|
|
|
|
if (so2->so_type != so->so_type)
|
|
|
|
return (EPROTOTYPE);
|
|
|
|
unp2 = sotounpcb(so2);
|
|
|
|
unp->unp_conn = unp2;
|
|
|
|
switch (so->so_type) {
|
|
|
|
|
|
|
|
case SOCK_DGRAM:
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink);
|
1994-05-24 10:09:53 +00:00
|
|
|
soisconnected(so);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_STREAM:
|
|
|
|
unp2->unp_conn = unp;
|
|
|
|
soisconnected(so);
|
|
|
|
soisconnected(so2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("unp_connect2");
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_disconnect(unp)
|
|
|
|
struct unpcb *unp;
|
|
|
|
{
|
|
|
|
register struct unpcb *unp2 = unp->unp_conn;
|
|
|
|
|
|
|
|
if (unp2 == 0)
|
|
|
|
return;
|
|
|
|
unp->unp_conn = 0;
|
|
|
|
switch (unp->unp_socket->so_type) {
|
|
|
|
|
|
|
|
case SOCK_DGRAM:
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_REMOVE(unp, unp_reflink);
|
1994-05-24 10:09:53 +00:00
|
|
|
unp->unp_socket->so_state &= ~SS_ISCONNECTED;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_STREAM:
|
|
|
|
soisdisconnected(unp->unp_socket);
|
|
|
|
unp2->unp_conn = 0;
|
|
|
|
soisdisconnected(unp2->unp_socket);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef notdef
|
1994-05-25 09:21:21 +00:00
|
|
|
void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_abort(unp)
|
|
|
|
struct unpcb *unp;
|
|
|
|
{
|
|
|
|
|
|
|
|
unp_detach(unp);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
This Implements the mumbled about "Jail" feature.
This is a seriously beefed up chroot kind of thing. The process
is jailed along the same lines as a chroot does it, but with
additional tough restrictions imposed on what the superuser can do.
For all I know, it is safe to hand over the root bit inside a
prison to the customer living in that prison, this is what
it was developed for in fact: "real virtual servers".
Each prison has an ip number associated with it, which all IP
communications will be coerced to use and each prison has its own
hostname.
Needless to say, you need more RAM this way, but the advantage is
that each customer can run their own particular version of apache
and not stomp on the toes of their neighbors.
It generally does what one would expect, but setting up a jail
still takes a little knowledge.
A few notes:
I have no scripts for setting up a jail, don't ask me for them.
The IP number should be an alias on one of the interfaces.
mount a /proc in each jail, it will make ps more useable.
/proc/<pid>/status tells the hostname of the prison for
jailed processes.
Quotas are only sensible if you have a mountpoint per prison.
There are no privisions for stopping resource-hogging.
Some "#ifdef INET" and similar may be missing (send patches!)
If somebody wants to take it from here and develop it into
more of a "virtual machine" they should be most welcome!
Tools, comments, patches & documentation most welcome.
Have fun...
Sponsored by: http://www.rndassociates.com/
Run for almost a year by: http://www.servetheweb.com/
1999-04-28 11:38:52 +00:00
|
|
|
static int
|
|
|
|
prison_unpcb(struct proc *p, struct unpcb *unp)
|
|
|
|
{
|
2001-02-21 06:39:57 +00:00
|
|
|
if (!jailed(p->p_ucred))
|
This Implements the mumbled about "Jail" feature.
This is a seriously beefed up chroot kind of thing. The process
is jailed along the same lines as a chroot does it, but with
additional tough restrictions imposed on what the superuser can do.
For all I know, it is safe to hand over the root bit inside a
prison to the customer living in that prison, this is what
it was developed for in fact: "real virtual servers".
Each prison has an ip number associated with it, which all IP
communications will be coerced to use and each prison has its own
hostname.
Needless to say, you need more RAM this way, but the advantage is
that each customer can run their own particular version of apache
and not stomp on the toes of their neighbors.
It generally does what one would expect, but setting up a jail
still takes a little knowledge.
A few notes:
I have no scripts for setting up a jail, don't ask me for them.
The IP number should be an alias on one of the interfaces.
mount a /proc in each jail, it will make ps more useable.
/proc/<pid>/status tells the hostname of the prison for
jailed processes.
Quotas are only sensible if you have a mountpoint per prison.
There are no privisions for stopping resource-hogging.
Some "#ifdef INET" and similar may be missing (send patches!)
If somebody wants to take it from here and develop it into
more of a "virtual machine" they should be most welcome!
Tools, comments, patches & documentation most welcome.
Have fun...
Sponsored by: http://www.rndassociates.com/
Run for almost a year by: http://www.servetheweb.com/
1999-04-28 11:38:52 +00:00
|
|
|
return (0);
|
|
|
|
if (p->p_fd->fd_rdir == unp->unp_rvnode)
|
|
|
|
return (0);
|
|
|
|
return (1);
|
|
|
|
}
|
|
|
|
|
1998-05-15 20:11:40 +00:00
|
|
|
static int
|
2000-07-04 11:25:35 +00:00
|
|
|
unp_pcblist(SYSCTL_HANDLER_ARGS)
|
1998-05-15 20:11:40 +00:00
|
|
|
{
|
1998-10-25 17:44:59 +00:00
|
|
|
int error, i, n;
|
1998-05-15 20:11:40 +00:00
|
|
|
struct unpcb *unp, **unp_list;
|
|
|
|
unp_gen_t gencnt;
|
2001-08-18 02:53:50 +00:00
|
|
|
struct xunpgen *xug;
|
1998-05-15 20:11:40 +00:00
|
|
|
struct unp_head *head;
|
2001-08-18 02:53:50 +00:00
|
|
|
struct xunpcb *xu;
|
1998-05-15 20:11:40 +00:00
|
|
|
|
1998-07-15 02:32:35 +00:00
|
|
|
head = ((intptr_t)arg1 == SOCK_DGRAM ? &unp_dhead : &unp_shead);
|
1998-05-15 20:11:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The process of preparing the PCB list is too time-consuming and
|
|
|
|
* resource-intensive to repeat twice on every request.
|
|
|
|
*/
|
|
|
|
if (req->oldptr == 0) {
|
|
|
|
n = unp_count;
|
2001-08-18 02:53:50 +00:00
|
|
|
req->oldidx = 2 * (sizeof *xug)
|
1998-05-15 20:11:40 +00:00
|
|
|
+ (n + n/8) * sizeof(struct xunpcb);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (req->newptr != 0)
|
|
|
|
return EPERM;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, now we're committed to doing something.
|
|
|
|
*/
|
2001-08-18 02:53:50 +00:00
|
|
|
xug = malloc(sizeof(*xug), M_TEMP, M_WAITOK);
|
1998-05-15 20:11:40 +00:00
|
|
|
gencnt = unp_gencnt;
|
|
|
|
n = unp_count;
|
|
|
|
|
2001-08-18 02:53:50 +00:00
|
|
|
xug->xug_len = sizeof *xug;
|
|
|
|
xug->xug_count = n;
|
|
|
|
xug->xug_gen = gencnt;
|
|
|
|
xug->xug_sogen = so_gencnt;
|
|
|
|
error = SYSCTL_OUT(req, xug, sizeof *xug);
|
|
|
|
if (error) {
|
|
|
|
free(xug, M_TEMP);
|
1998-05-15 20:11:40 +00:00
|
|
|
return error;
|
2001-08-18 02:53:50 +00:00
|
|
|
}
|
1998-05-15 20:11:40 +00:00
|
|
|
|
|
|
|
unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK);
|
|
|
|
if (unp_list == 0)
|
|
|
|
return ENOMEM;
|
|
|
|
|
1999-11-16 10:56:05 +00:00
|
|
|
for (unp = LIST_FIRST(head), i = 0; unp && i < n;
|
|
|
|
unp = LIST_NEXT(unp, unp_link)) {
|
This Implements the mumbled about "Jail" feature.
This is a seriously beefed up chroot kind of thing. The process
is jailed along the same lines as a chroot does it, but with
additional tough restrictions imposed on what the superuser can do.
For all I know, it is safe to hand over the root bit inside a
prison to the customer living in that prison, this is what
it was developed for in fact: "real virtual servers".
Each prison has an ip number associated with it, which all IP
communications will be coerced to use and each prison has its own
hostname.
Needless to say, you need more RAM this way, but the advantage is
that each customer can run their own particular version of apache
and not stomp on the toes of their neighbors.
It generally does what one would expect, but setting up a jail
still takes a little knowledge.
A few notes:
I have no scripts for setting up a jail, don't ask me for them.
The IP number should be an alias on one of the interfaces.
mount a /proc in each jail, it will make ps more useable.
/proc/<pid>/status tells the hostname of the prison for
jailed processes.
Quotas are only sensible if you have a mountpoint per prison.
There are no privisions for stopping resource-hogging.
Some "#ifdef INET" and similar may be missing (send patches!)
If somebody wants to take it from here and develop it into
more of a "virtual machine" they should be most welcome!
Tools, comments, patches & documentation most welcome.
Have fun...
Sponsored by: http://www.rndassociates.com/
Run for almost a year by: http://www.servetheweb.com/
1999-04-28 11:38:52 +00:00
|
|
|
if (unp->unp_gencnt <= gencnt && !prison_unpcb(req->p, unp))
|
1998-05-15 20:11:40 +00:00
|
|
|
unp_list[i++] = unp;
|
|
|
|
}
|
|
|
|
n = i; /* in case we lost some during malloc */
|
|
|
|
|
|
|
|
error = 0;
|
2001-08-18 02:53:50 +00:00
|
|
|
xu = malloc(sizeof(*xu), M_TEMP, M_WAITOK);
|
1998-05-15 20:11:40 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
unp = unp_list[i];
|
|
|
|
if (unp->unp_gencnt <= gencnt) {
|
2001-08-18 02:53:50 +00:00
|
|
|
xu->xu_len = sizeof *xu;
|
|
|
|
xu->xu_unpp = unp;
|
1998-05-15 20:11:40 +00:00
|
|
|
/*
|
|
|
|
* XXX - need more locking here to protect against
|
|
|
|
* connect/disconnect races for SMP.
|
|
|
|
*/
|
|
|
|
if (unp->unp_addr)
|
2001-08-18 02:53:50 +00:00
|
|
|
bcopy(unp->unp_addr, &xu->xu_addr,
|
1998-05-15 20:11:40 +00:00
|
|
|
unp->unp_addr->sun_len);
|
|
|
|
if (unp->unp_conn && unp->unp_conn->unp_addr)
|
|
|
|
bcopy(unp->unp_conn->unp_addr,
|
2001-08-18 02:53:50 +00:00
|
|
|
&xu->xu_caddr,
|
1998-05-15 20:11:40 +00:00
|
|
|
unp->unp_conn->unp_addr->sun_len);
|
2001-08-18 02:53:50 +00:00
|
|
|
bcopy(unp, &xu->xu_unp, sizeof *unp);
|
|
|
|
sotoxsocket(unp->unp_socket, &xu->xu_socket);
|
|
|
|
error = SYSCTL_OUT(req, xu, sizeof *xu);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
2001-08-18 02:53:50 +00:00
|
|
|
free(xu, M_TEMP);
|
1998-05-15 20:11:40 +00:00
|
|
|
if (!error) {
|
|
|
|
/*
|
|
|
|
* Give the user an updated idea of our state.
|
|
|
|
* If the generation differs from what we told
|
|
|
|
* her before, she knows that something happened
|
|
|
|
* while we were processing this request, and it
|
|
|
|
* might be necessary to retry.
|
|
|
|
*/
|
2001-08-18 02:53:50 +00:00
|
|
|
xug->xug_gen = unp_gencnt;
|
|
|
|
xug->xug_sogen = so_gencnt;
|
|
|
|
xug->xug_count = unp_count;
|
|
|
|
error = SYSCTL_OUT(req, xug, sizeof *xug);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
free(unp_list, M_TEMP);
|
2001-08-18 02:53:50 +00:00
|
|
|
free(xug, M_TEMP);
|
1998-05-15 20:11:40 +00:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD,
|
|
|
|
(caddr_t)(long)SOCK_DGRAM, 0, unp_pcblist, "S,xunpcb",
|
|
|
|
"List of active local datagram sockets");
|
|
|
|
SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist, CTLFLAG_RD,
|
|
|
|
(caddr_t)(long)SOCK_STREAM, 0, unp_pcblist, "S,xunpcb",
|
|
|
|
"List of active local stream sockets");
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_shutdown(unp)
|
|
|
|
struct unpcb *unp;
|
|
|
|
{
|
|
|
|
struct socket *so;
|
|
|
|
|
|
|
|
if (unp->unp_socket->so_type == SOCK_STREAM && unp->unp_conn &&
|
|
|
|
(so = unp->unp_conn->unp_socket))
|
|
|
|
socantrcvmore(so);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_drop(unp, errno)
|
|
|
|
struct unpcb *unp;
|
|
|
|
int errno;
|
|
|
|
{
|
|
|
|
struct socket *so = unp->unp_socket;
|
|
|
|
|
|
|
|
so->so_error = errno;
|
|
|
|
unp_disconnect(unp);
|
|
|
|
if (so->so_head) {
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_REMOVE(unp, unp_link);
|
|
|
|
unp->unp_gencnt = ++unp_gencnt;
|
|
|
|
unp_count--;
|
1994-05-24 10:09:53 +00:00
|
|
|
so->so_pcb = (caddr_t) 0;
|
1997-08-16 19:16:27 +00:00
|
|
|
if (unp->unp_addr)
|
|
|
|
FREE(unp->unp_addr, M_SONAME);
|
1998-05-15 20:11:40 +00:00
|
|
|
zfree(unp_zone, unp);
|
1994-05-24 10:09:53 +00:00
|
|
|
sofree(so);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef notdef
|
1994-05-25 09:21:21 +00:00
|
|
|
void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_drain()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1994-05-25 09:21:21 +00:00
|
|
|
int
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_externalize(rights)
|
|
|
|
struct mbuf *rights;
|
|
|
|
{
|
|
|
|
struct proc *p = curproc; /* XXX */
|
|
|
|
register int i;
|
|
|
|
register struct cmsghdr *cm = mtod(rights, struct cmsghdr *);
|
2000-03-09 15:15:27 +00:00
|
|
|
register int *fdp;
|
|
|
|
register struct file **rp;
|
1994-05-24 10:09:53 +00:00
|
|
|
register struct file *fp;
|
2000-03-09 15:15:27 +00:00
|
|
|
int newfds = (cm->cmsg_len - (CMSG_DATA(cm) - (u_char *)cm))
|
|
|
|
/ sizeof (struct file *);
|
1994-05-24 10:09:53 +00:00
|
|
|
int f;
|
|
|
|
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* if the new FD's will not fit, then we free them all
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (!fdavail(p, newfds)) {
|
2000-03-09 15:15:27 +00:00
|
|
|
rp = (struct file **)CMSG_DATA(cm);
|
1994-05-24 10:09:53 +00:00
|
|
|
for (i = 0; i < newfds; i++) {
|
|
|
|
fp = *rp;
|
2000-03-09 15:15:27 +00:00
|
|
|
/*
|
|
|
|
* zero the pointer before calling unp_discard,
|
|
|
|
* since it may end up in unp_gc()..
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
*rp++ = 0;
|
2000-03-09 15:15:27 +00:00
|
|
|
unp_discard(fp);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
return (EMSGSIZE);
|
|
|
|
}
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* now change each pointer to an fd in the global table to
|
|
|
|
* an integer that is the index to the local fd table entry
|
|
|
|
* that we set up to point to the global one we are transferring.
|
2000-03-09 15:15:27 +00:00
|
|
|
* If sizeof (struct file *) is bigger than or equal to sizeof int,
|
|
|
|
* then do it in forward order. In that case, an integer will
|
|
|
|
* always come in the same place or before its corresponding
|
|
|
|
* struct file pointer.
|
|
|
|
* If sizeof (struct file *) is smaller than sizeof int, then
|
|
|
|
* do it in reverse order.
|
1996-12-05 22:41:13 +00:00
|
|
|
*/
|
2000-03-09 15:15:27 +00:00
|
|
|
if (sizeof (struct file *) >= sizeof (int)) {
|
|
|
|
fdp = (int *)(cm + 1);
|
|
|
|
rp = (struct file **)CMSG_DATA(cm);
|
|
|
|
for (i = 0; i < newfds; i++) {
|
|
|
|
if (fdalloc(p, 0, &f))
|
|
|
|
panic("unp_externalize");
|
|
|
|
fp = *rp++;
|
|
|
|
p->p_fd->fd_ofiles[f] = fp;
|
|
|
|
fp->f_msgcount--;
|
|
|
|
unp_rights--;
|
|
|
|
*fdp++ = f;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fdp = (int *)(cm + 1) + newfds - 1;
|
|
|
|
rp = (struct file **)CMSG_DATA(cm) + newfds - 1;
|
|
|
|
for (i = 0; i < newfds; i++) {
|
|
|
|
if (fdalloc(p, 0, &f))
|
|
|
|
panic("unp_externalize");
|
|
|
|
fp = *rp--;
|
|
|
|
p->p_fd->fd_ofiles[f] = fp;
|
|
|
|
fp->f_msgcount--;
|
|
|
|
unp_rights--;
|
|
|
|
*fdp-- = f;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2000-03-09 15:15:27 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust length, in case sizeof(struct file *) and sizeof(int)
|
|
|
|
* differs.
|
|
|
|
*/
|
|
|
|
cm->cmsg_len = CMSG_LEN(newfds * sizeof(int));
|
|
|
|
rights->m_len = cm->cmsg_len;
|
1994-05-24 10:09:53 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1998-05-15 20:11:40 +00:00
|
|
|
void
|
|
|
|
unp_init(void)
|
|
|
|
{
|
|
|
|
unp_zone = zinit("unpcb", sizeof(struct unpcb), nmbclusters, 0, 0);
|
|
|
|
if (unp_zone == 0)
|
|
|
|
panic("unp_init");
|
|
|
|
LIST_INIT(&unp_dhead);
|
|
|
|
LIST_INIT(&unp_shead);
|
|
|
|
}
|
|
|
|
|
Add support to sendmsg()/recvmsg() for passing credentials between
processes using AF_LOCAL sockets. This hack is going to be used with
Secure RPC to duplicate a feature of STREAMS which has no real counterpart
in sockets (with STREAMS/TLI, you can apparently use t_getinfo() to learn
UID of a local process on the other side of a transport endpoint).
What happens is this: the client sets up a sendmsg() call with ancillary
data using the SCM_CREDS socket-level control message type. It does not
need to fill in the structure. When the kernel notices the data,
unp_internalize() fills in the cmesgcred structure with the sending
process' credentials (UID, EUID, GID, and ancillary groups). This data
is later delivered to the receiving process. The receiver can then
perform the follwing tests:
- Did the client send ancillary data?
o Yes, proceed.
o No, refuse to authenticate the client.
- The the client send data of type SCM_CREDS?
o Yes, proceed.
o No, refuse to authenticate the client.
- Is the cmsgcred structure the right size?
o Yes, proceed.
o No, signal a possible error.
The receiver can now inspect the credential information and use it to
authenticate the client.
1997-03-21 16:12:32 +00:00
|
|
|
#ifndef MIN
|
|
|
|
#define MIN(a,b) (((a)<(b))?(a):(b))
|
|
|
|
#endif
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_internalize(control, p)
|
|
|
|
struct mbuf *control;
|
|
|
|
struct proc *p;
|
|
|
|
{
|
2000-03-09 15:15:27 +00:00
|
|
|
struct filedesc *fdescp = p->p_fd;
|
1994-05-24 10:09:53 +00:00
|
|
|
register struct cmsghdr *cm = mtod(control, struct cmsghdr *);
|
|
|
|
register struct file **rp;
|
|
|
|
register struct file *fp;
|
2000-03-09 15:15:27 +00:00
|
|
|
register int i, fd, *fdp;
|
Add support to sendmsg()/recvmsg() for passing credentials between
processes using AF_LOCAL sockets. This hack is going to be used with
Secure RPC to duplicate a feature of STREAMS which has no real counterpart
in sockets (with STREAMS/TLI, you can apparently use t_getinfo() to learn
UID of a local process on the other side of a transport endpoint).
What happens is this: the client sets up a sendmsg() call with ancillary
data using the SCM_CREDS socket-level control message type. It does not
need to fill in the structure. When the kernel notices the data,
unp_internalize() fills in the cmesgcred structure with the sending
process' credentials (UID, EUID, GID, and ancillary groups). This data
is later delivered to the receiving process. The receiver can then
perform the follwing tests:
- Did the client send ancillary data?
o Yes, proceed.
o No, refuse to authenticate the client.
- The the client send data of type SCM_CREDS?
o Yes, proceed.
o No, refuse to authenticate the client.
- Is the cmsgcred structure the right size?
o Yes, proceed.
o No, signal a possible error.
The receiver can now inspect the credential information and use it to
authenticate the client.
1997-03-21 16:12:32 +00:00
|
|
|
register struct cmsgcred *cmcred;
|
1994-05-24 10:09:53 +00:00
|
|
|
int oldfds;
|
2000-03-09 15:15:27 +00:00
|
|
|
u_int newlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
Add support to sendmsg()/recvmsg() for passing credentials between
processes using AF_LOCAL sockets. This hack is going to be used with
Secure RPC to duplicate a feature of STREAMS which has no real counterpart
in sockets (with STREAMS/TLI, you can apparently use t_getinfo() to learn
UID of a local process on the other side of a transport endpoint).
What happens is this: the client sets up a sendmsg() call with ancillary
data using the SCM_CREDS socket-level control message type. It does not
need to fill in the structure. When the kernel notices the data,
unp_internalize() fills in the cmesgcred structure with the sending
process' credentials (UID, EUID, GID, and ancillary groups). This data
is later delivered to the receiving process. The receiver can then
perform the follwing tests:
- Did the client send ancillary data?
o Yes, proceed.
o No, refuse to authenticate the client.
- The the client send data of type SCM_CREDS?
o Yes, proceed.
o No, refuse to authenticate the client.
- Is the cmsgcred structure the right size?
o Yes, proceed.
o No, signal a possible error.
The receiver can now inspect the credential information and use it to
authenticate the client.
1997-03-21 16:12:32 +00:00
|
|
|
if ((cm->cmsg_type != SCM_RIGHTS && cm->cmsg_type != SCM_CREDS) ||
|
|
|
|
cm->cmsg_level != SOL_SOCKET || cm->cmsg_len != control->m_len)
|
1994-05-24 10:09:53 +00:00
|
|
|
return (EINVAL);
|
Add support to sendmsg()/recvmsg() for passing credentials between
processes using AF_LOCAL sockets. This hack is going to be used with
Secure RPC to duplicate a feature of STREAMS which has no real counterpart
in sockets (with STREAMS/TLI, you can apparently use t_getinfo() to learn
UID of a local process on the other side of a transport endpoint).
What happens is this: the client sets up a sendmsg() call with ancillary
data using the SCM_CREDS socket-level control message type. It does not
need to fill in the structure. When the kernel notices the data,
unp_internalize() fills in the cmesgcred structure with the sending
process' credentials (UID, EUID, GID, and ancillary groups). This data
is later delivered to the receiving process. The receiver can then
perform the follwing tests:
- Did the client send ancillary data?
o Yes, proceed.
o No, refuse to authenticate the client.
- The the client send data of type SCM_CREDS?
o Yes, proceed.
o No, refuse to authenticate the client.
- Is the cmsgcred structure the right size?
o Yes, proceed.
o No, signal a possible error.
The receiver can now inspect the credential information and use it to
authenticate the client.
1997-03-21 16:12:32 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill in credential information.
|
|
|
|
*/
|
|
|
|
if (cm->cmsg_type == SCM_CREDS) {
|
|
|
|
cmcred = (struct cmsgcred *)(cm + 1);
|
|
|
|
cmcred->cmcred_pid = p->p_pid;
|
o Merge contents of struct pcred into struct ucred. Specifically, add the
real uid, saved uid, real gid, and saved gid to ucred, as well as the
pcred->pc_uidinfo, which was associated with the real uid, only rename
it to cr_ruidinfo so as not to conflict with cr_uidinfo, which
corresponds to the effective uid.
o Remove p_cred from struct proc; add p_ucred to struct proc, replacing
original macro that pointed.
p->p_ucred to p->p_cred->pc_ucred.
o Universally update code so that it makes use of ucred instead of pcred,
p->p_ucred instead of p->p_pcred, cr_ruidinfo instead of p_uidinfo,
cr_{r,sv}{u,g}id instead of p_*, etc.
o Remove pcred0 and its initialization from init_main.c; initialize
cr_ruidinfo there.
o Restruction many credential modification chunks to always crdup while
we figure out locking and optimizations; generally speaking, this
means moving to a structure like this:
newcred = crdup(oldcred);
...
p->p_ucred = newcred;
crfree(oldcred);
It's not race-free, but better than nothing. There are also races
in sys_process.c, all inter-process authorization, fork, exec, and
exit.
o Remove sigio->sio_ruid since sigio->sio_ucred now contains the ruid;
remove comments indicating that the old arrangement was a problem.
o Restructure exec1() a little to use newcred/oldcred arrangement, and
use improved uid management primitives.
o Clean up exit1() so as to do less work in credential cleanup due to
pcred removal.
o Clean up fork1() so as to do less work in credential cleanup and
allocation.
o Clean up ktrcanset() to take into account changes, and move to using
suser_xxx() instead of performing a direct uid==0 comparision.
o Improve commenting in various kern_prot.c credential modification
calls to better document current behavior. In a couple of places,
current behavior is a little questionable and we need to check
POSIX.1 to make sure it's "right". More commenting work still
remains to be done.
o Update credential management calls, such as crfree(), to take into
account new ruidinfo reference.
o Modify or add the following uid and gid helper routines:
change_euid()
change_egid()
change_ruid()
change_rgid()
change_svuid()
change_svgid()
In each case, the call now acts on a credential not a process, and as
such no longer requires more complicated process locking/etc. They
now assume the caller will do any necessary allocation of an
exclusive credential reference. Each is commented to document its
reference requirements.
o CANSIGIO() is simplified to require only credentials, not processes
and pcreds.
o Remove lots of (p_pcred==NULL) checks.
o Add an XXX to authorization code in nfs_lock.c, since it's
questionable, and needs to be considered carefully.
o Simplify posix4 authorization code to require only credentials, not
processes and pcreds. Note that this authorization, as well as
CANSIGIO(), needs to be updated to use the p_cansignal() and
p_cansched() centralized authorization routines, as they currently
do not take into account some desirable restrictions that are handled
by the centralized routines, as well as being inconsistent with other
similar authorization instances.
o Update libkvm to take these changes into account.
Obtained from: TrustedBSD Project
Reviewed by: green, bde, jhb, freebsd-arch, freebsd-audit
2001-05-25 16:59:11 +00:00
|
|
|
cmcred->cmcred_uid = p->p_ucred->cr_ruid;
|
|
|
|
cmcred->cmcred_gid = p->p_ucred->cr_rgid;
|
Add support to sendmsg()/recvmsg() for passing credentials between
processes using AF_LOCAL sockets. This hack is going to be used with
Secure RPC to duplicate a feature of STREAMS which has no real counterpart
in sockets (with STREAMS/TLI, you can apparently use t_getinfo() to learn
UID of a local process on the other side of a transport endpoint).
What happens is this: the client sets up a sendmsg() call with ancillary
data using the SCM_CREDS socket-level control message type. It does not
need to fill in the structure. When the kernel notices the data,
unp_internalize() fills in the cmesgcred structure with the sending
process' credentials (UID, EUID, GID, and ancillary groups). This data
is later delivered to the receiving process. The receiver can then
perform the follwing tests:
- Did the client send ancillary data?
o Yes, proceed.
o No, refuse to authenticate the client.
- The the client send data of type SCM_CREDS?
o Yes, proceed.
o No, refuse to authenticate the client.
- Is the cmsgcred structure the right size?
o Yes, proceed.
o No, signal a possible error.
The receiver can now inspect the credential information and use it to
authenticate the client.
1997-03-21 16:12:32 +00:00
|
|
|
cmcred->cmcred_euid = p->p_ucred->cr_uid;
|
|
|
|
cmcred->cmcred_ngroups = MIN(p->p_ucred->cr_ngroups,
|
|
|
|
CMGROUP_MAX);
|
|
|
|
for (i = 0; i < cmcred->cmcred_ngroups; i++)
|
|
|
|
cmcred->cmcred_groups[i] = p->p_ucred->cr_groups[i];
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
oldfds = (cm->cmsg_len - sizeof (*cm)) / sizeof (int);
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* check that all the FDs passed in refer to legal OPEN files
|
|
|
|
* If not, reject the entire operation.
|
|
|
|
*/
|
2000-03-09 15:15:27 +00:00
|
|
|
fdp = (int *)(cm + 1);
|
1994-05-24 10:09:53 +00:00
|
|
|
for (i = 0; i < oldfds; i++) {
|
2000-03-09 15:15:27 +00:00
|
|
|
fd = *fdp++;
|
|
|
|
if ((unsigned)fd >= fdescp->fd_nfiles ||
|
|
|
|
fdescp->fd_ofiles[fd] == NULL)
|
1994-05-24 10:09:53 +00:00
|
|
|
return (EBADF);
|
|
|
|
}
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* Now replace the integer FDs with pointers to
|
|
|
|
* the associated global file table entry..
|
2000-03-09 15:15:27 +00:00
|
|
|
* Allocate a bigger buffer as necessary. But if an cluster is not
|
|
|
|
* enough, return E2BIG.
|
1996-12-05 22:41:13 +00:00
|
|
|
*/
|
2000-03-09 15:15:27 +00:00
|
|
|
newlen = CMSG_LEN(oldfds * sizeof(struct file *));
|
|
|
|
if (newlen > MCLBYTES)
|
|
|
|
return (E2BIG);
|
|
|
|
if (newlen - control->m_len > M_TRAILINGSPACE(control)) {
|
|
|
|
if (control->m_flags & M_EXT)
|
|
|
|
return (E2BIG);
|
2000-12-21 21:44:31 +00:00
|
|
|
MCLGET(control, M_TRYWAIT);
|
2000-03-09 15:15:27 +00:00
|
|
|
if ((control->m_flags & M_EXT) == 0)
|
|
|
|
return (ENOBUFS);
|
|
|
|
|
|
|
|
/* copy the data to the cluster */
|
|
|
|
memcpy(mtod(control, char *), cm, cm->cmsg_len);
|
|
|
|
cm = mtod(control, struct cmsghdr *);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust length, in case sizeof(struct file *) and sizeof(int)
|
|
|
|
* differs.
|
|
|
|
*/
|
|
|
|
control->m_len = cm->cmsg_len = newlen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Transform the file descriptors into struct file pointers.
|
|
|
|
* If sizeof (struct file *) is bigger than or equal to sizeof int,
|
|
|
|
* then do it in reverse order so that the int won't get until
|
|
|
|
* we're done.
|
|
|
|
* If sizeof (struct file *) is smaller than sizeof int, then
|
|
|
|
* do it in forward order.
|
|
|
|
*/
|
|
|
|
if (sizeof (struct file *) >= sizeof (int)) {
|
|
|
|
fdp = (int *)(cm + 1) + oldfds - 1;
|
|
|
|
rp = (struct file **)CMSG_DATA(cm) + oldfds - 1;
|
|
|
|
for (i = 0; i < oldfds; i++) {
|
|
|
|
fp = fdescp->fd_ofiles[*fdp--];
|
|
|
|
*rp-- = fp;
|
|
|
|
fp->f_count++;
|
|
|
|
fp->f_msgcount++;
|
|
|
|
unp_rights++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
fdp = (int *)(cm + 1);
|
|
|
|
rp = (struct file **)CMSG_DATA(cm);
|
|
|
|
for (i = 0; i < oldfds; i++) {
|
|
|
|
fp = fdescp->fd_ofiles[*fdp++];
|
|
|
|
*rp++ = fp;
|
|
|
|
fp->f_count++;
|
|
|
|
fp->f_msgcount++;
|
|
|
|
unp_rights++;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int unp_defer, unp_gcing;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_gc()
|
|
|
|
{
|
|
|
|
register struct file *fp, *nextfp;
|
|
|
|
register struct socket *so;
|
|
|
|
struct file **extra_ref, **fpp;
|
|
|
|
int nunref, i;
|
|
|
|
|
|
|
|
if (unp_gcing)
|
|
|
|
return;
|
|
|
|
unp_gcing = 1;
|
|
|
|
unp_defer = 0;
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* before going through all this, set all FDs to
|
|
|
|
* be NOT defered and NOT externally accessible
|
|
|
|
*/
|
1999-11-16 10:56:05 +00:00
|
|
|
LIST_FOREACH(fp, &filehead, f_list)
|
1994-05-24 10:09:53 +00:00
|
|
|
fp->f_flag &= ~(FMARK|FDEFER);
|
|
|
|
do {
|
1999-11-16 10:56:05 +00:00
|
|
|
LIST_FOREACH(fp, &filehead, f_list) {
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* If the file is not open, skip it
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (fp->f_count == 0)
|
|
|
|
continue;
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* If we already marked it as 'defer' in a
|
|
|
|
* previous pass, then try process it this time
|
|
|
|
* and un-mark it
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (fp->f_flag & FDEFER) {
|
|
|
|
fp->f_flag &= ~FDEFER;
|
|
|
|
unp_defer--;
|
|
|
|
} else {
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* if it's not defered, then check if it's
|
|
|
|
* already marked.. if so skip it
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (fp->f_flag & FMARK)
|
|
|
|
continue;
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* If all references are from messages
|
|
|
|
* in transit, then skip it. it's not
|
|
|
|
* externally accessible.
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (fp->f_count == fp->f_msgcount)
|
|
|
|
continue;
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* If it got this far then it must be
|
|
|
|
* externally accessible.
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
fp->f_flag |= FMARK;
|
|
|
|
}
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* either it was defered, or it is externally
|
|
|
|
* accessible and not already marked so.
|
|
|
|
* Now check if it is possibly one of OUR sockets.
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (fp->f_type != DTYPE_SOCKET ||
|
|
|
|
(so = (struct socket *)fp->f_data) == 0)
|
|
|
|
continue;
|
1995-05-11 00:13:26 +00:00
|
|
|
if (so->so_proto->pr_domain != &localdomain ||
|
1994-05-24 10:09:53 +00:00
|
|
|
(so->so_proto->pr_flags&PR_RIGHTS) == 0)
|
|
|
|
continue;
|
|
|
|
#ifdef notdef
|
|
|
|
if (so->so_rcv.sb_flags & SB_LOCK) {
|
|
|
|
/*
|
|
|
|
* This is problematical; it's not clear
|
|
|
|
* we need to wait for the sockbuf to be
|
|
|
|
* unlocked (on a uniprocessor, at least),
|
|
|
|
* and it's also not clear what to do
|
|
|
|
* if sbwait returns an error due to receipt
|
|
|
|
* of a signal. If sbwait does return
|
|
|
|
* an error, we'll go into an infinite
|
|
|
|
* loop. Delete all of this for now.
|
|
|
|
*/
|
|
|
|
(void) sbwait(&so->so_rcv);
|
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
#endif
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* So, Ok, it's one of our sockets and it IS externally
|
|
|
|
* accessible (or was defered). Now we look
|
1998-04-17 22:37:19 +00:00
|
|
|
* to see if we hold any file descriptors in its
|
1996-12-05 22:41:13 +00:00
|
|
|
* message buffers. Follow those links and mark them
|
|
|
|
* as accessible too.
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_scan(so->so_rcv.sb_mb, unp_mark);
|
|
|
|
}
|
|
|
|
} while (unp_defer);
|
|
|
|
/*
|
|
|
|
* We grab an extra reference to each of the file table entries
|
|
|
|
* that are not otherwise accessible and then free the rights
|
|
|
|
* that are stored in messages on them.
|
|
|
|
*
|
|
|
|
* The bug in the orginal code is a little tricky, so I'll describe
|
|
|
|
* what's wrong with it here.
|
|
|
|
*
|
|
|
|
* It is incorrect to simply unp_discard each entry for f_msgcount
|
|
|
|
* times -- consider the case of sockets A and B that contain
|
|
|
|
* references to each other. On a last close of some other socket,
|
|
|
|
* we trigger a gc since the number of outstanding rights (unp_rights)
|
|
|
|
* is non-zero. If during the sweep phase the gc code un_discards,
|
|
|
|
* we end up doing a (full) closef on the descriptor. A closef on A
|
|
|
|
* results in the following chain. Closef calls soo_close, which
|
|
|
|
* calls soclose. Soclose calls first (through the switch
|
|
|
|
* uipc_usrreq) unp_detach, which re-invokes unp_gc. Unp_gc simply
|
|
|
|
* returns because the previous instance had set unp_gcing, and
|
|
|
|
* we return all the way back to soclose, which marks the socket
|
|
|
|
* with SS_NOFDREF, and then calls sofree. Sofree calls sorflush
|
|
|
|
* to free up the rights that are queued in messages on the socket A,
|
|
|
|
* i.e., the reference on B. The sorflush calls via the dom_dispose
|
|
|
|
* switch unp_dispose, which unp_scans with unp_discard. This second
|
|
|
|
* instance of unp_discard just calls closef on B.
|
|
|
|
*
|
|
|
|
* Well, a similar chain occurs on B, resulting in a sorflush on B,
|
|
|
|
* which results in another closef on A. Unfortunately, A is already
|
|
|
|
* being closed, and the descriptor has already been marked with
|
|
|
|
* SS_NOFDREF, and soclose panics at this point.
|
|
|
|
*
|
|
|
|
* Here, we first take an extra reference to each inaccessible
|
|
|
|
* descriptor. Then, we call sorflush ourself, since we know
|
|
|
|
* it is a Unix domain socket anyhow. After we destroy all the
|
|
|
|
* rights carried in messages, we do a last closef to get rid
|
|
|
|
* of our extra reference. This is the last close, and the
|
|
|
|
* unp_detach etc will shut down the socket.
|
|
|
|
*
|
|
|
|
* 91/09/19, bsy@cs.cmu.edu
|
|
|
|
*/
|
|
|
|
extra_ref = malloc(nfiles * sizeof(struct file *), M_FILE, M_WAITOK);
|
1999-11-16 10:56:05 +00:00
|
|
|
for (nunref = 0, fp = LIST_FIRST(&filehead), fpp = extra_ref; fp != 0;
|
1996-03-11 02:17:11 +00:00
|
|
|
fp = nextfp) {
|
1999-11-16 10:56:05 +00:00
|
|
|
nextfp = LIST_NEXT(fp, f_list);
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* If it's not open, skip it
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (fp->f_count == 0)
|
|
|
|
continue;
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* If all refs are from msgs, and it's not marked accessible
|
|
|
|
* then it must be referenced from some unreachable cycle
|
|
|
|
* of (shut-down) FDs, so include it in our
|
|
|
|
* list of FDs to remove
|
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
if (fp->f_count == fp->f_msgcount && !(fp->f_flag & FMARK)) {
|
|
|
|
*fpp++ = fp;
|
|
|
|
nunref++;
|
|
|
|
fp->f_count++;
|
|
|
|
}
|
|
|
|
}
|
1996-12-05 22:41:13 +00:00
|
|
|
/*
|
|
|
|
* for each FD on our hit list, do the following two things
|
|
|
|
*/
|
1999-01-21 08:29:12 +00:00
|
|
|
for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp) {
|
|
|
|
struct file *tfp = *fpp;
|
|
|
|
if (tfp->f_type == DTYPE_SOCKET && tfp->f_data != NULL)
|
|
|
|
sorflush((struct socket *)(tfp->f_data));
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
|
1997-02-10 02:22:35 +00:00
|
|
|
closef(*fpp, (struct proc *) NULL);
|
1994-05-24 10:09:53 +00:00
|
|
|
free((caddr_t)extra_ref, M_FILE);
|
|
|
|
unp_gcing = 0;
|
|
|
|
}
|
|
|
|
|
1994-05-25 09:21:21 +00:00
|
|
|
void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_dispose(m)
|
|
|
|
struct mbuf *m;
|
|
|
|
{
|
1997-02-10 02:22:35 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (m)
|
|
|
|
unp_scan(m, unp_discard);
|
|
|
|
}
|
|
|
|
|
2001-08-17 22:01:18 +00:00
|
|
|
static int
|
|
|
|
unp_listen(unp, p)
|
|
|
|
struct unpcb *unp;
|
|
|
|
struct proc *p;
|
|
|
|
{
|
|
|
|
|
|
|
|
bzero(&unp->unp_peercred, sizeof(unp->unp_peercred));
|
|
|
|
unp->unp_peercred.cr_uid = p->p_ucred->cr_uid;
|
|
|
|
unp->unp_peercred.cr_ngroups = p->p_ucred->cr_ngroups;
|
|
|
|
bcopy(p->p_ucred->cr_groups, unp->unp_peercred.cr_groups,
|
|
|
|
sizeof(unp->unp_peercred.cr_groups));
|
|
|
|
unp->unp_flags |= UNP_HAVEPCCACHED;
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_scan(m0, op)
|
|
|
|
register struct mbuf *m0;
|
1997-02-10 02:22:35 +00:00
|
|
|
void (*op) __P((struct file *));
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
|
|
|
register struct mbuf *m;
|
|
|
|
register struct file **rp;
|
|
|
|
register struct cmsghdr *cm;
|
|
|
|
register int i;
|
|
|
|
int qfds;
|
|
|
|
|
|
|
|
while (m0) {
|
|
|
|
for (m = m0; m; m = m->m_next)
|
|
|
|
if (m->m_type == MT_CONTROL &&
|
|
|
|
m->m_len >= sizeof(*cm)) {
|
|
|
|
cm = mtod(m, struct cmsghdr *);
|
|
|
|
if (cm->cmsg_level != SOL_SOCKET ||
|
|
|
|
cm->cmsg_type != SCM_RIGHTS)
|
|
|
|
continue;
|
2000-03-09 15:15:27 +00:00
|
|
|
qfds = (cm->cmsg_len -
|
|
|
|
(CMSG_DATA(cm) - (u_char *)cm))
|
1994-05-24 10:09:53 +00:00
|
|
|
/ sizeof (struct file *);
|
2000-03-09 15:15:27 +00:00
|
|
|
rp = (struct file **)CMSG_DATA(cm);
|
1994-05-24 10:09:53 +00:00
|
|
|
for (i = 0; i < qfds; i++)
|
|
|
|
(*op)(*rp++);
|
|
|
|
break; /* XXX, but saves time */
|
|
|
|
}
|
|
|
|
m0 = m0->m_act;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_mark(fp)
|
|
|
|
struct file *fp;
|
|
|
|
{
|
|
|
|
|
|
|
|
if (fp->f_flag & FMARK)
|
|
|
|
return;
|
|
|
|
unp_defer++;
|
|
|
|
fp->f_flag |= (FMARK|FDEFER);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
1994-05-24 10:09:53 +00:00
|
|
|
unp_discard(fp)
|
|
|
|
struct file *fp;
|
|
|
|
{
|
|
|
|
|
|
|
|
fp->f_msgcount--;
|
|
|
|
unp_rights--;
|
|
|
|
(void) closef(fp, (struct proc *)NULL);
|
|
|
|
}
|