Implement a LOCAL_PEERCRED socket option which returns a

`struct xucred` with the credentials of the connected peer.
Obviously this only works (and makes sense) on SOCK_STREAM
sockets.  This works for both the connect(2) and listen(2)
callers.

There is precise documentation of the semantics in unix(4).

Reviewed by:	dwmalone (eyeballed)
This commit is contained in:
dd 2001-08-17 22:01:18 +00:00
parent 90eec3a264
commit 5e416567e1
5 changed files with 135 additions and 4 deletions

View File

@ -32,7 +32,7 @@
.\" @(#)unix.4 8.1 (Berkeley) 6/9/93
.\" $FreeBSD$
.\"
.Dd June 9, 1993
.Dd July 15, 2001
.Dt UNIX 4
.Os
.Sh NAME
@ -147,6 +147,35 @@ passed to a receiver.
Descriptors that are awaiting delivery, or that are
purposely not received, are automatically closed by the system
when the destination socket is closed.
.Pp
The effective credentials (i.e., the user ID and group list) the of a
peer on a
.Dv SOCK_STREAM
socket may be obtained using the
.Dv LOCAL_PEERCRED
socket option.
This may be used by a server to obtain and verify the credentials of
its client, and vice versa by the client to verify the credentials
of the server.
These will arrive in the form of a filled in
.Ar struct xucred
(defined in
.Pa sys/ucred.h ) .
The credentials presented to the server (the
.Xr listen 2
caller) are those of the client when it called
.Xr connect 2 ;
the credentials presented to the client (the
.Xr connect 2
caller) are those of the server when it called
.Xr listen 2 .
This mechanism is reliable; there is no way for either party to influence
the credentials presented to its peer except by calling the appropriate
system call (e.g.,
.Xr connect 2
or
.Xr listen 2 )
under different effective credentials.
.Sh SEE ALSO
.Xr socket 2 ,
.Xr intro 4

View File

@ -51,7 +51,7 @@
static struct protosw localsw[] = {
{ SOCK_STREAM, &localdomain, 0, PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS,
0, 0, 0, 0,
0, 0, 0, &uipc_ctloutput,
0,
0, 0, 0, 0,
&uipc_usrreqs

View File

@ -91,6 +91,7 @@ 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 *));
static int unp_listen __P((struct unpcb *, struct proc *));
static int
uipc_abort(struct socket *so)
@ -199,7 +200,7 @@ uipc_listen(struct socket *so, struct proc *p)
if (unp == 0 || unp->unp_vnode == 0)
return EINVAL;
return 0;
return unp_listen(unp, p);
}
static int
@ -434,6 +435,41 @@ struct pr_usrreqs uipc_usrreqs = {
uipc_send, uipc_sense, uipc_shutdown, uipc_sockaddr,
sosend, soreceive, sopoll
};
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);
}
/*
* Both send and receive buffers are allocated PIPSIZ bytes of buffering
@ -609,7 +645,7 @@ unp_connect(so, nam, p)
register struct sockaddr_un *soun = (struct sockaddr_un *)nam;
register struct vnode *vp;
register struct socket *so2, *so3;
struct unpcb *unp2, *unp3;
struct unpcb *unp, *unp2, *unp3;
int error, len;
struct nameidata nd;
char buf[SOCK_MAXADDRLEN];
@ -648,12 +684,40 @@ unp_connect(so, nam, p)
error = ECONNREFUSED;
goto bad;
}
unp = sotounpcb(so);
unp2 = sotounpcb(so2);
unp3 = sotounpcb(so3);
if (unp2->unp_addr)
unp3->unp_addr = (struct sockaddr_un *)
dup_sockaddr((struct sockaddr *)
unp2->unp_addr, 1);
/*
* 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;
so2 = so3;
}
error = unp_connect2(so, so2);
@ -1244,6 +1308,21 @@ unp_dispose(m)
unp_scan(m, unp_discard);
}
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);
}
static void
unp_scan(m0, op)
register struct mbuf *m0;

View File

@ -46,12 +46,16 @@ struct sockaddr_un {
char sun_path[104]; /* path name (gag) */
};
/* Socket options. */
#define LOCAL_PEERCRED 0x001 /* retrieve peer credentails */
#ifdef _KERNEL
struct mbuf;
struct socket;
int uipc_usrreq __P((struct socket *so, int req, struct mbuf *m,
struct mbuf *nam, struct mbuf *control));
int uipc_ctloutput __P((struct socket *so, struct sockopt *sopt));
int unp_connect2 __P((struct socket *so, struct socket *so2));
void unp_dispose __P((struct mbuf *m));
int unp_externalize __P((struct mbuf *rights));

View File

@ -38,6 +38,7 @@
#define _SYS_UNPCB_H_
#include <sys/queue.h>
#include <sys/ucred.h>
/*
* Protocol control block for an active
@ -80,8 +81,26 @@ struct unpcb {
int unp_cc; /* copy of rcv.sb_cc */
int unp_mbcnt; /* copy of rcv.sb_mbcnt */
unp_gen_t unp_gencnt; /* generation count of this instance */
int unp_flags; /* flags */
struct xucred unp_peercred; /* peer credentials, if applicable */
};
/*
* Flags in unp_flags.
*
* UNP_HAVEPC - indicates that the unp_peercred member is filled in
* and is really the credentials of the connected peer. This is used
* to determine whether the contents should be sent to the user or
* not.
*
* UNP_HAVEPCCACHED - indicates that the unp_peercred member is filled
* in, but does *not* contain the credentials of the connected peer
* (there may not even be a peer). This is set in unp_listen() when
* it fills in unp_peercred for later consumption by unp_connect().
*/
#define UNP_HAVEPC 0x001
#define UNP_HAVEPCCACHED 0x002
#define sotounpcb(so) ((struct unpcb *)((so)->so_pcb))
/* Hack alert -- this structure depends on <sys/socketvar.h>. */